Skip to main content

Open source Python tools for working with NISAR datasets

Project description

nisar_pytools

Open source Python tools for working with NISAR datasets.

About

nisar_pytools provides utilities for searching, downloading, reading, and processing data products from NASA's NISAR (NASA-ISRO Synthetic Aperture Radar) mission.

Supported Products

  • GSLC - Geocoded Single Look Complex
  • GUNW - Geocoded Unwrapped Interferogram

Additional NISAR product types will be added over time.

Getting Started

Prerequisites

  • Python 3.10+
  • Miniforge (recommended)
  • NASA Earthdata login (for downloading from ASF)

Installation

From PyPI:

pip install nisar-pytools

With optional extras:

pip install nisar-pytools[dem]       # DEM fetching (dem_stitcher)
pip install nisar-pytools[dolphin]   # dolphin InSAR time-series prep
pip install nisar-pytools[viz]       # Visualization (matplotlib)
pip install nisar-pytools[all]       # Everything

From source (for development):

git clone https://github.com/zmhoppinen/nisar_pytools.git
cd nisar_pytools
mamba env create -f environment.yml
conda activate nisar_pytools

Usage - Command Line

Geotiff export

Command line utility for quick export of commonly used bands from a NISAR HDF5 as GeoTIFFshandy for pulling into QGIS.

Installed with the package as the nisar_pytools console script; run nisar_pytools to-geotiff --help for the full band catalog and flag reference.

# Default-all: write every default band for the product, next to the .h5
# GUNW -> unwrapped_phase, wrapped_phase, coherence, ionosphere
# GSLC -> amplitude (10*log10(|SLC|^2) in dB)
nisar_pytools to-geotiff NISAR_L2_PR_GUNW_...h5

# One band, explicit polarization, custom output directory
nisar_pytools to-geotiff NISAR_L2_PR_GUNW_...h5 \
    --band unwrapped_phase --pol HH --output-dir /tmp/gunw_tifs

# Subset a multi-GB GSLC to a lat/lon AOI -- streamed, low memory
nisar_pytools to-geotiff NISAR_L2_PR_GSLC_...h5 \
    --bbox-wgs84 -118.5 41.0 -118.3 41.2

# Same crop in the file's native CRS (UTM meters here)
nisar_pytools to-geotiff NISAR_L2_PR_GSLC_...h5 \
    --bbox 380000 4540000 400000 4565000

# GSLC amplitude on frequency B
nisar_pytools to-geotiff NISAR_L2_PR_GSLC_...h5 --freq B

Outputs are tiled GeoTIFFs named <h5_stem>_<band>_<pol>.tif. Writes stream chunk-by-chunk via dask + rioxarray, so a full-resolution 41 GB GSLC processes with ~330 MB peak memory.

Usage - Python

Search and Download

from nisar_pytools import find_nisar, download_urls

# find_nisar() searches ASF for NISAR products. Parameters:
#   aoi          - area of interest: [xmin, ymin, xmax, ymax], shapely geometry,
#                  or dict like {"west": -115, "south": 43, "east": -114, "north": 44}
#   start_date   - start of temporal search window (ISO string or datetime)
#   end_date     - end of temporal search window

#   product_type - "GSLC", "GUNW", "RSLC", "GCOV", "RIFG", "RUNW", "ROFF", "GOFF"
#   path_number  - relative orbit / track number (optional)
#   frame        - frame number (optional)
#   direction    - "ASCENDING" or "DESCENDING" (optional)
#   include_qa   - if True, include QA files (default False)

# Broad search: find all GSLC data over an area (any track, any direction)
all_gslcs = find_nisar(
    aoi=[-115, 43, -114, 44],
    start_date="2025-06-01",
    end_date="2025-12-01",
    product_type="GSLC",
)

# Narrow search: specific track and direction for time-series analysis
track_77 = find_nisar(
    aoi=[-115, 43, -114, 44],
    start_date="2025-06-01",
    end_date="2025-12-01",
    product_type="GSLC",
    path_number=77,
    direction="ASCENDING",
)

# Search for GUNW interferograms instead of SLCs
gunws = find_nisar(
    aoi=[-115, 43, -114, 44],
    start_date="2025-11-01",
    end_date="2026-02-01",
    product_type="GUNW",
)

# Download in parallel with automatic HDF5 validation
fps = download_urls(track_77, "local/gslcs/")

Read a Single File

from nisar_pytools import open_nisar
from nisar_pytools.utils.metadata import get_slc, get_orbit_info, get_acquisition_time

dt = open_nisar("NISAR_L2_PR_GSLC_...h5")

# Quick access to a polarization channel
hh = get_slc(dt, polarization="HH")           # lazy, CRS set
hv = get_slc(dt, polarization="HV")
hh_b = get_slc(dt, polarization="HH", frequency="frequencyB")

# Metadata without navigating the tree
print(get_acquisition_time(dt))  # 2025-11-03 12:46:15
print(get_orbit_info(dt))        # {'track_number': 77, 'frame_number': 24, ...}
print(hh.rio.crs)                # EPSG:32611

# Or access the full DataTree directly
freq_a = dt["science/LSAR/GSLC/grids/frequencyA"].dataset

Stack GSLCs into a Time Series

from nisar_pytools import stack_gslcs

# Stack multiple same-track GSLCs into a (time, y, x) DataArray
stack = stack_gslcs(
    ["gslc_date1.h5", "gslc_date2.h5", "gslc_date3.h5"],
    frequency="frequencyA",
    polarization="HH",
)
# Sorted by time, grid-validated, dask-backed, CRS assigned

Basic SAR Processing

from nisar_pytools.processing import (
    interferogram, coherence, multilook, unwrap, calculate_phase
)

# Interferogram (validates matching grids)
ifg = interferogram(slc1, slc2)

# Multilooked interferogram
ml_ifg = multilook(ifg, looks_y=4, looks_x=4)

# Coherence estimation
coh = coherence(slc1, slc2, window_size=11)

# Phase unwrapping with SNAPHU
unw, conncomp = unwrap(ifg, coh, nlooks=20.0)

Prepare GSLCs for dolphin

dolphin is an open-source InSAR time-series tool (phase linking, unwrapping, network inversion) from the OPERA/ISCE framework. prep_dolphin crops your GSLCs to an AOI, exports them as complex GeoTIFFs, and generates a ready-to-run dolphin config YAML.

from pathlib import Path
from nisar_pytools.processing import prep_dolphin

gslc_files = sorted(Path("gslcs/").glob("NISAR_L2_PR_GSLC_*.h5"))

config = prep_dolphin(
    gslc_paths=gslc_files,
    out_dir="dolphin_run/",
    aoi_wgs84=(-110.54, 44.65, -109.63, 45.18),    # Greater Yellowstone
    skip_dates={"20251104"},                          # drop early-season date
    dolphin_overrides=[
        (["phase_linking", "half_window", "y"], 20),
        (["phase_linking", "half_window", "x"], 20),
        (["output_options", "strides", "y"], 5),
        (["output_options", "strides", "x"], 5),
        (["unwrap_options", "unwrap_method"], "spurt"),
    ],
)
# Logs: "Ready. Review the config, then run:
#          dolphin run dolphin_run/dolphin_config.yaml"

You can also use crop_gslc_to_tif standalone to extract a single GSLC:

from nisar_pytools.processing import crop_gslc_to_tif

crop_gslc_to_tif("NISAR_L2_PR_GSLC_...h5", "out.tif",
                 bbox_utm=(540000, 4950000, 560000, 4970000))

Phase Linking

from nisar_pytools.processing import phase_link

# EMI phase linking on a GSLC stack
linked, temporal_coh = phase_link(stack, search_window=11, confidence=0.95)

Polarimetric Decomposition

Requires a quad-pol acquisition (HH + HV + VV). NISAR acquires quad-pol over specific regions — check listOfPolarizations in the frequency group to confirm your data has all three channels. Dual-pol (HH + HV only) data cannot be used for H-A-alpha decomposition.

from nisar_pytools import open_nisar
from nisar_pytools.utils.metadata import get_slc
from nisar_pytools.processing import h_a_alpha

dt = open_nisar("NISAR_L2_PR_GSLC_...h5")

# Extract the three quad-pol channels (must call .compute() for in-memory)
hh = get_slc(dt, "HH").compute()
hv = get_slc(dt, "HV").compute()
vv = get_slc(dt, "VV").compute()  # only available in quad-pol mode

# H-A-alpha decomposition (Cloude-Pottier)
ds = h_a_alpha(hh, hv, vv)
# Returns Dataset with: entropy, anisotropy, alpha, mean_alpha

Local Incidence Angle

import numpy as np
from nisar_pytools import open_nisar
from nisar_pytools.utils.local_incidence_angle import local_incidence_angle

# Open a GSLC or GUNW — both have radarGrid with LOS vectors
dt = open_nisar("NISAR_L2_PR_GSLC_...h5")

# Extract LOS vectors from the radarGrid metadata
rg = dt["science/LSAR/GSLC/metadata/radarGrid"].dataset
los_x = np.asarray(rg["losUnitVectorX"])  # shape: (n_heights, ny, nx)
los_y = np.asarray(rg["losUnitVectorY"])
los_z = np.sqrt(np.maximum(1.0 - los_x**2 - los_y**2, 0.0))  # derive Z

heights = np.asarray(rg.coords["z"])      # height layers
x_rg = np.asarray(rg.coords["x"])         # radarGrid x coordinates
y_rg = np.asarray(rg.coords["y"])         # radarGrid y coordinates
epsg = int(rg.attrs.get("projection"))     # CRS

# Compute LIA using a DEM (must be in projected CRS, same as radarGrid)
lia = local_incidence_angle(dem, los_x, los_y, los_z, heights, x_rg, y_rg, epsg=epsg)
# lia is an xr.DataArray in degrees with CRS set

Roadmap

  • Lazy HDF5 reader returning xarray DataTree with CRS
  • Prep dolphin to run GSLC to dolphin ready yaml + geotiffs
  • ASF search and parallel download with validation
  • GSLC time-series stacking
  • Interferogram, coherence, multilooking, phase extraction
  • Phase unwrapping (SNAPHU)
  • Phase linking (EMI with SHP selection)
  • Polarimetric decomposition (H-A-alpha)
  • Local incidence angle computation
  • Visualization helpers (amplitude, phase, interferogram, coherence)
  • Support for additional NISAR product types

Contributing

Contributions are welcome! Please fork the repo and open a pull request, or open an issue to suggest improvements.

License

Distributed under the MIT License. See LICENSE.txt for more information.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nisar_pytools-0.2.0.tar.gz (112.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nisar_pytools-0.2.0-py3-none-any.whl (75.2 kB view details)

Uploaded Python 3

File details

Details for the file nisar_pytools-0.2.0.tar.gz.

File metadata

  • Download URL: nisar_pytools-0.2.0.tar.gz
  • Upload date:
  • Size: 112.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nisar_pytools-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f52b675b418fa6e6f55c9a72aa9f7516cfaa2a2ea617c288f16d5378665928db
MD5 ad3849e10c66c8dfb230999147dce88e
BLAKE2b-256 18aad6f049aa7f88afee40e1e3fc2d0df94ee2cd5bf0d439acca6a70aa4d37bb

See more details on using hashes here.

Provenance

The following attestation bundles were made for nisar_pytools-0.2.0.tar.gz:

Publisher: publish.yml on ZachHoppinen/nisar_pytools

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nisar_pytools-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: nisar_pytools-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 75.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nisar_pytools-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3eb687a2e9b06e671ff9a5160f0f652ae5b860a8f5708dc482296820c0f776d2
MD5 0e6a5760d03570157f43011107468c7c
BLAKE2b-256 02daa335c964a537faf83d53769308be00cd0e92ba174cf79e72239729ed553f

See more details on using hashes here.

Provenance

The following attestation bundles were made for nisar_pytools-0.2.0-py3-none-any.whl:

Publisher: publish.yml on ZachHoppinen/nisar_pytools

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page