This is a toolbox for searching, downloading, and accessing NISAR data. It began as a local, proof-of-concept exploration of NISAR data. In its current state, it is intended to assist in SkyTruth's incident response work, to facilitate the incorporation of NISAR data into workflows that currently utilize Sentinel-1 or Sentinel-2 imagery.
A general note on the NISAR data processing described below, nisar_access defaults to Frequency B products. These are lower resolution, and also substantially smaller than Frequency A data. The suggested workflow is to search and evaluate on Frequency B data, and only download the Frequency A as needed.
It also supports ALOS-2 PALSAR-2 ScanSAR L2.2 imagery from JAXA's public S3 bucket (no login needed); see PALSAR-2 Data Access below.
In time, it will hopefully explore access to other less-utilized NASA data products, such as ICESat-2.
Where possible it prioritizes the use of Rust-based libraries (e.g. polars, pyreqwest, ruff).
This project uses uv as its default Python environment manager. The checked-in
environment contract is pyproject.toml, uv.lock, and .python-version.
Run all commands in this README from the repository root.
- Set up the shared environment:
uv sync --locked- Create all local, git-ignored files and directories (can be re-run, will not overwrite any already existing files/dirs).
uv run python -m utils.setupThis creates .env (from .env.default), an empty utils/aois.py, archive/, and the data/nisar_* and data/alos2_* folders.
Optional: Start Jupyter with the same environment:
uv run jupyter labIn Cursor or VS Code notebooks, choose the Python environment from this repo's
.venv. If prompted for a kernel source, select Python Environments..., then
the interpreter under .venv.
src/: search / download / convert tools, run as modules (see NISAR Data Access and PALSAR-2 Data Access)utils/aois.py: private AOIs (git-ignored)utils/generate_aoi_details.py: interactive helper that builds an AOI entryutils/setup.py: one-time setup; creates.env,utils/aois.py,archive/, and thedata/folders if missingutils/variables.py: shared constants: ASF settings, data paths, per-product H5 layouts,.envloading, JAXA/PALSAR-2 settingsdata/: git-ignored except.gitkeepalos2_geotiffs/: converted PALSAR-2 gamma0 GeoTIFFsalos2_granule_files/: granule CSVs from JAXA S3 searchesalos2_tif_scenes/: downloaded PALSAR-2 source.tifscenesearth_access_tests/,other_data/: samples for other explorationsnisar_geotiffs/: converted GeoTIFFsnisar_granule_files/: granule CSVs from ASF searchesnisar_h5_scenes/: downloaded.h5scenes
notebooks/: Jupyter notebooks (outputs should be stripped before committing)archive/: old snippets, miscellaneous code used only in local, throw-away tests (git-ignored)
Large source documents, licensed datasets, private materials, and downloaded research PDFs should not be committed to this repository unless they are clearly approved for redistribution.
TODO: strip notebook outputs before commit (nbstripout filter or .githooks/pre-commit).
This project uses Ruff for both linting and code
formatting. Ruff is declared in the dev dependency group, so uv sync installs it
automatically.
Check the repository for lint issues:
uv run ruff check .Apply the safe automatic fixes Ruff can make for the enabled rules:
uv run ruff check --fix .Format the code:
uv run ruff format .Verify formatting without editing files — handy as a pre-PR check:
uv run ruff format --check .All commands below must be run from the repository root: python -m src.… and python -m utils.… only find the src and utils packages from there.
The code in this repo requires an Earthdata access token. Tokens last 60 days. To generate a token follow these steps:
- Open
.env(created byutils.setup; or runcp .env.default .env) - Go to: https://urs.earthdata.nasa.gov/ and login (or create an account)
- Go to the Generate Token tab
- Click the Generate Token button
- Copy the Token and paste it into the
.envfile'sEARTHDATA_TOKEN. - In the
.envset theEARTHDATA_TOKEN_DATEto the date the token was generated
The search and download features of this repository depend on AOIs, currently defined by bounding boxes. AOI information should be stored in the git-ignored utils/aois.py. A few test AOIs (wva-test-area, drc-test-area, indian-ocean-test-area) defined in utils/variables.py are always available; an AOI in aois.py with the same name overrides them. Entry format: "aoi-name": {"bbox": "min_lon,min_lat,max_lon,max_lat"},. You can append the file directly following that format, or you can run the following code to generate one from bounds or a point plus a km buffer:
uv run python utils/generate_aoi_details.pysrc.nisar_access and the GeoTIFF conversion support GSLC, GCOV (default), GUNW, SME2.
--dbapplies to GSLC/GCOV only.src.asf_sar_searchwill also acceptGOFF(search only; not currently supported by the geotiff conversion).
uv run python -m src.nisar_access --aoi AOI_NAME --sd YYYY-MM-DD --ed YYYY-MM-DDFlags:
--product--polarization POL [POL ...]: one GeoTIFF per polarization; defaults per product, polarization options may not be consistent by collection- GCOV:
HHHH,HVHV,VVVV - GSLC/GUNW:
HH,HV
- GCOV:
--frequency A|B: Frequency A products are higher resolution, but also larger files. Defaults to Frequency B.--db: applies to GSLC/GCOV only.--crop: crops the output GeoTIFF to the AOI bbox (defaults to keeping the full scene)--keep-h5: by default each.h5is deleted after converting--out-dir: a user defined path to an output directory, if not defined geotiffs will be written todata/nisar_geotiffs/
- Search the ASF API. Writes a CSV file to
data/nisar_granule_files/.OR use an ad-hoc bbox, with an optionaluv run python -m src.asf_sar_search --processing-level PROCESSING_LEVEL --aoi AOI_NAME --sd YYYY-MM-DD --ed YYYY-MM-DD
--locationlabel in the filename:uv run python -m src.asf_sar_search --processing-level PROCESSING_LEVEL --bbox 'MIN_LON,MIN_LAT,MAX_LON,MAX_LAT' --location LABEL --sd YYYY-MM-DD --ed YYYY-MM-DD - Download the NISAR H5 scenes in
data/nisar_h5_scenes/. Scenes already on disk are skipped.uv run python -m src.nisar_dl --granule-file GRANULE_LIST.csv
--granule-fileis required; a bare filename is looked up indata/nisar_granule_files/.--granule NAME [NAME ...]downloads only those granules from the CSV. - Convert H5 to GeoTIFF in
data/nisar_geotiffs/.Flags:uv run python -m src.nisar_convert_to_tiff --h5 PATH/TO/H5_FILE.h5 [H5_FILE2.h5 ...]
--polarization--frequency A|B--db--bbox MIN_LON MIN_LAT MAX_LON MAX_LAT --aoi-name NAME: crop--remove-h5--out-dir--inspect: print the H5 structure instead of converting
ALOS-2 PALSAR-2 ScanSAR L2.2 scenes (Aug 2014 to present) are read from JAXA's public S3 bucket, so no credentials are needed. Searches use the same AOIs as NISAR (see Define AOIs above). All commands below must be run from the repository root.
JAXA's free ScanSAR archive mostly covers tropical land, coasts and polar regions; open ocean and much of the US, Europe and the Middle East are rarely or never observed, so an empty search is common outside those areas.
Any product or figure using this data must credit JAXA (see: ALOS2_ATTRIBUTION in utils/variables.py).
WBD,WWD,VBD: HH and HVWBS,WWS: HH only; HV is skipped for these scenes
uv run python -m src.alos2_access --aoi AOI_NAME --sd YYYY-MM-DD --ed YYYY-MM-DDFlags:
--mode MODE [MODE ...]: only these beam modes (defaults to all)--polarization POL [POL ...]:HHand/orHV, one GeoTIFF per polarization (defaults to both)--db: write gamma0 in dB--mask: set layover, shadow and invalid pixels to NaN (also downloads each scene's_MSK.tif)--crop: crops the output GeoTIFF to the AOI bbox (defaults to keeping the full scene)--keep-tif: by default each downloaded source.tifis deleted after converting--out-dir: a user defined path to an output directory, if not defined geotiffs will be written todata/alos2_geotiffs/
- Search JAXA's S3 archive. Writes a CSV file to
data/alos2_granule_files/.OR use an ad-hoc bbox, with an optionaluv run python -m src.alos2_search --aoi AOI_NAME --sd YYYY-MM-DD --ed YYYY-MM-DD
--locationlabel in the filename:uv run python -m src.alos2_search --bbox 'MIN_LON,MIN_LAT,MAX_LON,MAX_LAT' --location LABEL --sd YYYY-MM-DD --ed YYYY-MM-DD--mode MODE [MODE ...]limits the search to those beam modes. - Download the PALSAR-2 scenes to
data/alos2_tif_scenes/. Scenes already on disk are skipped.uv run python -m src.alos2_dl --granule-file GRANULE_LIST.csv
--granule-fileis required; a bare filename is looked up indata/alos2_granule_files/.--granule NAME [NAME ...]downloads only those granules from the CSV.--polarizationpicksHHand/orHV(defaults to both).--maskalso downloads each scene's_MSK.tif, needed for--maskat conversion. - Convert the source tifs (DN) to calibrated gamma0 GeoTIFFs in
data/alos2_geotiffs/. A bare filename is looked up indata/alos2_tif_scenes/.Flags:uv run python -m src.alos2_convert_to_tiff --tif SCENE_HH_SLP.tif [SCENE_HV_SLP.tif ...]
--db--mask: needs the scene's_MSK.tif(download it withsrc.alos2_dl --mask)--bbox MIN_LON MIN_LAT MAX_LON MAX_LAT --aoi-name NAME: crop--remove-tif--out-dir--inspect: print the tif's profile and tags instead of converting
This work is licensed under the Apache License, Version 2.0. See LICENSE