diff --git a/.circleci/config.yml b/.circleci/config.yml
index 40a247c06..21ee62ba7 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -43,7 +43,7 @@ jobs:
eval "$($HOME/bin/micromamba shell hook -s posix)"
micromamba activate RAiDER
python -m pip install .
- python -c "import RAiDER; from RAiDER.delay import main"
+ python -c "import RAiDER; from RAiDER.delay import tropo_delay"
python -c "import RAiDER; from RAiDER.interpolator import interp_along_axis"
- run:
name: Run unit tests
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 185a25b9b..3919476ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/)
and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.3.0]
+RAiDER package was refactored to expose the main functionality as a Python library, including the `prepareWeatherModel`
+and `tropo_delay` functions, as well as anciliarry functions needed for defining AOIs, look vectors, etc.
+
+### New/Updated Features
++ Python library access to main functions for accessing weather model data and calculating delays
++ Slant delay calculation through projection is supported for cubes with orbit files
++ Upgrade dem-stitcher to [`>=2.3.1`](https://github.com/ACCESS-Cloud-Based-InSAR/dem-stitcher/blob/dev/CHANGELOG.md#231) so that the updated urls for the GLO-30 DEM are used.
++ `raider.py ++calcDelaysGUNW GUNWFILE` is enabled as a placeholder only.
++ Upgraded ISCE3 to `>=v0.9.0` to fix a conda build issue as described in [#425](https://github.com/dbekaert/RAiDER/issues/425)
++ Allow user to specify --download_only or download_only=True in the configure file
++ Added documentation for the Python library interface.
++ Added some unit tests.
++ Fixed some bugs and tweaked the CLI.
++ Added unit tests, docstrings, initial API reference
++ __main__ file to allow calls to different functionality. `raider.py ++process downloadGNSS ...` can now perform the functionality of `raiderDownloadGNSS.py ...
+
+
+
## [0.2.0]
RAiDER package was refactored to use a configure file (yaml) to parse parameters. In addition, ocker container images
diff --git a/README.md b/README.md
old mode 100755
new mode 100644
index a401a581c..1a00261b8
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
# RAiDER
+
Raytracing Atmospheric Delay Estimation for RADAR
[![Language](https://img.shields.io/badge/python-3.7%2B-blue.svg)](https://www.python.org/)
@@ -13,15 +14,18 @@ Copyright (c) 2019-2022, California Institute of Technology ("Caltech"). All rig
THIS IS RESEARCH CODE PROVIDED TO YOU "AS IS" WITH NO WARRANTIES OF CORRECTNESS. USE AT YOUR OWN RISK.
## Contents
-- [1. Getting Started](#1-getting-started)
- - [Installing With Conda](#installing-with-conda)
- - [Using the Docker Image](#using-the-docker-image)
- - [Installing from Source](#installing-from-source)
-- [2. Setup of third party weather model access](#2-setup-of-third-party-weather-model-access)
-- [3. Running RAiDER and Documentation](#3-running-raider-and-documentation)
-- [4. Citing](#4-citation)
-- [5. Contributors](#5-contributors)
+
+1. [Getting Started](#1-getting-started)
+ - [Installing With Conda](#installing-with-conda)
+ - [Using the Docker Image](#using-the-docker-image)
+ - [Installing from Source](#installing-from-source)
+2. [Setup of third party weather model access](#2-setup-of-third-party-weather-model-access)
+3. [Running RAiDER and Documentation](#3-running-raider-and-documentation)
+4. [Citing](#4-citation)
+5. [Development](#5-development)
+ - [Contributors](#contributors)
------
+
## 1. Getting Started
RAiDER has been tested on the following systems:
@@ -31,6 +35,7 @@ RAiDER has been tested on the following systems:
RAiDER does **not** currently run on arm64 processors on Mac. We will update this note once the build becomes available.
### Installing With Conda
+
RAiDER is available on [conda-forge](https://anaconda.org/conda-forge/raider). __[Conda](https://docs.conda.io/en/latest/index.html)__ is a cross-platform way to use Python that allows you to setup and use "virtual environments." These can help to keep dependencies for different sets of code separate. We recommend using [Miniforge](https://github.com/conda-forge/miniforge), a conda environment manager that uses conda-forge as its default code repo. Alternatively,see __[here](https://docs.anaconda.com/anaconda/install/)__ for help installing Anaconda and __[here](https://docs.conda.io/en/latest/miniconda.html)__ for installing Miniconda.
Installing RAiDER:
@@ -40,6 +45,7 @@ conda activate RAiDER
```
### Using the Docker image
+
RAiDER provides a [docker container image](https://docs.docker.com/get-started/) with all the necessary dependencies pre-installed. To get the latest released version:
```
docker pull ghcr.io/dbekaert/raider:latest
@@ -64,32 +70,43 @@ cd work
```
For more docker run options, see: .
-### Installing from source
-You can also install RAiDER directly from source. Doing so is recommended for those who would like to [contribute to the source code](https://github.com/dbekaert/RAiDER/blob/dev/CONTRIBUTING.md), which we heartily encourage! For more details on installing from source see [here](https://github.com/dbekaert/RAiDER/blob/dev/Installing_from_source.md).
-```
-git clone https://github.com/dbekaert/RAiDER.git
-cd RAiDER
-conda create -f environment.yml
-conda activate RAiDER
-python -m pip install -e .
-```
+
------
## 2. Setup of third party weather model access
-RAiDER has the ability to download weather models from third-parties; some of which require license agreements. See [here](WeatherModels.md) for details.
+
+RAiDER has the ability to download weather models from third-parties; some of which require license agreements. See [here](https://github.com/dbekaert/RAiDER/blob/dev/docs/WeatherModels.md) for details.
------
## 3. Running RAiDER and Documentation
+
For detailed documentation, examples, and Jupyter notebooks see the [RAiDER-docs repository](https://github.com/dbekaert/RAiDER-docs).
-We welcome contributions of other examples on how to leverage the RAiDER (see [here](https://github.com/dbekaert/RAiDER/blob/master/CONTRIBUTING.md) for instructions).
+We welcome contributions of other examples on how to leverage the RAiDER (see [here](https://github.com/dbekaert/RAiDER/blob/dev/CONTRIBUTING.md) for instructions).
``` raiderDelay.py -h ``` provides a help menu and list of example commands to get started.
-The RAiDER scripts are highly modulized in Python and allows for building your own processing workflow.
+The RAiDER scripts are highly modularized in Python and allows for building your own processing workflow.
------
## 4. Citation
TODO
------
-## 5. Contributors
+## 5. Development
+
+Contributions are welcome and heartily encourage! See our [contributing guide](https://github.com/dbekaert/RAiDER/blob/dev/CONTRIBUTING.md).
+
+### Development install
+For development, we recommend installing directly from source.
+```
+git clone https://github.com/dbekaert/RAiDER.git
+cd RAiDER
+conda create -f environment.yml
+conda activate RAiDER
+python -m pip install -e .
+```
+For more details on installing from source see [here](https://github.com/dbekaert/RAiDER/blob/dev/docs/Installing_from_source.md).
+
+------
+### Contributors
+
* David Bekaert
* Jeremy Maurer
* Raymond Hogenson
diff --git a/Installing_from_source.md b/docs/Installing_from_source.md
similarity index 100%
rename from Installing_from_source.md
rename to docs/Installing_from_source.md
diff --git a/WeatherModels.md b/docs/WeatherModels.md
similarity index 62%
rename from WeatherModels.md
rename to docs/WeatherModels.md
index eae7cbd5b..ddbc87eea 100644
--- a/WeatherModels.md
+++ b/docs/WeatherModels.md
@@ -1,30 +1,28 @@
# Accessing weather model data
-RAiDER has built-in support for a number of different weather models. RAiDER provides all of the interfacing to data servers required to access data for the different weather models, although some weather models require a license agreement and accounts to be set-up. Instructions for accessing data, including license-limited data, are provided below. It is the user's responsibility to accept license agreements for whatever model is desired.
+RAiDER has built-in support for a number of different weather models. RAiDER provides all the interfacing to data servers required to access data for the different weather models, although some weather models require a license agreement and accounts to be set-up. Instructions for accessing data, including license-limited data, are provided below. It is the user's responsibility to accept license agreements for whatever model is desired.
In addition, RAiDER provides functionality for adding additional weather models. See the [RAiDER-docs repository](https://github.com/dbekaert/RAiDER-docs) page on how to do this. We would love to expand the suite of supported models, and welcome any contributions. Please see the contributing guidelines or reach out through an issue ticket for help.
------
-## Contents
+## 1. Usage
-1. [NOAA weather models (HRRR)](#noaa-weather-models-(hrrr))
-2. [ECMWF weather models (ERA5, ERA5T, ERAI, HRES)](#ecmwf-weather-models-(era5,-era5t,-erai,-hres))
-3. [NASA weather models (GMAO, MERRA2)](#nasa-weather-models-(gmao,-merra2))
+::: RAiDER.processWM.prepareWeatherModel
+ options:
+ show_root_heading: true
+ heading_level: 3
-#Potential download failure:#
+### Potential download failure
ERA-5/ERA-I products require access to the ESA Copernicus servers. GMAO and MERRA-2 products require access to the NASA Earthdata servers. If you are unable to download products, ensure that you have registered and have downloaded the public API key, and accepted/added the license/application for type of product you wish to download as detailed below.
------
-## 1. NOAA weather models (HRRR)
-High-resolution rapid refresh (HRRR) weather model data products are generated by __[NOAA](https://rapidrefresh.noaa.gov/hrrr/)__ for the coninental US (CONUS) but not archived beyond three days. However a public __[archive](home.chpc.utah.edu/~u0553130/Brian_Blaylock/hrrr_FAQ.html)__ is available at the University of Utah. This archive does not require a license agreement. This model has the highest spatial resolution available in RAiDER, with a horizontal grid spacing of about 3 km, and is provided in a Lambert conformal conic projection.
-
-[return to table of content](#contents)
-
+## 2. NOAA weather models (HRRR)
+High-resolution rapid refresh (HRRR) weather model data products are generated by __[NOAA](https://rapidrefresh.noaa.gov/hrrr/)__ for the coninental US (CONUS) but not archived beyond three days. However, a public __[archive](home.chpc.utah.edu/~u0553130/Brian_Blaylock/hrrr_FAQ.html)__ is available at the University of Utah. This archive does not require a license agreement. This model has the highest spatial resolution available in RAiDER, with a horizontal grid spacing of about 3 km, and is provided in a Lambert conformal conic projection.
------
-## 2. ECMWF weather models (ERA5, ERA5T, ERAI, HRES)
+## 3. ECMWF weather models (ERA5, ERA5T, ERAI, HRES)
The Copernicus Climate Data Store (CDS) provides access to the European Centre for Medium-Range Weather Forecasts (__[ECMWF](https://www.ecmwf.int/)__) provides a number of different weather models, including ERA5 and ERA5T reanalysis models.
The ECMWF provides access to both reanalysis and real-time prediction models. You can read more information about their reanalysis models __[here](https://www.ecmwf.int/en/research/climate-reanalysis)__ and real-time model __[here](https://www.ecmwf.int/en/forecasts/datasets/catalogue-ecmwf-real-time-products)__. ECMWF models are global, with horizontal resolution of about 30 km for ERA-I, ERA-5, and ERA-5T, and 6 km for Hi-RES. All of these models come in a global projection (EPSG 4326, WGS-84).
@@ -34,14 +32,19 @@ The ECMWF provides access to both reanalysis and real-time prediction models. Yo
2. Confirm your email, etc.
3. Install the public API key and client as instructed __[here](https://cds.climate.copernicus.eu/api-how-to)__:
- a. Copy the URL and API key from the webpage into a file in your home directory name ~/.cdsapirc
- url: https://cds.climate.copernicus.eu/api/v2
- key: your_key_here
- __**Note**: the key represents the API key obtained upon the registration of CDS API, and should be replaced with the user's own information.__
-
- b. Install the CDS API using pip:
- pip install cdsapi
- ___**Note**: this step has been included in the conda install of RAiDER, thus can be omitted if one uses the recommended conda install of RAiDER___
+ a. Copy the URL and API key from the webpage into a file in your home directory name `~/.cdsapirc`
+
+ url: https://cds.climate.copernicus.eu/api/v2
+ key: your_key_here
+
+ **Note**: the key represents the API key obtained upon the registration of CDS API, and should be replaced with the user's own information.
+
+ b. Install the CDS API using pip
+
+ pip install cdsapi
+
+ **Note**: this step has been included in the conda install of RAiDER, thus can be omitted if one uses the recommended conda install of RAiDER
+
4. You must accept the [license](https://cds.climate.copernicus.eu/cdsapp/#!/terms/licence-to-use-copernicus-products) for each product you wish to download.
@@ -49,26 +52,27 @@ The ECMWF provides access to both reanalysis and real-time prediction models. Yo
ECMWF requires a license agreement to be able to access, download, and use their products. Instructions for completing this process is below.
-1. Create an account on the ECMWF servers __[here](https://accounts.ecmwf.int/auth/realms/ecmwf/protocol/openid-connect/auth?response_type=code&scope=openid%20email&client_id=apache-www&state=sBYlpcTRPhat8d6uuM9swLCxuP8&redirect_uri=https%3A%2F%2Fwww.ecmwf.int%2Foidc.cgi&nonce=RyEzBUy4m6oo_HxRQEmJxbc5jrKY4KFZd1Usgi8cpnM)__. The ERA-I model is open-access, while HRES requires a special liscence agreement.
+1. Create an account on the ECMWF servers __[here](https://accounts.ecmwf.int/auth/realms/ecmwf/protocol/openid-connect/auth?response_type=code&scope=openid%20email&client_id=apache-www&state=sBYlpcTRPhat8d6uuM9swLCxuP8&redirect_uri=https%3A%2F%2Fwww.ecmwf.int%2Foidc.cgi&nonce=RyEzBUy4m6oo_HxRQEmJxbc5jrKY4KFZd1Usgi8cpnM)__. The ERA-I model is open-access, while HRES requires a special licence agreement.
2. Confirm your email, etc.
3. Install the public API key and client as instructed __[here](https://confluence.ecmwf.int/display/WEBAPI/Access+ECMWF+Public+Datasets#AccessECMWFPublicDatasets-key)__:
- a. Copy the URL and API key from the webpage into a file in your home directory name ~/.ecmwfapirc
- {
- "url" : "https://api.ecmwf.int/v1",
- "key" : your key here,
- "email" : your email here
- }
+ a. Copy the URL and API key from the webpage into a file in your home directory name `~/.ecmwfapirc`
+
+ {
+ "url" : "https://api.ecmwf.int/v1",
+ "key" : your key here,
+ "email" : your email here
+ }
- __**Note**: the email that is used to register the user account, and the key represents the API key obtained upon the registration of ECMWF API, and should be replaced with the user's own information.__
+ **Note**: the email that is used to register the user account, and the key represents the API key obtained upon the registration of ECMWF API, and should be replaced with the user's own information.
- b. Install the ECMWF API using pip:
- ```pip install ecmwf-api-client```
- ___**Note**: this step has been included in the conda install of RAiDER, thus can be omitted if one uses the recommended conda install of RAiDER___
+ b. Install the ECMWF API using pip:
-[return to table of content](#contents)
+ pip install ecmwf-api-client`
-### 3. NASA weather models (GMAO, MERRA2)
+ **Note**: this step has been included in the conda install of RAiDER, thus can be omitted if one uses the recommended conda install of RAiDER
+
+## 4. NASA weather models (GMAO, MERRA2)
1. The Global Modeling and Assimilation Office (__[GMAO](https://www.nccs.nasa.gov/services/data-collections/coupled-products/geos5-forecast#:~:text=The%20Global%20Modeling%20and%20Assimilation,near%2Dreal%2Dtime%20production.)__) at NASA generates reanalysis weather models. GMAO products can also be accessed without a license agreement through the pyDAP interface implemented in RAiDER. GMAO has a horizontal grid spacing of approximately 33 km, and its projection is EPSG code 4326 (WGS-84).
@@ -85,7 +89,7 @@ Reference: __[The Modern-Era Retrospective Analysis for Research and Application
login
password
- __**Note**: the username and password represent the user's username and password.__
+ **Note**: the username and password represent the user's username and password.
4. Add the application `NASA GESDISC DATA ARCHIVE` by clicking on the `Applications->Authorized Apps` on the menu after logging into your Earthdata profile, and then scrolling down to the application `NASA GESDISC DATA ARCHIVE` to approve it. _This seems not required for GMAO for now, but recommended to do so for all OpenDAP-based weather models._
5. Install the OpenDAP using pip:
@@ -93,9 +97,6 @@ Reference: __[The Modern-Era Retrospective Analysis for Research and Application
pip install pydap==3.2.1
- ___**Note**: this step has been included in the conda install of RAiDER, thus can be omitted if one uses the recommended conda install of RAiDER___
+ **Note**: this step has been included in the conda install of RAiDER, thus can be omitted if one uses the recommended conda install of RAiDER
- ___**Note**: PyDAP v3.2.1 is required for now (thus specified in the above pip install command) because the latest v3.2.2 (as of now) has a known [bug](https://colab.research.google.com/drive/1f_ss1Oa3VzgAOd_p8sgekdnLVE5NW6s5) in accessing and slicing the GMAO data. This bug is expected to be fixed in newer versions of PyDAP.___
-
-[return to table of content](#contents)
-
+ **Note**: PyDAP v3.2.1 is required for now (thus specified in the above pip install command) because the latest v3.2.2 (as of now) has a known [bug](https://colab.research.google.com/drive/1f_ss1Oa3VzgAOd_p8sgekdnLVE5NW6s5) in accessing and slicing the GMAO data. This bug is expected to be fixed in newer versions of PyDAP.
diff --git a/docs/index.md b/docs/index.md
new file mode 120000
index 000000000..32d46ee88
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1 @@
+../README.md
\ No newline at end of file
diff --git a/docs/macros.py b/docs/macros.py
new file mode 100644
index 000000000..dc8fd6674
--- /dev/null
+++ b/docs/macros.py
@@ -0,0 +1,17 @@
+import requests
+
+import RAiDER
+
+
+def define_env(env):
+ """Macros Hook"""
+
+ @env.macro
+ def raider_version():
+ return RAiDER.__version__
+
+ @env.macro
+ def get_content(url):
+ response = requests.get(url)
+ response.raise_for_status()
+ return response.content.decode()
diff --git a/docs/reference.md b/docs/reference.md
new file mode 100644
index 000000000..da94b41f2
--- /dev/null
+++ b/docs/reference.md
@@ -0,0 +1,5 @@
+# RAiDER *v{{ raider_version() }}* API Reference
+
+::: RAiDER
+ options:
+ show_submodules: true
diff --git a/docs/reference_td.md b/docs/reference_td.md
new file mode 100644
index 000000000..8832496ca
--- /dev/null
+++ b/docs/reference_td.md
@@ -0,0 +1,3 @@
+***`tropo_delay`***
+
+::: RAiDER.delay
\ No newline at end of file
diff --git a/docs/tutorials.md b/docs/tutorials.md
new file mode 100644
index 000000000..a56121378
--- /dev/null
+++ b/docs/tutorials.md
@@ -0,0 +1,3 @@
+### Tutorials
+
+{{ get_content('https://raw.githubusercontent.com/dbekaert/RAiDER-docs/main/README.md') }}
diff --git a/environment.yml b/environment.yml
index 8f5e10837..b018451be 100644
--- a/environment.yml
+++ b/environment.yml
@@ -17,12 +17,12 @@ dependencies:
- cxx-compiler
- cython
- dask
- - dem_stitcher>=2.3.0
+ - dem_stitcher>=2.3.1
- ecmwf-api-client
- h5py
- herbie-data
- h5netcdf
- - isce3==0.8.0=*_2
+ - isce3>=0.9.0
- lxml
- matplotlib
- netcdf4
@@ -31,11 +31,14 @@ dependencies:
- progressbar
- pydap>3.2.2
- pyproj>=2.2.0
+ - pyyaml
- rasterio>=1.3.0
+ - requests
- s3fs
- scipy
- shapely
- sysroot_linux-64
+ - tqdm
- xarray
# For packaging and testing
- autopep8
@@ -44,7 +47,13 @@ dependencies:
- pytest-timeout
- pytest-console-scripts
- setuptools_scm >=6.2
-
+ # For docs website
+ - mkdocs
+ - mkdocstrings
+ - mkdocstrings-python
+ - mkdocs-macros-plugin
+ - mkdocs-material
+ - mkdocs-material-extensions
# For RAiDER-docs
- jupyterlab
- jupyter_contrib_nbextensions
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 000000000..1d799865b
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,29 @@
+site_name: RAiDER API Documentation
+site_description:
+repo_url: https://github.com/dbekaert/RAiDER
+repo_name: RAiDER
+
+nav:
+ - Home page: index.md
+ - Tutorials:
+ - Overview: tutorials.md
+ - RAiDER tutorial: https://nbviewer.org/github/dbekaert/RAiDER-docs/blob/main/notebooks/RAiDER_tutorial/RAiDER_tutorial.ipynb" target="_blank
+ - raiderStats tutorial: https://nbviewer.org/github/dbekaert/RAiDER-docs/blob/main/notebooks/raiderStats/raiderStats_tutorial.ipynb" target="_blank
+ - Downloading GNSS tropospheric delays: https://nbviewer.org/github/dbekaert/RAiDER-docs/blob/main/notebooks/raiderDownloadGNSS/raiderDownloadGNSS_tutorial.ipynb" target="_blank
+ - GNSS delay manipulation with Pandas: https://nbviewer.org/github/dbekaert/RAiDER-docs/blob/main/notebooks/Pandas_tutorial/Pandas_tutorial.ipynb" target="_blank
+ - Weather Model Access: WeatherModels.md
+ - Delay Calculation: reference_td.md
+ - API Reference: reference.md
+
+
+theme:
+ name: material
+
+plugins:
+ - search
+ - macros:
+ module_name: docs/macros
+ - mkdocstrings:
+ handlers:
+ python:
+ inherited_members: true
diff --git a/pyproject.toml b/pyproject.toml
index 170f9830e..19b81705d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,7 +15,8 @@ authors = [
{name="David Bekaert", email="david.bekaert@jpl.nasa.gov"},
{name="Jeremy Maurer", email="maurer.jeremy@gmail.com"},
{name="Piyush Agram", email="piyush@descarteslabs.com"},
- {name="Simran Sangha", email="simran.s.sangha@jpl.nasa.gov"}
+ {name="Simran Sangha", email="simran.s.sangha@jpl.nasa.gov"},
+ {name="Brett Buzzanga", email="buzzanga@jpl.nasa.gov"},
]
description = "Raytracing Atmospheric Delay Estimation for RADAR"
readme = "README.md"
@@ -40,11 +41,13 @@ repository = "https://github.com/dbekaert/RAiDER"
"Bug Tracker" = "https://github.com/dbekaert/RAiDER/issues"
[project.scripts]
-"generateGACOSVRT.py" = "RAiDER.models.generateGACOSVRT:main"
-"raiderDownloadGNSS.py" = "RAiDER.gnss.downloadGNSSDelays:main"
-"raider.py" = "RAiDER.cli.raider:main"
-"prepFromAria.py" = "RAiDER.cli.prepFromAria:main"
+"raider.py" = "RAiDER.cli.__main__:main"
+"calcDelays.py" = "RAiDER.cli.__main__:calcDelays"
+"calcDelaysGUNW.py" = "RAiDER.cli.__main__:calcDelaysGUNW"
+"raiderDownloadGNSS.py" = "RAiDER.cli.__main__:downloadGNSS"
+"downloadGNSS.py" = "RAiDER.cli.__main__:downloadGNSS"
"raiderStats.py" = "RAiDER.cli.statsPlot:main"
+"generateGACOSVRT.py" = "RAiDER.models.generateGACOSVRT:main"
[tool.setuptools]
zip-safe = false
diff --git a/test/_checkArgs.py b/test/_checkArgs.py
deleted file mode 100644
index 8be5b17e0..000000000
--- a/test/_checkArgs.py
+++ /dev/null
@@ -1,442 +0,0 @@
-import datetime
-import os
-import pytest
-
-import multiprocessing as mp
-import numpy as np
-import pandas as pd
-
-from test import TEST_DIR, pushd
-
-# import RAiDER.runProgram
-from RAiDER.cli.raider import (
- parseCMD, read_template_file, create_parser, read_template_file,
-)
-from RAiDER.checkArgs import checkArgs, makeDelayFileNames
-from RAiDER.constants import _ZREF
-from RAiDER.losreader import Zenith, Conventional, Raytracing
-
-
-SCENARIO_1 = os.path.join(TEST_DIR, "scenario_1")
-SCENARIO_2 = os.path.join(TEST_DIR, "scenario_2")
-
-
-def isWriteable(dirpath):
- '''Test whether a directory is writeable'''
- try:
- filehandle = open(os.path.join(dirpath, 'tmp.txt'), 'w')
- filehandle.close()
- return True
- except IOError:
- return False
-
-
-@pytest.fixture
-def parsed_args(tmp_path):
- parser = create_parser()
- args = parser.parse_args([
- '--date', '20200103',
- '--time', '23:00:00',
- # '--latlon', 'latfile.dat', 'lonfile.dat',
- '--bbox', '-1', '1', '-1', '1',
- '--model', 'ERA5',
- '--outformat', 'hdf5'
- ])
- return args, parser
-
-
-def test_checkArgs_outfmt_1(parsed_args):
- '''Test that passing height levels with hdf5 outformat works'''
- args, p = parsed_args
- args.outformat = 'hdf5'
- args.heightlvs = [10, 100, 1000]
- checkArgs(args, p)
- assert True
-
-
-def test_checkArgs_outfmt_2(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.heightlvs = [10, 100, 1000]
- args.outformat = 'envi'
- with pytest.raises(ValueError):
- checkArgs(args, p)
-
-
-def test_checkArgs_outfmt_3(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
- argDict = checkArgs(args, p)
- assert argDict['flag'] == 'station_file'
-
-
-def test_checkArgs_outfmt_4(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.query_area = [os.path.join(SCENARIO_1, 'geom', 'lat.dat'), os.path.join(SCENARIO_1, 'geom', 'lat.dat')]
- argDict = checkArgs(args, p)
- assert argDict['flag'] == 'files'
-
-
-def test_checkArgs_outfmt_5(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
- argDict = checkArgs(args, p)
- assert pd.read_csv(argDict['wetFilenames'][0]).shape == (8, 4)
-
-
-def test_checkArgs_outloc_1(parsed_args):
- '''Test that the default output and weather model directories are correct'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- out = argDict['out']
- wmLoc = argDict['wmLoc']
- assert os.path.abspath(out) == os.getcwd()
- assert os.path.abspath(wmLoc) == os.path.join(os.getcwd(), 'weather_files')
-
-
-def test_checkArgs_outloc_2(parsed_args, tmp_path):
- '''Tests that the correct output location gets assigned when provided'''
- with pushd(tmp_path):
- args, p = parsed_args
- args.out = tmp_path
- argDict = checkArgs(args, p)
- out = argDict['out']
- assert out == tmp_path
-
-
-def test_checkArgs_outloc_2b(parsed_args, tmp_path):
- ''' Tests that the weather model directory gets passed through by itself'''
- with pushd(tmp_path):
- args, p = parsed_args
- args.out = tmp_path
- args.wmLoc = 'weather_dir'
- argDict = checkArgs(args, p)
- assert argDict['wmLoc'] == 'weather_dir'
-
-
-def test_checkArgs_outloc_3(parsed_args):
- '''Tests that the weather model directory gets created when needed'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert os.path.isdir(argDict['wmLoc'])
-
-
-def test_checkArgs_outloc_4(parsed_args):
- '''Tests for creating writeable weather model directory'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
-
- assert isWriteable(argDict['wmLoc'])
-
-
-def test_ll_bounds_1(parsed_args):
- '''Tests that lats out of bounds raises error'''
- args, p = parsed_args
- args.query_area[0] = -91
- with pytest.raises(ValueError):
- checkArgs(args, p)
-
-
-def test_ll_bounds_2(parsed_args):
- '''Tests that lats out of bounds raises error'''
- args, p = parsed_args
- args.query_area[1] = 91
- with pytest.raises(ValueError):
- checkArgs(args, p)
-
-
-def test_los_1(parsed_args):
- '''Tests that lats out of bounds raises error'''
- args, p = parsed_args
- args.lineofsight = 'los.rdr'
- argDict = checkArgs(args, p)
- assert isinstance(argDict['los'], Conventional)
- assert argDict['los']._file == 'los.rdr'
-
-
-def test_los_2(parsed_args):
- '''Tests that lats out of bounds raises error'''
- args, p = parsed_args
- args.statevectors = 'sv.txt'
- argDict = checkArgs(args, p)
- assert isinstance(argDict['los'], Conventional)
- assert argDict['los']._file == 'sv.txt'
-
-
-def test_los_3(parsed_args):
- '''Tests that lats out of bounds raises error'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert isinstance(argDict['los'], Zenith)
-
-
-def test_models_1a(parsed_args):
- '''Tests that the weather model gets passed through correctly'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert argDict['weather_model']['type'].Model() == 'ERA-5'
- assert argDict['weather_model']['name'] == 'era5'
-
-
-def test_models_1b(parsed_args):
- '''Tests that the weather model gets passed through correctly'''
- args, p = parsed_args
- args.model = 'HRRR'
- argDict = checkArgs(args, p)
- assert argDict['weather_model']['type'].Model() == 'HRRR'
- assert argDict['weather_model']['name'] == 'hrrr'
-
-
-def test_models_1c(parsed_args):
- '''Tests that the weather model gets passed through correctly'''
- args, p = parsed_args
- args.model = 'NCMR'
- argDict = checkArgs(args, p)
- assert argDict['weather_model']['type'].Model() == 'NCMR'
- assert argDict['weather_model']['name'] == 'ncmr'
-
-
-def test_models_1d(parsed_args):
- '''Tests that the weather model gets passed through correctly'''
- args, p = parsed_args
- args.model = 'era-5'
- argDict = checkArgs(args, p)
- assert argDict['weather_model']['type'].Model() == 'ERA-5'
- assert argDict['weather_model']['name'] == 'era5'
-
-
-def test_models_1e(parsed_args):
- '''Tests that the weather model gets passed through correctly'''
- args, p = parsed_args
- args.model = 'ERA-5'
- argDict = checkArgs(args, p)
- assert argDict['weather_model']['type'].Model() == 'ERA-5'
- assert argDict['weather_model']['name'] == 'era5'
-
-
-def test_models_1f(parsed_args):
- '''Tests that the weather model gets passed through correctly'''
- args, p = parsed_args
- args.model = 'Era-5'
- argDict = checkArgs(args, p)
- assert argDict['weather_model']['type'].Model() == 'ERA-5'
- assert argDict['weather_model']['name'] == 'era5'
-
-
-def test_models_2(parsed_args):
- '''Tests that unknown weather models get rejected'''
- args, p = parsed_args
- args.model = 'unknown'
- with pytest.raises(NotImplementedError):
- checkArgs(args, p)
-
-
-def test_models_3a(parsed_args):
- '''Tests that WRF weather models requires files'''
- args, p = parsed_args
- args.model = 'WRF'
- with pytest.raises(RuntimeError):
- checkArgs(args, p)
-
-
-def test_models_3b(parsed_args):
- '''Tests that HDF5 weather models requires files'''
- args, p = parsed_args
- args.model = 'HDF5'
- with pytest.raises(RuntimeError):
- checkArgs(args, p)
-
-
-def test_models_3c(parsed_args):
- '''Tests that WRF weather models requires files'''
- args, p = parsed_args
- args.model = 'WRF'
- args.files = ['file1.wrf', 'file2.wrf']
- # argDict = checkArgs(args, p)
- # TODO
- assert True
-
-
-def test_zref_1(parsed_args):
- '''tests that default zref gets generated'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert argDict['zref'] == _ZREF
-
-
-def test_zref_2(parsed_args):
- '''tests that default zref gets generated'''
- ztest = 20000
- args, p = parsed_args
- args.zref = ztest
- argDict = checkArgs(args, p)
- assert argDict['zref'] == ztest
-
-
-def test_parallel_1(parsed_args):
- '''tests that parallel options are handled correctly'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert argDict['parallel'] == 1
-
-
-def test_parallel_2(parsed_args):
- '''tests that parallel options are handled correctly'''
- args, p = parsed_args
- args.parallel = 'all'
- argDict = checkArgs(args, p)
- assert argDict['parallel'] == mp.cpu_count()
-
-
-def test_parallel_3(parsed_args):
- '''tests that parallel options are handled correctly'''
- args, p = parsed_args
- args.parallel = 2
- argDict = checkArgs(args, p)
- assert argDict['parallel'] == 2
-
-
-def test_parallel_4(parsed_args):
- '''tests that parallel options are handled correctly'''
- args, p = parsed_args
- args.parallel = 2000
- argDict = checkArgs(args, p)
- assert argDict['parallel'] == mp.cpu_count()
-
-
-def test_verbose_1(parsed_args):
- '''tests that verbose option is handled correctly'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert not argDict['verbose']
-
-
-def test_verbose_2(parsed_args):
- '''tests that verbose option is handled correctly'''
- args, p = parsed_args
- args.verbose = True
- argDict = checkArgs(args, p)
- assert argDict['verbose']
-
-
-def test_download_only_1(parsed_args):
- '''tests that the download-only option is handled correctly'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert not argDict['download_only']
-
-
-def test_download_only_2(parsed_args):
- '''tests that the download-only option is handled correctly'''
- args, p = parsed_args
- args.download_only = True
- argDict = checkArgs(args, p)
- assert argDict['download_only']
-
-
-def test_useWeatherNodes_1(parsed_args):
- '''tests that the correct flag gets passed'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert argDict['flag'] == 'bounding_box' # default arguments use a bounding box
-
-
-def test_filenames_1(parsed_args):
- '''tests that the correct filenames are generated'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert 'Delay' not in argDict['wetFilenames'][0]
- assert 'wet' in argDict['wetFilenames'][0]
- assert 'hydro' in argDict['hydroFilenames'][0]
- assert '20200103' in argDict['wetFilenames'][0]
- assert '20200103' in argDict['hydroFilenames'][0]
- assert len(argDict['hydroFilenames']) == 1
-
-
-def test_filenames_2(parsed_args):
- '''tests that the correct filenames are generated'''
- args, p = parsed_args
- args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
- argDict = checkArgs(args, p)
- assert 'Delay' in argDict['wetFilenames'][0]
- assert '20200103' in argDict['wetFilenames'][0]
- assert len(argDict['wetFilenames']) == 1
-
-
-def test_makeDelayFileNames_1():
- assert makeDelayFileNames(None, None, "h5", "name", "dir") == \
- ("dir/name_wet_ztd.h5", "dir/name_hydro_ztd.h5")
-
-
-def test_makeDelayFileNames_2():
- assert makeDelayFileNames(None, (), "h5", "name", "dir") == \
- ("dir/name_wet_std.h5", "dir/name_hydro_std.h5")
-
-
-def test_makeDelayFileNames_3():
- assert makeDelayFileNames(datetime.datetime(2020, 1, 1, 1, 2, 3), None, "h5", "model_name", "dir") == \
- (
- "dir/model_name_wet_20200101T010203_ztd.h5",
- "dir/model_name_hydro_20200101T010203_ztd.h5"
- )
-
-
-def test_makeDelayFileNames_4():
- assert makeDelayFileNames(datetime.datetime(1900, 12, 31, 1, 2, 3), "los", "h5", "model_name", "dir") == \
- (
- "dir/model_name_wet_19001231T010203_std.h5",
- "dir/model_name_hydro_19001231T010203_std.h5"
- )
-
-
-def test_model2module():
- model_module_name, model_obj = modelName2Module('ERA5')
- assert model_obj().Model() == 'ERA-5'
-
-
-def test_dem_1(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- argDict = checkArgs(args, p)
- assert argDict['heights'][0] == 'skip'
- assert argDict['heights'][1] is None
-
-
-def test_dem_2(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.heightlvs = [10, 100, 1000]
- argDict = checkArgs(args, p)
- assert argDict['heights'][0] == 'lvs'
- assert np.allclose(argDict['heights'][1], [10, 100, 1000])
-
-
-def test_dem_3(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.heightlvs = [10, 100, 1000]
- args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
- argDict = checkArgs(args, p)
- assert argDict['heights'][0] == 'lvs'
- assert np.allclose(argDict['heights'][1], [10, 100, 1000])
-
-
-def test_dem_4(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.query_area = os.path.join(SCENARIO_2, 'stations.csv')
- argDict = checkArgs(args, p)
- assert argDict['heights'][0] == 'pandas'
- assert argDict['heights'][1][0] == argDict['wetFilenames'][0]
-
-
-def test_dem_5(parsed_args):
- '''Test that passing a raster format with height levels throws an error'''
- args, p = parsed_args
- args.query_area = [os.path.join(SCENARIO_1, 'geom', 'lat.dat'), os.path.join(SCENARIO_1, 'geom', 'lat.dat')]
- argDict = checkArgs(args, p)
- assert argDict['heights'][0] == 'download'
- assert argDict['heights'][1] == os.path.join(argDict['out'], 'geom', 'warpedDEM.dem')
diff --git a/test/scenario_3/HRRR_tropo_20181113T230000_ray.nc b/test/scenario_3/HRRR_tropo_20181113T230000_ray.nc
new file mode 100644
index 000000000..8ba4eed64
Binary files /dev/null and b/test/scenario_3/HRRR_tropo_20181113T230000_ray.nc differ
diff --git a/test/scenario_3/HRRR_tropo_20181113T230000_std.nc b/test/scenario_3/HRRR_tropo_20181113T230000_std.nc
index 8ba4eed64..063de4f1a 100644
Binary files a/test/scenario_3/HRRR_tropo_20181113T230000_std.nc and b/test/scenario_3/HRRR_tropo_20181113T230000_std.nc differ
diff --git a/test/scenario_3/raider_example_3.yaml b/test/scenario_3/raider_example_3.yaml
index b336ab452..94d298c8c 100644
--- a/test/scenario_3/raider_example_3.yaml
+++ b/test/scenario_3/raider_example_3.yaml
@@ -11,7 +11,7 @@ aoi_group:
height_group:
height_levels: 0 100 500 1000
los_group:
- ray_trace: True # Use projected slant delay by default
+ ray_trace: True
orbit_file: test/scenario_3/S1A_OPER_AUX_POEORB_OPOD_20181203T120749_V20181112T225942_20181114T005942.EOF
runtime_group:
output_projection: 4326
diff --git a/test/scenario_3/raider_example_3_proj.yaml b/test/scenario_3/raider_example_3_proj.yaml
new file mode 100644
index 000000000..5f26ffb2b
--- /dev/null
+++ b/test/scenario_3/raider_example_3_proj.yaml
@@ -0,0 +1,18 @@
+# vim: set filetype=yaml:
+look_dir: right
+date_group:
+ date_list: [20181113]
+time_group:
+ time: "23:00:00"
+ end_time:
+weather_model: HRRR
+aoi_group:
+ bounding_box: 36.8 36.85 -76.15 -76.05
+height_group:
+ height_levels: 0 100 500 1000
+los_group:
+ ray_trace: False # Use projected slant delay by default
+ orbit_file: test/scenario_3/S1A_OPER_AUX_POEORB_OPOD_20181203T120749_V20181112T225942_20181114T005942.EOF
+runtime_group:
+ output_projection: 4326
+ cube_spacing_in_m: 5000.0
diff --git a/test/scenario_3/raider_example_4.yaml b/test/scenario_3/raider_example_4.yaml
deleted file mode 100644
index ce6dc46bf..000000000
--- a/test/scenario_3/raider_example_4.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-# vim: set filetype=yaml:
- look_dir: right
- date_group:
- date_list: [20181113]
- time_group:
- time: "23:00:00"
- end_time:
- weather_model: HRRR
- aoi_group:
- bounding_box: 36.8 36.85 -76.15 -76.05
- los_group:
- ray_trace: False # Use projected slant delay by default
- orbit_file: test/scenario_3/S1A_OPER_AUX_POEORB_OPOD_20181203T120749_V20181112T225942_20181114T005942.EOF
- runtime_group:
- output_projection: 4326
- cube_spacing_in_m: 250.0
-
\ No newline at end of file
diff --git a/test/test_checkArgs.py b/test/test_checkArgs.py
new file mode 100644
index 000000000..e0699e733
--- /dev/null
+++ b/test/test_checkArgs.py
@@ -0,0 +1,180 @@
+import datetime
+import os
+import pytest
+
+import multiprocessing as mp
+import numpy as np
+import pandas as pd
+
+from test import TEST_DIR, pushd
+
+from RAiDER.cli import DEFAULT_DICT
+from RAiDER.checkArgs import checkArgs, makeDelayFileNames
+from RAiDER.llreader import BoundingBox, StationFile, RasterRDR
+from RAiDER.losreader import Zenith, Conventional, Raytracing
+from RAiDER.models.gmao import GMAO
+
+
+SCENARIO_1 = os.path.join(TEST_DIR, "scenario_1")
+SCENARIO_2 = os.path.join(TEST_DIR, "scenario_2")
+
+@pytest.fixture
+def args():
+ d = DEFAULT_DICT
+ d['date_list'] = [datetime.datetime(2018, 1, 1)]
+ d['time'] = datetime.time(12,0,0)
+ d['aoi'] = BoundingBox([38, 39, -92, -91])
+ d['los'] = Zenith()
+ d['weather_model'] = GMAO()
+
+ return d
+
+
+def isWriteable(dirpath):
+ '''Test whether a directory is writeable'''
+ try:
+ filehandle = open(os.path.join(dirpath, 'tmp.txt'), 'w')
+ filehandle.close()
+ return True
+ except IOError:
+ return False
+
+def test_checkArgs_outfmt_1(args):
+ '''Test that passing height levels with hdf5 outformat works'''
+ args = args
+ args.file_format = 'h5'
+ args.heightlvls = [10, 100, 1000]
+ checkArgs(args)
+ assert os.path.splitext(args.wetFilenames[0])[-1] == '.h5'
+
+
+def test_checkArgs_outfmt_2(args):
+ '''Test that passing a raster format with height levels throws an error'''
+ args = args
+ args.heightlvs = [10, 100, 1000]
+ args.file_format = 'GTiff'
+ args = checkArgs(args)
+ assert os.path.splitext(args.wetFilenames[0])[-1] == '.nc'
+
+
+def test_checkArgs_outfmt_3(args):
+ '''Test that passing a raster format with height levels throws an error'''
+ args = args
+ with pytest.raises(FileNotFoundError):
+ args.aoi = StationFile(os.path.join('fake_dir', 'stations.csv'))
+
+
+def test_checkArgs_outfmt_4(args):
+ '''Test that passing a raster format with height levels throws an error'''
+ args = args
+ args.aoi = RasterRDR(
+ lat_file = os.path.join(SCENARIO_1, 'geom', 'lat.dat'),
+ lon_file = os.path.join(SCENARIO_1, 'geom', 'lon.dat'),
+ )
+ argDict = checkArgs(args)
+ assert argDict.aoi.type()=='radar_rasters'
+
+
+def test_checkArgs_outfmt_5(args):
+ '''Test that passing a raster format with height levels throws an error'''
+ args = args
+ args.aoi = StationFile(os.path.join(SCENARIO_2, 'stations.csv'))
+ argDict = checkArgs(args)
+ assert pd.read_csv(argDict['wetFilenames'][0]).shape == (8, 4)
+
+
+def test_checkArgs_outloc_1(args):
+ '''Test that the default output and weather model directories are correct'''
+ args = args
+ argDict = checkArgs(args)
+ out = argDict['output_directory']
+ wmLoc = argDict['weather_model_directory']
+ assert os.path.abspath(out) == os.getcwd()
+ assert os.path.abspath(wmLoc) == os.path.join(os.getcwd(), 'weather_files')
+
+
+def test_checkArgs_outloc_2(args, tmp_path):
+ '''Tests that the correct output location gets assigned when provided'''
+ with pushd(tmp_path):
+ args = args
+ args.output_directory = tmp_path
+ argDict = checkArgs(args)
+ out = argDict['output_directory']
+ assert out == tmp_path
+
+
+def test_checkArgs_outloc_2b(args, tmp_path):
+ ''' Tests that the weather model directory gets passed through by itself'''
+ with pushd(tmp_path):
+ args = args
+ args.output_directory = tmp_path
+ args.weather_model_directory = 'weather_dir'
+ argDict = checkArgs(args)
+ assert argDict['weather_model_directory'] == 'weather_dir'
+
+
+def test_checkArgs_outloc_3(args, tmp_path):
+ '''Tests that the weather model directory gets created when needed'''
+ with pushd(tmp_path):
+ args = args
+ args.output_directory = tmp_path
+ argDict = checkArgs(args)
+ assert os.path.isdir(argDict['weather_model_directory'])
+
+
+def test_checkArgs_outloc_4(args):
+ '''Tests for creating writeable weather model directory'''
+ args = args
+ argDict = checkArgs(args)
+
+ assert isWriteable(argDict['weather_model_directory'])
+
+
+def test_filenames_1(args):
+ '''tests that the correct filenames are generated'''
+ args = args
+ argDict = checkArgs(args)
+ assert 'Delay' not in argDict['wetFilenames'][0]
+ assert 'wet' in argDict['wetFilenames'][0]
+ assert 'hydro' in argDict['hydroFilenames'][0]
+ assert '20180101' in argDict['wetFilenames'][0]
+ assert '20180101' in argDict['hydroFilenames'][0]
+ assert len(argDict['hydroFilenames']) == 1
+
+
+def test_filenames_2(args):
+ '''tests that the correct filenames are generated'''
+ args = args
+ args.aoi = StationFile(os.path.join(SCENARIO_2, 'stations.csv'))
+ argDict = checkArgs(args)
+ assert '20180101' in argDict['wetFilenames'][0]
+ assert len(argDict['wetFilenames']) == 1
+
+
+def test_makeDelayFileNames_1():
+ assert makeDelayFileNames(None, None, "h5", "name", "dir") == \
+ ("dir/name_wet_ztd.h5", "dir/name_hydro_ztd.h5")
+
+
+def test_makeDelayFileNames_2():
+ assert makeDelayFileNames(None, (), "h5", "name", "dir") == \
+ ("dir/name_wet_std.h5", "dir/name_hydro_std.h5")
+
+
+def test_makeDelayFileNames_3():
+ assert makeDelayFileNames(datetime.datetime(2020, 1, 1, 1, 2, 3), None, "h5", "model_name", "dir") == \
+ (
+ "dir/model_name_wet_20200101T010203_ztd.h5",
+ "dir/model_name_hydro_20200101T010203_ztd.h5"
+ )
+
+
+def test_makeDelayFileNames_4():
+ assert makeDelayFileNames(datetime.datetime(1900, 12, 31, 1, 2, 3), "los", "h5", "model_name", "dir") == \
+ (
+ "dir/model_name_wet_19001231T010203_std.h5",
+ "dir/model_name_hydro_19001231T010203_std.h5"
+ )
+
+
+
diff --git a/test/test_llreader.py b/test/test_llreader.py
index dff8681a6..71da892dd 100644
--- a/test/test_llreader.py
+++ b/test/test_llreader.py
@@ -6,7 +6,7 @@
from test import GEOM_DIR, TEST_DIR
-from RAiDER.cli.raider import create_parser
+from RAiDER.cli.raider import calcDelays
from RAiDER.utilFcns import rio_open
from RAiDER.llreader import (
@@ -20,7 +20,7 @@
@pytest.fixture
def parser():
- return create_parser()
+ return calcDelays()
@pytest.fixture
@@ -33,6 +33,14 @@ def llfiles():
return os.path.join(SCENARIO1_DIR, 'lat.dat'), os.path.join(SCENARIO1_DIR, 'lon.dat')
+def test_latlon_reader_2():
+ with pytest.raises(ValueError):
+ RasterRDR(lat_file=None, lon_file=None)
+
+ with pytest.raises(ValueError):
+ RasterRDR(lat_file='doesnotexist.rdr', lon_file='doesnotexist.rdr')
+
+
def test_latlon_reader():
latfile = os.path.join(GEOM_DIR, 'lat.rdr')
lonfile = os.path.join(GEOM_DIR, 'lon.rdr')
@@ -88,3 +96,9 @@ def test_bounds_from_csv(station_file):
bounds_true = [33.746, 36.795, -118.312, -114.892]
snwe = bounds_from_csv(station_file)
assert all([np.allclose(b, t) for b, t in zip(snwe, bounds_true)])
+
+
+def test_readZ_sf(station_file):
+ aoi = StationFile(station_file)
+ assert np.allclose(aoi.readZ(), .1)
+
diff --git a/test/test_scenario_3.py b/test/test_scenario_3.py
new file mode 100644
index 000000000..b168508dd
--- /dev/null
+++ b/test/test_scenario_3.py
@@ -0,0 +1,27 @@
+import os
+import pytest
+import subprocess
+
+from test import TEST_DIR
+
+import numpy as np
+import xarray as xr
+
+
+
+def test_scenario_3():
+ SCENARIO_DIR = os.path.join(TEST_DIR, "scenario_3")
+
+ test_path = os.path.join(SCENARIO_DIR, 'raider_example_3.yaml')
+ process = subprocess.run(['raider.py', test_path],stdout=subprocess.PIPE, universal_newlines=True)
+ assert process.returncode == 0
+
+ new_data = xr.load_dataset('HRRR_tropo_20181113T230000_ray.nc')
+ golden_data = xr.load_dataset(os.path.join(SCENARIO_DIR, 'HRRR_tropo_20181113T230000_ray.nc'))
+
+ assert np.allclose(golden_data['wet'], new_data['wet'])
+ assert np.allclose(golden_data['hydro'], new_data['hydro'])
+
+ # Clean up files
+ subprocess.run(['rm', '-f', './HRRR*'])
+ subprocess.run(['rm', '-rf', './weather_files'])
diff --git a/test/test_scenario_3_proj.py b/test/test_scenario_3_proj.py
new file mode 100644
index 000000000..5e3c07af1
--- /dev/null
+++ b/test/test_scenario_3_proj.py
@@ -0,0 +1,27 @@
+import os
+import pytest
+import subprocess
+
+from test import TEST_DIR
+
+import numpy as np
+import xarray as xr
+
+
+# @pytest.mark.skip
+def test_scenario_3_proj():
+ SCENARIO_DIR = os.path.join(TEST_DIR, "scenario_3")
+
+ test_path = os.path.join(SCENARIO_DIR, 'raider_example_3_proj.yaml')
+ process = subprocess.run(['raider.py', test_path],stdout=subprocess.PIPE, universal_newlines=True)
+ assert process.returncode == 0
+
+ new_data = xr.load_dataset('HRRR_tropo_20181113T230000_std.nc')
+ golden_data = xr.load_dataset(os.path.join(SCENARIO_DIR, 'HRRR_tropo_20181113T230000_std.nc'))
+
+ assert np.allclose(golden_data['wet'], new_data['wet'])
+ assert np.allclose(golden_data['hydro'], new_data['hydro'])
+
+ # Clean up files
+ subprocess.run(['rm', '-f', './HRRR*'])
+ subprocess.run(['rm', '-rf', './weather_files'])
\ No newline at end of file
diff --git a/test/test_scenarios.py b/test/test_scenarios.py
index 42adde142..26ce04511 100644
--- a/test/test_scenarios.py
+++ b/test/test_scenarios.py
@@ -17,6 +17,7 @@ def test_scenario_1():
new_data = xr.load_dataset('HRRR_tropo_20200101T120000_ztd.nc')
golden_data = xr.load_dataset(os.path.join(SCENARIO_DIR, 'HRRR_tropo_20200101T120000_ztd.nc'))
+
assert np.allclose(golden_data['wet'], new_data['wet'])
assert np.allclose(golden_data['hydro'], new_data['hydro'])
@@ -25,32 +26,3 @@ def test_scenario_1():
subprocess.run(['rm', '-f', './HRRR*'])
subprocess.run(['rm', '-rf', './weather_files'])
-
-#TODO: Next release include GNSS station test
-# @pytest.mark.long
-# def test_scenario_2():
-# test_path = os.path.join(TEST_DIR, "scenario_2", 'raider_example_2.yaml')
-# process = subprocess.run(['raider.py', test_path],stdout=subprocess.PIPE, universal_newlines=True)
-# assert process.returncode == 0
-
-# # Clean up files
-# subprocess.run(['rm', '-f', './HRRR*'])
-# subprocess.run(['rm', '-rf', './weather_files'])
-
-
-def test_scenario_3():
- SCENARIO_DIR = os.path.join(TEST_DIR, "scenario_3")
-
- test_path = os.path.join(SCENARIO_DIR, 'raider_example_3.yaml')
- process = subprocess.run(['raider.py', test_path],stdout=subprocess.PIPE, universal_newlines=True)
- assert process.returncode == 0
-
- new_data = xr.load_dataset('HRRR_tropo_20181113T230000_std.nc')
- golden_data = xr.load_dataset(os.path.join(SCENARIO_DIR, 'HRRR_tropo_20181113T230000_std.nc'))
-
- assert np.allclose(golden_data['wet'], new_data['wet'])
- assert np.allclose(golden_data['hydro'], new_data['hydro'])
-
- # Clean up files
- subprocess.run(['rm', '-f', './HRRR*'])
- subprocess.run(['rm', '-rf', './weather_files'])
diff --git a/test/weather_model/test_downloaders.py b/test/weather_model/test_downloaders.py
index 6f86dc118..bd1237197 100644
--- a/test/weather_model/test_downloaders.py
+++ b/test/weather_model/test_downloaders.py
@@ -10,7 +10,8 @@
from RAiDER.models.era5t import ERA5T
from RAiDER.models.erai import ERAI
-@pytest.mark.skip
+
+@pytest.mark.long
def test_era5():
wm = ERA5()
wm.fetch(
@@ -19,7 +20,8 @@ def test_era5():
datetime(2020, 1, 1, 0, 0, 0)
)
-@pytest.mark.skip
+
+@pytest.mark.long
def test_era5t():
wm = ERA5T()
wm.fetch(
@@ -29,6 +31,7 @@ def test_era5t():
)
+@pytest.mark.long
def test_erai():
wm = ERAI()
wm.fetch(
diff --git a/test/weather_model/test_weather_model.py b/test/weather_model/test_weather_model.py
index 0cf5cc633..74ddd4f28 100644
--- a/test/weather_model/test_weather_model.py
+++ b/test/weather_model/test_weather_model.py
@@ -13,7 +13,7 @@
from pyproj import CRS
from RAiDER.constants import _ZMIN, _ZREF
-from RAiDER.delay import build_cube_ray
+from RAiDER.delay import _build_cube_ray
from RAiDER.losreader import state_to_los
from RAiDER.models.weatherModel import (
WeatherModel,
@@ -240,7 +240,7 @@ def test_build_cube_ray(setup_fake_raytracing, model):
#TODO: Check that the look vectors are not nans
lv, xyz = state_to_los(svs, np.stack([_Y.ravel(), _X.ravel(), _Z.ravel()], axis=-1),out="ecef")
- out = build_cube_ray(xs, ys, zs, orb, look_dir, CRS(4326), CRS(4326), [m.interpWet(), m.interpHydro()], elp=elp)
+ out = _build_cube_ray(xs, ys, zs, orb, look_dir, CRS(4326), CRS(4326), [m.interpWet(), m.interpHydro()], elp=elp)
assert out.shape == out_true.shape
diff --git a/tools/RAiDER/aria/calcGUNW.py b/tools/RAiDER/aria/calcGUNW.py
new file mode 100644
index 000000000..79ea59ca1
--- /dev/null
+++ b/tools/RAiDER/aria/calcGUNW.py
@@ -0,0 +1,8 @@
+def main(args):
+ """ Calculate interferometric phase delay at dates in ARIA standard product (GUNW) """
+ pass
+
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/RAiDER/aria/prepFromGUNW.py b/tools/RAiDER/aria/prepFromGUNW.py
new file mode 100644
index 000000000..6076d5b02
--- /dev/null
+++ b/tools/RAiDER/aria/prepFromGUNW.py
@@ -0,0 +1,11 @@
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+# Author: Jeremy Maurer, Brett Buzzanga
+# Copyright 2022, by the California Institute of Technology. ALL RIGHTS
+# RESERVED. United States Government Sponsorship acknowledged.
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+def main(args):
+ """ Get needed RAiDER parameters from an ARIA standard product (GUNW) """
+ pass
diff --git a/tools/RAiDER/checkArgs.py b/tools/RAiDER/checkArgs.py
index 774b548f5..04443f7c8 100644
--- a/tools/RAiDER/checkArgs.py
+++ b/tools/RAiDER/checkArgs.py
@@ -8,11 +8,14 @@
import os
import pandas as pd
+import rasterio.drivers as rd
from datetime import datetime
+
from RAiDER.losreader import Zenith
from RAiDER.llreader import BoundingBox
+from RAiDER.logger import logger
def checkArgs(args):
@@ -34,36 +37,54 @@ def checkArgs(args):
# filenames
wetNames, hydroNames = [], []
for d in args.date_list:
- if not args.aoi is not BoundingBox:
- if args.station_file is not None:
+ if (args.aoi.type() != 'bounding_box'):
+
+ # Handle the GNSS station file
+ if (args.aoi.type()=='station_file'):
wetFilename = os.path.join(
args.output_directory,
'{}_Delay_{}.csv'
.format(
- args.weather_model,
- args.time.strftime('%Y%m%dT%H%M%S'),
+ args.weather_model.Model(),
+ d.strftime('%Y%m%dT%H%M%S'),
)
)
- hydroFilename = wetFilename
+ hydroFilename = None # only the 'wetFilename' is used for the station_file
- # copy the input file to the output location for editing
- indf = pd.read_csv(args.query_area).drop_duplicates(subset=["Lat", "Lon"])
+ # copy the input station file to the output location for editing
+ indf = pd.read_csv(args.aoi._filename).drop_duplicates(subset=["Lat", "Lon"])
indf.to_csv(wetFilename, index=False)
else:
- wetNames.append(None)
- hydroNames.append(None)
+ # This implies rasters
+ fmt = get_raster_ext(args.file_format)
+ wetFilename, hydroFilename = makeDelayFileNames(
+ d,
+ args.los,
+ fmt,
+ args.weather_model._dataset.upper(),
+ args.output_directory,
+ )
+
+
else:
+ # In this case a cube file format is needed
+ if args.file_format not in '.nc .h5 h5 hdf5 .hdf5 nc'.split():
+ fmt = 'nc'
+ logger.debug('Invalid extension %s for cube. Defaulting to .nc', args.file_format)
+ else:
+ fmt = args.file_format.strip('.').replace('df', '')
+
wetFilename, hydroFilename = makeDelayFileNames(
d,
args.los,
- args.raster_format,
+ fmt,
args.weather_model._dataset.upper(),
args.output_directory,
)
-
- wetNames.append(wetFilename)
- hydroNames.append(hydroFilename)
+
+ wetNames.append(wetFilename)
+ hydroNames.append(hydroFilename)
args.wetFilenames = wetNames
args.hydroFilenames = hydroNames
@@ -71,6 +92,20 @@ def checkArgs(args):
return args
+def get_raster_ext(fmt):
+ drivers = rd.raster_driver_extensions()
+ extensions = {value.upper():key for key, value in drivers.items()}
+
+ # add in ENVI/ISCE formats with generic extension
+ extensions['ENVI'] = '.dat'
+ extensions['ISCE'] = '.dat'
+
+ try:
+ return extensions[fmt.upper()]
+ except KeyError:
+ raise ValueError('{} is not a valid gdal/rasterio file format for rasters'.format(fmt))
+
+
def makeDelayFileNames(time, los, outformat, weather_model_name, out):
'''
return names for the wet and hydrostatic delays.
diff --git a/tools/RAiDER/cli/__init__.py b/tools/RAiDER/cli/__init__.py
index e69de29bb..79a9cc19e 100644
--- a/tools/RAiDER/cli/__init__.py
+++ b/tools/RAiDER/cli/__init__.py
@@ -0,0 +1,45 @@
+import os
+from RAiDER.constants import _ZREF, _CUBE_SPACING_IN_M
+
+class AttributeDict(dict):
+ __getattr__ = dict.__getitem__
+ __setattr__ = dict.__setitem__
+ __delattr__ = dict.__delitem__
+
+DEFAULT_DICT = AttributeDict(
+ dict(
+ look_dir='right',
+ date_start=None,
+ date_end=None,
+ date_step=None,
+ date_list=None,
+ time=None,
+ end_time=None,
+ weather_model=None,
+ lat_file=None,
+ lon_file=None,
+ station_file=None,
+ bounding_box=None,
+ geocoded_file=None,
+ dem=None,
+ use_dem_latlon=False,
+ height_levels=None,
+ height_file_rdr=None,
+ ray_trace=False,
+ zref=_ZREF,
+ cube_spacing_in_m=_CUBE_SPACING_IN_M,
+ los_file=None,
+ los_convention='isce',
+ los_cube=None,
+ orbit_file=None,
+ verbose=True,
+ raster_format='GTiff',
+ file_format='GTiff',
+ output_directory=os.getcwd(),
+ weather_model_directory=os.path.join(
+ os.getcwd(),
+ 'weather_files'
+ ),
+ output_projection='EPSG:4236',
+ )
+ )
diff --git a/tools/RAiDER/cli/__main__.py b/tools/RAiDER/cli/__main__.py
new file mode 100644
index 000000000..b1cc91a95
--- /dev/null
+++ b/tools/RAiDER/cli/__main__.py
@@ -0,0 +1,25 @@
+import argparse
+import os
+from importlib.metadata import entry_points
+
+from RAiDER.cli.raider import calcDelays, downloadGNSS, calcDelaysGUNW
+
+def main():
+ parser = argparse.ArgumentParser(
+ prefix_chars='+',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter
+ )
+ parser.add_argument(
+ '++process', choices=['calcDelays', 'downloadGNSS', 'calcDelaysGUNW'],
+ default='calcDelays',
+ help='Select the entrypoint to use'
+ )
+ args, unknowns = parser.parse_known_args()
+ os.sys.argv = [args.process, *unknowns]
+
+ process_entry_point = entry_points(group='console_scripts', name=f'{args.process}.py')[0]
+ process_entry_point.load()()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/tools/RAiDER/cli/raider.py b/tools/RAiDER/cli/raider.py
index b417732b7..8613a4f82 100644
--- a/tools/RAiDER/cli/raider.py
+++ b/tools/RAiDER/cli/raider.py
@@ -8,11 +8,10 @@
import RAiDER
from RAiDER.constants import _ZREF, _CUBE_SPACING_IN_M
from RAiDER.logger import logger, logging
-from RAiDER.cli.validators import (enforce_time, enforce_bbox, parse_dates,
- get_query_region, get_heights, get_los, enforce_wm)
-
-from RAiDER.checkArgs import checkArgs
-from RAiDER.delay import main as main_delay
+from RAiDER.cli import DEFAULT_DICT, AttributeDict
+from RAiDER.cli.validators import (
+ enforce_time, enforce_bbox, parse_dates, get_query_region, get_heights, get_los, enforce_wm
+)
HELP_MESSAGE = """
@@ -32,47 +31,6 @@
raider.py customTemplatefile.cfg
"""
-class AttributeDict(dict):
- __getattr__ = dict.__getitem__
- __setattr__ = dict.__setitem__
- __delattr__ = dict.__delitem__
-
-DEFAULT_DICT = dict(
- look_dir='right',
- date_start=None,
- date_end=None,
- date_step=None,
- date_list=None,
- time=None,
- end_time=None,
- weather_model=None,
- lat_file=None,
- lon_file=None,
- station_file=None,
- bounding_box=None,
- geocoded_file=None,
- dem=None,
- use_dem_latlon=False,
- height_levels=None,
- height_file_rdr=None,
- ray_trace=False,
- zref=_ZREF,
- cube_spacing_in_m=_CUBE_SPACING_IN_M, # TODO - Where are these parsed?
- los_file=None,
- los_convention='isce',
- los_cube=None,
- orbit_file=None,
- verbose=True,
- raster_format='GTiff',
- output_directory=os.getcwd(),
- weather_model_directory=os.path.join(
- os.getcwd(),
- 'weather_files'
- ),
- output_projection='EPSG:4236',
- )
-
-
def create_parser():
"""Parse command line arguments using argparse."""
p = argparse.ArgumentParser(
@@ -156,14 +114,17 @@ def parseCMD(iargs=None):
def read_template_file(fname):
"""
Read the template file into a dictionary structure.
- Parameters: fname - str, full path to the template file
- delimiter - str, string to separate the key and value
- skip_chars - list of str, skip certain charaters in values
- Returns: template - dict, file content
- Examples: template = read_template('raider.yaml')
+ Args:
+ fname (str): full path to the template file
+ Returns:
+ dict: arguments to pass to RAiDER functions
+
+ Examples:
+ >>> template = read_template_file('raider.yaml')
- Modified from MintPy's 'read_template'
"""
+ from RAiDER.cli.validators import (enforce_time, enforce_bbox, parse_dates,
+ get_query_region, get_heights, get_los, enforce_wm)
with open(fname, 'r') as f:
try:
params = yaml.safe_load(f)
@@ -231,30 +192,303 @@ def drop_nans(d):
return d
-##########################################################################
-def main(iargs=None):
- # parse
- inps = parseCMD(iargs)
+def calcDelays(iargs=None):
+ """ Parse command line arguments using argparse. """
+ import RAiDER
+ from RAiDER.delay import tropo_delay
+ from RAiDER.checkArgs import checkArgs
+ from RAiDER.processWM import prepareWeatherModel
+ from RAiDER.utilFcns import writeDelays
+ examples = 'Examples of use:' \
+ '\n\t raider.py customTemplatefile.cfg' \
+ '\n\t raider.py -g'
+
+ p = argparse.ArgumentParser(
+ description =
+ 'Command line interface for RAiDER processing with a configure file.'
+ 'Default options can be found by running: raider.py --generate_config',
+ epilog=examples, formatter_class=argparse.RawDescriptionHelpFormatter)
+
+ p.add_argument(
+ 'customTemplateFile', nargs='?',
+ help='custom template with option settings.\n' +
+ "ignored if the default smallbaselineApp.cfg is input."
+ )
+
+ p.add_argument(
+ '-g', '--generate_template', action='store_true',
+ help='generate default template (if it does not exist) and exit.'
+ )
+
+ p.add_argument(
+ '--download_only', action='store_true',
+ help='only download a weather model.'
+ )
+
+ ## if not None, will replace first argument (customTemplateFile)
+ args = p.parse_args(args=iargs)
+
+ # default input file
+ template_file = os.path.join(os.path.dirname(RAiDER.__file__),
+ 'cli', 'raider.yaml')
+
+ if args.generate_template:
+ dst = os.path.join(os.getcwd(), 'raider.yaml')
+ shutil.copyfile(template_file, dst)
+ logger.info('Wrote %s', dst)
+ os.sys.exit()
+
+
+ # check: existence of input template files
+ if (not args.customTemplateFile
+ and not os.path.isfile(os.path.basename(template_file))
+ and not args.generate_template):
+ msg = "No template file found! It requires that either:"
+ msg += "\n a custom template file, OR the default template "
+ msg += "\n file 'raider.yaml' exists in current directory."
+
+ p.print_usage()
+ print(examples)
+ raise SystemExit(f'ERROR: {msg}')
+
+ if args.customTemplateFile:
+ # check the existence
+ if not os.path.isfile(args.customTemplateFile):
+ raise FileNotFoundError(args.customTemplateFile)
+
+ args.customTemplateFile = os.path.abspath(args.customTemplateFile)
+ else:
+ args.customTemplateFile = template_file
# Read the template file
- params = read_template_file(inps.customTemplateFile)
+ params = read_template_file(args.customTemplateFile)
# Argument checking
- params = checkArgs(params)
-
- params['download_only'] = inps.download_only
+ params = checkArgs(params)
if not params.verbose:
logger.setLevel(logging.INFO)
-
+ delay_dct = {}
for t, w, f in zip(
params['date_list'],
params['wetFilenames'],
params['hydroFilenames']
):
+
+ los = params['los']
+ aoi = params['aoi']
+ model = params['weather_model']
+
+ if los.ray_trace():
+ ll_bounds = aoi.add_buffer(buffer=1) # add a buffer for raytracing
+ else:
+ ll_bounds = aoi.bounds()
+
+ ###########################################################
+ # weather model calculation
+ logger.debug('Starting to run the weather model calculation')
+ logger.debug('Time: {}'.format(t.strftime('%Y%m%d')))
+ logger.debug('Beginning weather model pre-processing')
try:
- (_, _) = main_delay(t, w, f, params)
+ weather_model_file = prepareWeatherModel(
+ model, t,
+ ll_bounds=ll_bounds, # SNWE
+ wmLoc=params['weather_model_directory'],
+ makePlots=params['verbose'],
+ )
except RuntimeError:
logger.exception("Date %s failed", t)
continue
+
+ # Now process the delays
+ try:
+ wet_delay, hydro_delay = tropo_delay(
+ t, weather_model_file, aoi, los,
+ height_levels = params['height_levels'],
+ out_proj = params['output_projection'],
+ look_dir = params['look_dir'],
+ cube_spacing_m = params['cube_spacing_in_m'],
+ )
+ except RuntimeError:
+ logger.exception("Date %s failed", t)
+ continue
+
+ ###########################################################
+ # Write the delays to file
+ # Different options depending on the inputs
+
+ if los.is_Projected():
+ out_filename = w.replace("_ztd", "_std")
+ f = f.replace("_ztd", "_std")
+ elif los.ray_trace():
+ out_filename = w.replace("_std", "_ray")
+ f = f.replace("_std", "_ray")
+ else:
+ out_filename = w
+
+ if hydro_delay is None:
+ # means that a dataset was returned
+ ds = wet_delay
+ ext = os.path.splitext(out_filename)[1]
+ if ext not in ['.nc', '.h5']:
+ out_filename = f'{os.path.splitext(out_filename)[0]}.nc'
+
+ out_filename = out_filename.replace("wet", "tropo")
+
+ if out_filename.endswith(".nc"):
+ ds.to_netcdf(out_filename, mode="w")
+ elif out_filename.endswith(".h5"):
+ ds.to_netcdf(out_filename, engine="h5netcdf", invalid_netcdf=True)
+
+ else:
+ if aoi.type() == 'station_file':
+ out_filename = f'{os.path.splitext(out_filename)[0]}.csv'
+
+ if aoi.type() in ['station_file', 'radar_rasters', 'geocoded_file']:
+ writeDelays(aoi, wet_delay, hydro_delay, out_filename, f, outformat=params['raster_format'])
+
+ logger.info('Wrote hydro delays to: %s', f)
+
+ logger.info('Wrote wet delays to: %s', out_filename)
+
+ # delay_dct[t] = wet_delay, hydro_delay
+ delay_dct[t] = out_filename, f
+
+ return delay_dct
+
+
+## ------------------------------------------------------ downloadGNSSDelays.py
+def downloadGNSS():
+ """Parse command line arguments using argparse."""
+ from RAiDER.gnss.downloadGNSSDelays import main as dlGNSS
+ p = argparse.ArgumentParser(
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ description=""" \
+ Check for and download tropospheric zenith delays for a set of GNSS stations from UNR
+
+ Example call to virtually access and append zenith delay information to a CSV table in specified output
+ directory, across specified range of time (in YYMMDD YYMMDD) and all available times of day, and confined to specified
+ geographic bounding box :
+ downloadGNSSdelay.py --out products -y 20100101 20141231 -b '39 40 -79 -78'
+
+ Example call to virtually access and append zenith delay information to a CSV table in specified output
+ directory, across specified range of time (in YYMMDD YYMMDD) and specified time of day, and distributed globally :
+ downloadGNSSdelay.py --out products -y 20100101 20141231 --returntime '00:00:00'
+
+
+ Example call to virtually access and append zenith delay information to a CSV table in specified output
+ directory, across specified range of time in 12 day steps (in YYMMDD YYMMDD days) and specified time of day, and distributed globally :
+ downloadGNSSdelay.py --out products -y 20100101 20141231 12 --returntime '00:00:00'
+
+ Example call to virtually access and append zenith delay information to a CSV table in specified output
+ directory, across specified range of time (in YYMMDD YYMMDD) and specified time of day, and distributed globally but restricted
+ to list of stations specified in input textfile :
+ downloadGNSSdelay.py --out products -y 20100101 20141231 --returntime '00:00:00' -f station_list.txt
+
+ NOTE, following example call to physically download zenith delay information not recommended as it is not
+ necessary for most applications.
+ Example call to physically download and append zenith delay information to a CSV table in specified output
+ directory, across specified range of time (in YYMMDD YYMMDD) and specified time of day, and confined to specified
+ geographic bounding box :
+ downloadGNSSdelay.py --download --out products -y 20100101 20141231 --returntime '00:00:00' -b '39 40 -79 -78'
+ """)
+
+ # Stations to check/download
+ area = p.add_argument_group(
+ 'Stations to check/download. Can be a lat/lon bounding box or file, or will run the whole world if not specified')
+ area.add_argument(
+ '--station_file', '-f', default=None, dest='station_file',
+ help=('Text file containing a list of 4-char station IDs separated by newlines'))
+ area.add_argument(
+ '-b', '--bounding_box', dest='bounding_box', type=str, default=None,
+ help="Provide either valid shapefile or Lat/Lon Bounding SNWE. -- Example : '19 20 -99.5 -98.5'")
+ area.add_argument(
+ '--gpsrepo', '-gr', default='UNR', dest='gps_repo',
+ help=('Specify GPS repository you wish to query. Currently supported archives: UNR.'))
+
+ misc = p.add_argument_group("Run parameters")
+ add_out(misc)
+
+ misc.add_argument(
+ '--date', dest='dateList',
+ help=dedent("""\
+ Date to calculate delay.
+ Can be a single date, a list of two dates (earlier, later) with 1-day interval, or a list of two dates and interval in days (earlier, later, interval).
+ Example accepted formats:
+ YYYYMMDD or
+ YYYYMMDD YYYYMMDD
+ YYYYMMDD YYYYMMDD N
+ """),
+ nargs="+",
+ action=DateListAction,
+ type=date_type,
+ required=True
+ )
+
+ misc.add_argument(
+ '--returntime', dest='returnTime',
+ help="Return delays closest to this specified time. If not specified, the GPS delays for all times will be returned. Input in 'HH:MM:SS', e.g. '16:00:00'",
+ default=None)
+
+ misc.add_argument(
+ '--download',
+ help='Physically download data. Note this option is not necessary to proceed with statistical analyses, as data can be handled virtually in the program.',
+ action='store_true', dest='download', default=False)
+
+ add_cpus(misc)
+ add_verbose(misc)
+
+ args = p.parse_args()
+
+ dlGNSS(args)
+ return
+
+
+## ------------------------------------------------------------ prepFromGUNW.py
+def calcDelaysGUNW(iargs=None):
+ from RAiDER.aria.prepFromGUNW import main as GUNW_prep
+ from RAiDER.aria.calcGUNW import main as GUNW_calc
+
+ p = argparse.ArgumentParser(
+ description='Calculate a cube of interferometic delays for GUNW files')
+
+ p.add_argument(
+ 'file', type=str,
+ help='1 ARIA GUNW netcdf file'
+ )
+
+ p.add_argument(
+ '-m', '--model', default='HRRR', type=str,
+ help='Weather model (Default=HRRR).'
+ )
+
+
+ p.add_argument(
+ '-o', '--output_directory', default=os.getcwd(), type=str,
+ help='Directory to store results (Default=./).'
+ )
+
+ p.add_argument(
+ '-w', '--write', default=True,
+ help='Optionally write the delays into the given GUNW product (Default=True).'
+ )
+
+
+ args = p.parse_args(args=iargs)
+ args.argv = iargs if iargs else os.sys.argv[1:]
+ # args.files = glob.glob(args.files) # eventually support multiple files
+
+ ## below are placeholders and not yet implemented
+ ## prep the config needed for delay calcs
+ # path_cfg, wavelength = GUNW_prep(args)
+
+ ## write the delays to disk using config and return dictionary of:
+ # date: wet/hydro filename
+ # dct_delays = calcDelays([path_cfg])
+
+ ## calculate the interferometric phase and write it out
+ # GUNW_calc(tropoDelayFile, args.file, wavelength, args.output_directory, args.write)
+
+ return
+
diff --git a/tools/RAiDER/cli/raider.yaml b/tools/RAiDER/cli/raider.yaml
index 1ebe8ee24..8e135917e 100644
--- a/tools/RAiDER/cli/raider.yaml
+++ b/tools/RAiDER/cli/raider.yaml
@@ -121,7 +121,7 @@ los_group:
##
runtime_group:
verbose: True
- raster_format: GTiff # Can be any rasterio-compatible format
+ file_format: GTiff # Can be any rasterio-compatible format
output_directory: . # uses the runtime directory by default
weather_model_directory: # Defaults to