nisar-pytools 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nisar_pytools-0.1.0/.github/workflows/ci.yml +35 -0
- nisar_pytools-0.1.0/.github/workflows/publish.yml +28 -0
- nisar_pytools-0.1.0/.gitignore +34 -0
- nisar_pytools-0.1.0/.pre-commit-config.yaml +11 -0
- nisar_pytools-0.1.0/CHANGELOG.md +41 -0
- nisar_pytools-0.1.0/CLAUDE.md +52 -0
- nisar_pytools-0.1.0/LICENSE.txt +20 -0
- nisar_pytools-0.1.0/MANIFEST.in +4 -0
- nisar_pytools-0.1.0/PKG-INFO +207 -0
- nisar_pytools-0.1.0/README.md +154 -0
- nisar_pytools-0.1.0/environment.yml +31 -0
- nisar_pytools-0.1.0/notebooks/.gitkeep +0 -0
- nisar_pytools-0.1.0/notebooks/example.ipynb +108 -0
- nisar_pytools-0.1.0/pyproject.toml +84 -0
- nisar_pytools-0.1.0/setup.cfg +4 -0
- nisar_pytools-0.1.0/src/nisar_pytools/__init__.py +3 -0
- nisar_pytools-0.1.0/src/nisar_pytools/io/__init__.py +15 -0
- nisar_pytools-0.1.0/src/nisar_pytools/io/_download.py +176 -0
- nisar_pytools-0.1.0/src/nisar_pytools/io/_export.py +153 -0
- nisar_pytools-0.1.0/src/nisar_pytools/io/_h5_to_datatree.py +303 -0
- nisar_pytools-0.1.0/src/nisar_pytools/io/_reader.py +64 -0
- nisar_pytools-0.1.0/src/nisar_pytools/io/_search.py +115 -0
- nisar_pytools-0.1.0/src/nisar_pytools/io/_stack.py +245 -0
- nisar_pytools-0.1.0/src/nisar_pytools/processing/__init__.py +41 -0
- nisar_pytools-0.1.0/src/nisar_pytools/processing/phase_linking.py +233 -0
- nisar_pytools-0.1.0/src/nisar_pytools/processing/polsar.py +376 -0
- nisar_pytools-0.1.0/src/nisar_pytools/processing/sar.py +288 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/__init__.py +0 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/_search_validation.py +219 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/_validation.py +96 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/conversion.py +55 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/dem.py +173 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/local_incidence_angle.py +170 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/masking.py +60 -0
- nisar_pytools-0.1.0/src/nisar_pytools/utils/metadata.py +57 -0
- nisar_pytools-0.1.0/src/nisar_pytools/viz/__init__.py +13 -0
- nisar_pytools-0.1.0/src/nisar_pytools/viz/plotting.py +212 -0
- nisar_pytools-0.1.0/src/nisar_pytools.egg-info/PKG-INFO +207 -0
- nisar_pytools-0.1.0/src/nisar_pytools.egg-info/SOURCES.txt +59 -0
- nisar_pytools-0.1.0/src/nisar_pytools.egg-info/dependency_links.txt +1 -0
- nisar_pytools-0.1.0/src/nisar_pytools.egg-info/requires.txt +33 -0
- nisar_pytools-0.1.0/src/nisar_pytools.egg-info/top_level.txt +1 -0
- nisar_pytools-0.1.0/tests/__init__.py +0 -0
- nisar_pytools-0.1.0/tests/conftest.py +143 -0
- nisar_pytools-0.1.0/tests/test_conversion.py +57 -0
- nisar_pytools-0.1.0/tests/test_dem.py +73 -0
- nisar_pytools-0.1.0/tests/test_download.py +161 -0
- nisar_pytools-0.1.0/tests/test_export.py +85 -0
- nisar_pytools-0.1.0/tests/test_h5_to_datatree.py +188 -0
- nisar_pytools-0.1.0/tests/test_local_incidence_angle.py +140 -0
- nisar_pytools-0.1.0/tests/test_masking.py +49 -0
- nisar_pytools-0.1.0/tests/test_metadata.py +46 -0
- nisar_pytools-0.1.0/tests/test_phase_linking.py +172 -0
- nisar_pytools-0.1.0/tests/test_polsar.py +203 -0
- nisar_pytools-0.1.0/tests/test_reader.py +98 -0
- nisar_pytools-0.1.0/tests/test_sar.py +399 -0
- nisar_pytools-0.1.0/tests/test_search.py +90 -0
- nisar_pytools-0.1.0/tests/test_search_validation.py +158 -0
- nisar_pytools-0.1.0/tests/test_stack.py +185 -0
- nisar_pytools-0.1.0/tests/test_validation.py +100 -0
- nisar_pytools-0.1.0/tests/test_viz.py +103 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
defaults:
|
|
13
|
+
run:
|
|
14
|
+
shell: bash -l {0}
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: conda-incubator/setup-miniconda@v3
|
|
20
|
+
with:
|
|
21
|
+
environment-file: environment.yml
|
|
22
|
+
activate-environment: nisar_pytools
|
|
23
|
+
miniforge-version: latest
|
|
24
|
+
|
|
25
|
+
- name: Lint
|
|
26
|
+
run: ruff check src/ tests/
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: pytest tests/ -v -m "not integration"
|
|
30
|
+
|
|
31
|
+
- name: Build package
|
|
32
|
+
run: python -m build
|
|
33
|
+
|
|
34
|
+
- name: Check package
|
|
35
|
+
run: twine check dist/*
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # Required for trusted publishing
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install build tools
|
|
22
|
+
run: pip install build
|
|
23
|
+
|
|
24
|
+
- name: Build package
|
|
25
|
+
run: python -m build
|
|
26
|
+
|
|
27
|
+
- name: Publish to PyPI
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Local data
|
|
2
|
+
local/
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*.egg-info/
|
|
8
|
+
*.egg
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.whl
|
|
12
|
+
|
|
13
|
+
# Environments
|
|
14
|
+
.env
|
|
15
|
+
.venv/
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.vscode/
|
|
19
|
+
.idea/
|
|
20
|
+
|
|
21
|
+
# OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# Jupyter
|
|
26
|
+
.ipynb_checkpoints/
|
|
27
|
+
|
|
28
|
+
# Testing
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.coverage
|
|
31
|
+
htmlcov/
|
|
32
|
+
|
|
33
|
+
# Ruff
|
|
34
|
+
.ruff_cache/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.1.0
|
|
4
|
+
|
|
5
|
+
### IO
|
|
6
|
+
- `open_nisar()` — read NISAR HDF5 files into lazy xarray DataTree with CRS
|
|
7
|
+
- `stack_gslcs()` — stack multiple same-track GSLCs into a `(time, y, x)` DataArray
|
|
8
|
+
- `find_nisar()` — search ASF for NISAR product URLs by AOI, dates, track, direction
|
|
9
|
+
- `download_urls()` — parallel download with retry and post-download HDF5 validation
|
|
10
|
+
- `to_zarr()` / `to_netcdf()` / `read_netcdf()` — export and import with complex data support
|
|
11
|
+
- Generic h5py-to-DataTree walker with dask-backed lazy arrays
|
|
12
|
+
- Automatic coordinate assignment and CRS from `projection` dataset
|
|
13
|
+
- HDF5 dimension scale resolution via `DIMENSION_LIST` attributes
|
|
14
|
+
|
|
15
|
+
### Processing
|
|
16
|
+
- `interferogram()` — complex interferogram with grid matching validation
|
|
17
|
+
- `coherence()` — sliding-window coherence estimation
|
|
18
|
+
- `multilook()` / `multilook_interferogram()` — spatial averaging and downsampling
|
|
19
|
+
- `unwrap()` — phase unwrapping via SNAPHU
|
|
20
|
+
- `calculate_phase()` — extract phase from complex data
|
|
21
|
+
- `phase_link()` — EMI phase linking with SHP selection on SLC stacks
|
|
22
|
+
- `h_a_alpha()` — Cloude-Pottier polarimetric decomposition (entropy, anisotropy, alpha)
|
|
23
|
+
- Individual `entropy()`, `anisotropy()`, `alpha()`, `mean_alpha()` functions
|
|
24
|
+
|
|
25
|
+
### Utilities
|
|
26
|
+
- `fetch_dem()` — auto-download Copernicus GLO-30 DEM matching a NISAR file's extent
|
|
27
|
+
- `local_incidence_angle()` — compute LIA from LOS vectors and a DEM
|
|
28
|
+
- `get_acquisition_time()`, `get_orbit_info()`, `get_bounding_polygon()` — metadata extraction
|
|
29
|
+
- `apply_mask()` / `get_mask()` — apply NISAR mask datasets
|
|
30
|
+
- `to_db()` / `from_db()` — dB conversion
|
|
31
|
+
- Date, AOI, URL, and path validation utilities
|
|
32
|
+
|
|
33
|
+
### Visualization
|
|
34
|
+
- `plot_amplitude()`, `plot_phase()`, `plot_interferogram()`, `plot_coherence()`
|
|
35
|
+
|
|
36
|
+
### Infrastructure
|
|
37
|
+
- PyPI-ready packaging with `pyproject.toml`
|
|
38
|
+
- CI/CD via GitHub Actions (lint, test, build, publish)
|
|
39
|
+
- Pre-commit hooks (nbstripout, ruff)
|
|
40
|
+
- Conda environment file
|
|
41
|
+
- 243 tests with synthetic HDF5 fixtures
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project
|
|
6
|
+
|
|
7
|
+
Python tools for reading NISAR (NASA-ISRO SAR) HDF5 data products into lazy xarray DataTree objects. Currently supports GSLC and GUNW product types.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Use the nisar_pytools conda env for all commands
|
|
13
|
+
# Install: mamba env create -f environment.yml
|
|
14
|
+
# Activate or prefix with: /Users/zmhoppinen/miniforge3/envs/nisar_pytools/bin/python
|
|
15
|
+
|
|
16
|
+
# Run all unit tests
|
|
17
|
+
/Users/zmhoppinen/miniforge3/envs/nisar_pytools/bin/python -m pytest tests/ -v -m "not integration"
|
|
18
|
+
|
|
19
|
+
# Run a single test file or test
|
|
20
|
+
/Users/zmhoppinen/miniforge3/envs/nisar_pytools/bin/python -m pytest tests/test_reader.py -v
|
|
21
|
+
/Users/zmhoppinen/miniforge3/envs/nisar_pytools/bin/python -m pytest tests/test_reader.py::TestOpenNisar::test_lazy_by_default -v
|
|
22
|
+
|
|
23
|
+
# Run integration tests (requires real NISAR files in local/)
|
|
24
|
+
NISAR_TEST_GSLC=local/gslcs/<file>.h5 NISAR_TEST_GUNW=local/gunws/<file>.h5 \
|
|
25
|
+
/Users/zmhoppinen/miniforge3/envs/nisar_pytools/bin/python -m pytest tests/ -v
|
|
26
|
+
|
|
27
|
+
# Lint
|
|
28
|
+
/Users/zmhoppinen/miniforge3/envs/nisar_pytools/bin/ruff check src/ tests/
|
|
29
|
+
|
|
30
|
+
# Install dependencies with mamba, not pip or conda
|
|
31
|
+
mamba install -n nisar_pytools -c conda-forge <package>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Architecture
|
|
35
|
+
|
|
36
|
+
**Entry point**: `from nisar_pytools import open_nisar` — takes an HDF5 file path, validates it, detects product type, returns `xr.DataTree` with dask-backed lazy arrays.
|
|
37
|
+
|
|
38
|
+
**Flow**: `open_nisar()` → `validate_nisar_hdf5()` → `detect_product_type()` → `h5_to_datatree()`
|
|
39
|
+
|
|
40
|
+
**Key design decisions**:
|
|
41
|
+
- Uses custom h5py walker (not `xr.open_datatree` with h5netcdf) because NISAR files have named types at root and 100+ scalar datasets that belong in attrs, not as DataArrays.
|
|
42
|
+
- All 2D+ arrays are dask-backed via `dask.array.from_array()`. Coordinates (1D `xCoordinates`/`yCoordinates`) are loaded eagerly since they're small.
|
|
43
|
+
- Scalar datasets become `Dataset.attrs`. 1D non-coordinate arrays (e.g., `listOfPolarizations`) also go to attrs.
|
|
44
|
+
- Unnamed dimensions are prefixed with the variable name (e.g., `eulerAngles_dim_1`) to avoid conflicts when sibling datasets have different shapes.
|
|
45
|
+
- The `h5py.File` handle is kept alive on `tree.__dict__["_h5file"]` to prevent GC while dask arrays reference it.
|
|
46
|
+
|
|
47
|
+
**`src/nisar_pytools/` layout**:
|
|
48
|
+
- `io/_reader.py` — `open_nisar()` entry point
|
|
49
|
+
- `io/_h5_to_datatree.py` — generic HDF5 group walker that builds DataTree
|
|
50
|
+
- `utils/_validation.py` — file validation and product type detection
|
|
51
|
+
|
|
52
|
+
**Tests** use synthetic HDF5 fixtures (tiny 8x10 arrays) defined in `tests/conftest.py`. Integration tests against real files are gated behind `@pytest.mark.integration` and env vars.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zachary Hoppinen
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nisar-pytools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Open source Python tools for working with NISAR datasets
|
|
5
|
+
Author-email: Zachary Hoppinen <zachary.hoppinen@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/zmhoppinen/nisar_pytools
|
|
8
|
+
Project-URL: Repository, https://github.com/zmhoppinen/nisar_pytools
|
|
9
|
+
Project-URL: Issues, https://github.com/zmhoppinen/nisar_pytools/issues
|
|
10
|
+
Keywords: nisar,sar,insar,radar,remote-sensing,hdf5,xarray
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE.txt
|
|
24
|
+
Requires-Dist: asf_search>=7.0
|
|
25
|
+
Requires-Dist: dask[array]>=2024.0
|
|
26
|
+
Requires-Dist: h5py>=3.10
|
|
27
|
+
Requires-Dist: numpy>=1.24
|
|
28
|
+
Requires-Dist: pyproj>=3.4
|
|
29
|
+
Requires-Dist: rasterio>=1.3
|
|
30
|
+
Requires-Dist: requests
|
|
31
|
+
Requires-Dist: rioxarray>=0.15
|
|
32
|
+
Requires-Dist: scipy>=1.10
|
|
33
|
+
Requires-Dist: shapely>=2.0
|
|
34
|
+
Requires-Dist: snaphu>=0.3
|
|
35
|
+
Requires-Dist: xarray>=2024.10
|
|
36
|
+
Provides-Extra: dem
|
|
37
|
+
Requires-Dist: dem_stitcher; extra == "dem"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: build; extra == "dev"
|
|
40
|
+
Requires-Dist: nbstripout; extra == "dev"
|
|
41
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest; extra == "dev"
|
|
43
|
+
Requires-Dist: ruff; extra == "dev"
|
|
44
|
+
Requires-Dist: twine; extra == "dev"
|
|
45
|
+
Provides-Extra: notebooks
|
|
46
|
+
Requires-Dist: jupyter; extra == "notebooks"
|
|
47
|
+
Requires-Dist: matplotlib; extra == "notebooks"
|
|
48
|
+
Provides-Extra: viz
|
|
49
|
+
Requires-Dist: matplotlib; extra == "viz"
|
|
50
|
+
Provides-Extra: all
|
|
51
|
+
Requires-Dist: nisar-pytools[dem,notebooks,viz]; extra == "all"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# nisar_pytools
|
|
55
|
+
|
|
56
|
+
Open source Python tools for working with NISAR datasets.
|
|
57
|
+
|
|
58
|
+
## About
|
|
59
|
+
|
|
60
|
+
`nisar_pytools` provides utilities for searching, downloading, reading, and processing data products from NASA's [NISAR](https://nisar.jpl.nasa.gov/) (NASA-ISRO Synthetic Aperture Radar) mission.
|
|
61
|
+
|
|
62
|
+
### Supported Products
|
|
63
|
+
|
|
64
|
+
- **GSLC** - Geocoded Single Look Complex
|
|
65
|
+
- **GUNW** - Geocoded Unwrapped Interferogram
|
|
66
|
+
|
|
67
|
+
Additional NISAR product types will be added over time.
|
|
68
|
+
|
|
69
|
+
## Getting Started
|
|
70
|
+
|
|
71
|
+
### Prerequisites
|
|
72
|
+
|
|
73
|
+
- Python 3.10+
|
|
74
|
+
- [Miniforge](https://github.com/conda-forge/miniforge) (recommended)
|
|
75
|
+
- NASA Earthdata login (for downloading from ASF)
|
|
76
|
+
|
|
77
|
+
### Installation
|
|
78
|
+
|
|
79
|
+
1. Clone the repository
|
|
80
|
+
```sh
|
|
81
|
+
git clone https://github.com/zmhoppinen/nisar_pytools.git
|
|
82
|
+
cd nisar_pytools
|
|
83
|
+
```
|
|
84
|
+
2. Create the conda environment
|
|
85
|
+
```sh
|
|
86
|
+
mamba env create -f environment.yml
|
|
87
|
+
conda activate nisar_pytools
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Usage
|
|
91
|
+
|
|
92
|
+
### Search and Download
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from nisar_pytools import find_nisar, download_urls
|
|
96
|
+
|
|
97
|
+
# Search ASF for GSLC products over an area of interest
|
|
98
|
+
urls = find_nisar(
|
|
99
|
+
aoi=[-115, 43, -114, 44],
|
|
100
|
+
start_date="2025-06-01",
|
|
101
|
+
end_date="2025-12-01",
|
|
102
|
+
product_type="GSLC",
|
|
103
|
+
path_number=77,
|
|
104
|
+
direction="ASCENDING",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Download in parallel with automatic validation
|
|
108
|
+
fps = download_urls(urls, "local/gslcs/")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Read a Single File
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from nisar_pytools import open_nisar
|
|
115
|
+
|
|
116
|
+
dt = open_nisar("NISAR_L2_PR_GSLC_...h5")
|
|
117
|
+
|
|
118
|
+
# Access a frequency/polarization group
|
|
119
|
+
freq_a = dt["science/LSAR/GSLC/grids/frequencyA"].dataset
|
|
120
|
+
hh = freq_a["HH"] # lazy dask-backed DataArray with CRS set
|
|
121
|
+
|
|
122
|
+
# Coordinates and CRS are assigned automatically
|
|
123
|
+
print(hh.rio.crs) # e.g. EPSG:32611
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Stack GSLCs into a Time Series
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from nisar_pytools import stack_gslcs
|
|
130
|
+
|
|
131
|
+
# Stack multiple same-track GSLCs into a (time, y, x) DataArray
|
|
132
|
+
stack = stack_gslcs(
|
|
133
|
+
["gslc_date1.h5", "gslc_date2.h5", "gslc_date3.h5"],
|
|
134
|
+
frequency="frequencyA",
|
|
135
|
+
polarization="HH",
|
|
136
|
+
)
|
|
137
|
+
# Sorted by time, grid-validated, dask-backed, CRS assigned
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### SAR Processing
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from nisar_pytools.processing import (
|
|
144
|
+
interferogram, coherence, multilook, unwrap, calculate_phase
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Interferogram (validates matching grids)
|
|
148
|
+
ifg = interferogram(slc1, slc2)
|
|
149
|
+
|
|
150
|
+
# Multilooked interferogram
|
|
151
|
+
ml_ifg = multilook(ifg, looks_y=4, looks_x=4)
|
|
152
|
+
|
|
153
|
+
# Coherence estimation
|
|
154
|
+
coh = coherence(slc1, slc2, window_size=11)
|
|
155
|
+
|
|
156
|
+
# Phase unwrapping with SNAPHU
|
|
157
|
+
unw, conncomp = unwrap(ifg, coh, nlooks=20.0)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Phase Linking
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from nisar_pytools.processing import phase_link
|
|
164
|
+
|
|
165
|
+
# EMI phase linking on a GSLC stack
|
|
166
|
+
linked, temporal_coh = phase_link(stack, window_size=11, confidence=0.95)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Polarimetric Decomposition
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from nisar_pytools.processing import h_a_alpha
|
|
173
|
+
|
|
174
|
+
# H-A-alpha decomposition from quad-pol SLC channels
|
|
175
|
+
ds = h_a_alpha(hh, hv, vv)
|
|
176
|
+
# Returns Dataset with: entropy, anisotropy, alpha, mean_alpha
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Local Incidence Angle
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from nisar_pytools.utils.local_incidence_angle import local_incidence_angle
|
|
183
|
+
|
|
184
|
+
lia = local_incidence_angle(dem, los_x, los_y, los_z, heights, x_rg, y_rg, epsg=32611)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Roadmap
|
|
188
|
+
|
|
189
|
+
- [x] Lazy HDF5 reader returning xarray DataTree with CRS
|
|
190
|
+
- [x] GSLC and GUNW support
|
|
191
|
+
- [x] ASF search and parallel download with validation
|
|
192
|
+
- [x] GSLC time-series stacking
|
|
193
|
+
- [x] Interferogram, coherence, multilooking, phase extraction
|
|
194
|
+
- [x] Phase unwrapping (SNAPHU)
|
|
195
|
+
- [x] Phase linking (EMI with SHP selection)
|
|
196
|
+
- [x] Polarimetric decomposition (H-A-alpha)
|
|
197
|
+
- [x] Local incidence angle computation
|
|
198
|
+
- [x] Visualization helpers (amplitude, phase, interferogram, coherence)
|
|
199
|
+
- [ ] Support for additional NISAR product types
|
|
200
|
+
|
|
201
|
+
## Contributing
|
|
202
|
+
|
|
203
|
+
Contributions are welcome! Please fork the repo and open a pull request, or open an issue to suggest improvements.
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
Distributed under the MIT License. See `LICENSE.txt` for more information.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# nisar_pytools
|
|
2
|
+
|
|
3
|
+
Open source Python tools for working with NISAR datasets.
|
|
4
|
+
|
|
5
|
+
## About
|
|
6
|
+
|
|
7
|
+
`nisar_pytools` provides utilities for searching, downloading, reading, and processing data products from NASA's [NISAR](https://nisar.jpl.nasa.gov/) (NASA-ISRO Synthetic Aperture Radar) mission.
|
|
8
|
+
|
|
9
|
+
### Supported Products
|
|
10
|
+
|
|
11
|
+
- **GSLC** - Geocoded Single Look Complex
|
|
12
|
+
- **GUNW** - Geocoded Unwrapped Interferogram
|
|
13
|
+
|
|
14
|
+
Additional NISAR product types will be added over time.
|
|
15
|
+
|
|
16
|
+
## Getting Started
|
|
17
|
+
|
|
18
|
+
### Prerequisites
|
|
19
|
+
|
|
20
|
+
- Python 3.10+
|
|
21
|
+
- [Miniforge](https://github.com/conda-forge/miniforge) (recommended)
|
|
22
|
+
- NASA Earthdata login (for downloading from ASF)
|
|
23
|
+
|
|
24
|
+
### Installation
|
|
25
|
+
|
|
26
|
+
1. Clone the repository
|
|
27
|
+
```sh
|
|
28
|
+
git clone https://github.com/zmhoppinen/nisar_pytools.git
|
|
29
|
+
cd nisar_pytools
|
|
30
|
+
```
|
|
31
|
+
2. Create the conda environment
|
|
32
|
+
```sh
|
|
33
|
+
mamba env create -f environment.yml
|
|
34
|
+
conda activate nisar_pytools
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Search and Download
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from nisar_pytools import find_nisar, download_urls
|
|
43
|
+
|
|
44
|
+
# Search ASF for GSLC products over an area of interest
|
|
45
|
+
urls = find_nisar(
|
|
46
|
+
aoi=[-115, 43, -114, 44],
|
|
47
|
+
start_date="2025-06-01",
|
|
48
|
+
end_date="2025-12-01",
|
|
49
|
+
product_type="GSLC",
|
|
50
|
+
path_number=77,
|
|
51
|
+
direction="ASCENDING",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Download in parallel with automatic validation
|
|
55
|
+
fps = download_urls(urls, "local/gslcs/")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Read a Single File
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from nisar_pytools import open_nisar
|
|
62
|
+
|
|
63
|
+
dt = open_nisar("NISAR_L2_PR_GSLC_...h5")
|
|
64
|
+
|
|
65
|
+
# Access a frequency/polarization group
|
|
66
|
+
freq_a = dt["science/LSAR/GSLC/grids/frequencyA"].dataset
|
|
67
|
+
hh = freq_a["HH"] # lazy dask-backed DataArray with CRS set
|
|
68
|
+
|
|
69
|
+
# Coordinates and CRS are assigned automatically
|
|
70
|
+
print(hh.rio.crs) # e.g. EPSG:32611
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Stack GSLCs into a Time Series
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from nisar_pytools import stack_gslcs
|
|
77
|
+
|
|
78
|
+
# Stack multiple same-track GSLCs into a (time, y, x) DataArray
|
|
79
|
+
stack = stack_gslcs(
|
|
80
|
+
["gslc_date1.h5", "gslc_date2.h5", "gslc_date3.h5"],
|
|
81
|
+
frequency="frequencyA",
|
|
82
|
+
polarization="HH",
|
|
83
|
+
)
|
|
84
|
+
# Sorted by time, grid-validated, dask-backed, CRS assigned
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### SAR Processing
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from nisar_pytools.processing import (
|
|
91
|
+
interferogram, coherence, multilook, unwrap, calculate_phase
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Interferogram (validates matching grids)
|
|
95
|
+
ifg = interferogram(slc1, slc2)
|
|
96
|
+
|
|
97
|
+
# Multilooked interferogram
|
|
98
|
+
ml_ifg = multilook(ifg, looks_y=4, looks_x=4)
|
|
99
|
+
|
|
100
|
+
# Coherence estimation
|
|
101
|
+
coh = coherence(slc1, slc2, window_size=11)
|
|
102
|
+
|
|
103
|
+
# Phase unwrapping with SNAPHU
|
|
104
|
+
unw, conncomp = unwrap(ifg, coh, nlooks=20.0)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Phase Linking
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from nisar_pytools.processing import phase_link
|
|
111
|
+
|
|
112
|
+
# EMI phase linking on a GSLC stack
|
|
113
|
+
linked, temporal_coh = phase_link(stack, window_size=11, confidence=0.95)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Polarimetric Decomposition
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from nisar_pytools.processing import h_a_alpha
|
|
120
|
+
|
|
121
|
+
# H-A-alpha decomposition from quad-pol SLC channels
|
|
122
|
+
ds = h_a_alpha(hh, hv, vv)
|
|
123
|
+
# Returns Dataset with: entropy, anisotropy, alpha, mean_alpha
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Local Incidence Angle
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from nisar_pytools.utils.local_incidence_angle import local_incidence_angle
|
|
130
|
+
|
|
131
|
+
lia = local_incidence_angle(dem, los_x, los_y, los_z, heights, x_rg, y_rg, epsg=32611)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Roadmap
|
|
135
|
+
|
|
136
|
+
- [x] Lazy HDF5 reader returning xarray DataTree with CRS
|
|
137
|
+
- [x] GSLC and GUNW support
|
|
138
|
+
- [x] ASF search and parallel download with validation
|
|
139
|
+
- [x] GSLC time-series stacking
|
|
140
|
+
- [x] Interferogram, coherence, multilooking, phase extraction
|
|
141
|
+
- [x] Phase unwrapping (SNAPHU)
|
|
142
|
+
- [x] Phase linking (EMI with SHP selection)
|
|
143
|
+
- [x] Polarimetric decomposition (H-A-alpha)
|
|
144
|
+
- [x] Local incidence angle computation
|
|
145
|
+
- [x] Visualization helpers (amplitude, phase, interferogram, coherence)
|
|
146
|
+
- [ ] Support for additional NISAR product types
|
|
147
|
+
|
|
148
|
+
## Contributing
|
|
149
|
+
|
|
150
|
+
Contributions are welcome! Please fork the repo and open a pull request, or open an issue to suggest improvements.
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
Distributed under the MIT License. See `LICENSE.txt` for more information.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: nisar_pytools
|
|
2
|
+
channels:
|
|
3
|
+
- conda-forge
|
|
4
|
+
- defaults
|
|
5
|
+
dependencies:
|
|
6
|
+
- python>=3.10
|
|
7
|
+
- asf_search>=7.0
|
|
8
|
+
- dask>=2024.0
|
|
9
|
+
- h5py>=3.10
|
|
10
|
+
- numpy>=1.24
|
|
11
|
+
- rasterio>=1.3
|
|
12
|
+
- pyproj>=3.4
|
|
13
|
+
- requests
|
|
14
|
+
- rioxarray>=0.15
|
|
15
|
+
- shapely>=2.0
|
|
16
|
+
- scipy>=1.10
|
|
17
|
+
- snaphu>=0.3
|
|
18
|
+
- xarray>=2024.10
|
|
19
|
+
- zarr
|
|
20
|
+
- h5netcdf
|
|
21
|
+
- build
|
|
22
|
+
- nbstripout
|
|
23
|
+
- pre-commit
|
|
24
|
+
- pytest
|
|
25
|
+
- ruff
|
|
26
|
+
- twine
|
|
27
|
+
- jupyter
|
|
28
|
+
- matplotlib
|
|
29
|
+
- pip
|
|
30
|
+
- pip:
|
|
31
|
+
- -e .
|
|
File without changes
|