NISAR Access and Visualize
Authors: Sumant Jha (MSFC/USRA), Emile Tenezakis (DevSeed), Alex Mandel (DevSeed), Samuel Ayers (UAH)
Date: March 9, 2023
Description: In the following tutorial, we are going to look at how to access, read, and visualize simulated-NISAR data which is available from the NISAR mission. This mission is expected to launch in 2024.
Run This Notebook
To access and run this tutorial within MAAP’s Algorithm Development Environment (ADE), please refer to the “Getting started with the MAAP” section of our documentation.
Disclaimer: it is highly recommended to run a tutorial within MAAP’s ADE, which already includes packages specific to MAAP, such as maap-py. Running the tutorial outside of the MAAP ADE may lead to errors.
About the Data
Unmanned Aerial Vehicle Synthetic Aperture Radar (UAVSAR)
This tutorial currently uses UAVSAR data, which has the purpose of mapping crustal deformations associated with natural hazards. UAVSAR data can also include topographic data which are derived from phase measurements. If two or more passes are made, UAVSAR data can be used to detect changes on the surface of the Earth, and is overall a highly useful tool in monitoring natural hazards and disasters. (Source: ESA eoPortal - UAVSAR)
Note: UAVSAR is a predecessor to NASA-ISRO Synthetic Aperture Radar (NISAR), which is a mission that will likely be launched in 2024. At this time we are using UAVSAR data to simulate the data that we will get from the NISAR satellite.
Additional Resources
Import and Install Packages
Before we start any work, we will need to prepare our Jupyter workspace with the necessary python packages. In this tutorial we rely on - h5py, numpy, glob, os, h5glance, osgeo, and matplotlib.
You might have some or all packages already installed. Follow the instructions below to check and install the packages required for this tutorial. The block below will check if your packages are available and install them if they are not.
[1]:
# %pip install h5py glob2 h5glance gdal matplotlib pystac-client
We also require that you have the gdal package installed out of the Jupyter environment to use: gdal_translate and gdalinfo. If on Mac OS, you might need to install the xcode package before being able to install gdal.
To install gdal outside the Jupyter environment, please follow the instructions here as it will vary by the OS you are using: https://gdal.org/download.html
Import installed packages to read the NISAR h5 files and look at the data structure.
[2]:
import h5py
import glob
import os
from h5glance import H5Glance
from pystac_client import Client
Download NISAR Data
NISAR data can be downloaded from https://uavsar.jpl.nasa.gov/cgi-bin/data.pl. At the time of this tutorial, there are 21033 NISAR products available through the above link. There are a few ways to download data that we can use. We can directly download an SLC file or we can download an H5 file and translate it to SLC to work with this tutorial. In this tutorial, we demonstrate how to get an H5 file, translate it to SLC and use it. Users can alternatively directly download SLC files.
We can browse the 129 NISAR mode products in the MAAP STAC catalog and retrieve its asset HREFs for download.
[3]:
cat = Client.open("https://stac.maap-project.org/")
items = list(cat.search(collections="nisar-sim", max_items=10).items())
[4]:
item_asset = items[0].assets["CX_129.h5"].href
Before downloading, we’ll create a new data directory to download the file into.
[5]:
# set data directory
dataDir = "./data"
# check if directory exists -> if directory doesn't exist, directory is created
if not os.path.exists(dataDir):
os.mkdir(dataDir)
[6]:
# download the file using wget
!wget -P {dataDir} {item_asset}
--2023-09-18 14:52:57-- https://downloaduav.jpl.nasa.gov/Release2z/Haywrd_14501_21043_012_210602_L090_CX_02/Haywrd_14501_21043_012_210602_L090_CX_129_02.h5
Resolving downloaduav.jpl.nasa.gov (downloaduav.jpl.nasa.gov)... 137.78.249.121
Connecting to downloaduav.jpl.nasa.gov (downloaduav.jpl.nasa.gov)|137.78.249.121|:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://uavsar.jpl.nasa.gov/Release2z/Haywrd_14501_21043_012_210602_L090_CX_02/Haywrd_14501_21043_012_210602_L090_CX_129_02.h5 [following]
--2023-09-18 14:52:57-- https://uavsar.jpl.nasa.gov/Release2z/Haywrd_14501_21043_012_210602_L090_CX_02/Haywrd_14501_21043_012_210602_L090_CX_129_02.h5
Resolving uavsar.jpl.nasa.gov (uavsar.jpl.nasa.gov)... 137.78.249.121
Connecting to uavsar.jpl.nasa.gov (uavsar.jpl.nasa.gov)|137.78.249.121|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1750205900 (1.6G)
Saving to: ‘./data/Haywrd_14501_21043_012_210602_L090_CX_129_02.h5.2’
Haywrd_14501_21043_ 100%[===================>] 1.63G 53.5MB/s in 35s
2023-09-18 14:53:40 (48.3 MB/s) - ‘./data/Haywrd_14501_21043_012_210602_L090_CX_129_02.h5.2’ saved [1750205900/1750205900]
The file should now appear in the directory we created.
If you downloaded multiple H5 files, initialize an empty variable to store the names of all H5 files in the directory.
[7]:
nisar_list = []
[8]:
for file in glob.glob("./data*/*.h5"):
nisar_list.append(file)
print(file)
./data/Haywrd_14501_21043_012_210602_L090_CX_129_02.h5
Explore the Data
Explore the structure of NISAR *.H5 file using H5glance.
[10]:
H5Glance(nisar_list[0])
[10]:
- alongTrackUnitVectorX [📋]: 6 × 392 × 225 entries, dtype: float32
- alongTrackUnitVectorY [📋]: 6 × 392 × 225 entries, dtype: float32
- coordinateX [📋]: 6 × 392 × 225 entries, dtype: float64
- coordinateY [📋]: 6 × 392 × 225 entries, dtype: float64
- elevationAngle [📋]: 6 × 392 × 225 entries, dtype: float32
- epsg [📋]: scalar entries, dtype: int32
- groundTrackVelocity [📋]: 6 × 392 × 225 entries, dtype: float32
- heightAboveEllipsoid [📋]: 6 entries, dtype: float64
- incidenceAngle [📋]: 6 × 392 × 225 entries, dtype: float32
- losUnitVectorX [📋]: 6 × 392 × 225 entries, dtype: float32
- losUnitVectorY [📋]: 6 × 392 × 225 entries, dtype: float32
- slantRange [📋]: 225 entries, dtype: float64
- zeroDopplerTime [📋]: 392 entries, dtype: float64
- ISCEVersion [📋]: scalar entries, dtype: 27-byte ASCII string
- SWSTCorrection [📋]: scalar entries, dtype: 27-byte ASCII string
- azimuthCompression [📋]: scalar entries, dtype: 27-byte ASCII string
- azimuthPresumming [📋]: scalar entries, dtype: 27-byte ASCII string
- dopplerCentroidEstimation [📋]: scalar entries, dtype: 27-byte ASCII string
- driftCompensator [📋]: scalar entries, dtype: 27-byte ASCII string
- elevationAntennaPatternCorrection [📋]: scalar entries, dtype: 27-byte ASCII string
- internalCalibration [📋]: scalar entries, dtype: 27-byte ASCII string
- patchProcessing [📋]: scalar entries, dtype: 27-byte ASCII string
- postProcessing [📋]: scalar entries, dtype: 27-byte ASCII string
- rangeCellMigration [📋]: scalar entries, dtype: 27-byte ASCII string
- rangeCompression [📋]: scalar entries, dtype: 27-byte ASCII string
- rangeDependentGainCorrection [📋]: scalar entries, dtype: 27-byte ASCII string
- rangeReferenceFunctionGenerator [📋]: scalar entries, dtype: 27-byte ASCII string
- rangeSpreadingLossCorrection [📋]: scalar entries, dtype: 27-byte ASCII string
- secondaryRangeCompression [📋]: scalar entries, dtype: 27-byte ASCII string
- attitudeFiles [📋]: 3 entries, dtype: 5-byte ASCII string
- auxcalFiles [📋]: 5 entries, dtype: 7-byte ASCII string
- configFiles [📋]: 7 entries, dtype: 11-byte ASCII string
- demFiles [📋]: 11 entries, dtype: 13-byte ASCII string
- l0bGranules [📋]: 23 entries, dtype: 29-byte ASCII string
- orbitFiles [📋]: 29 entries, dtype: 3-byte ASCII string
- azimuthChirpWeighting [📋]: 256 entries, dtype: float32
- effectiveVelocity [📋]: 392 × 225 entries, dtype: float64
- rangeChirpWeighting [📋]: 256 entries, dtype: float32
- referenceTerrainHeight [📋]: 392 entries, dtype: float32
- slantRange [📋]: 225 entries, dtype: float64
- zeroDopplerTime [📋]: 392 entries, dtype: float64
- HH [📋]: 15404 × 2640 entries, dtype: (r: float32, i: float32)
- HV [📋]: 15404 × 2640 entries, dtype: (r: float32, i: float32)
- VH [📋]: 15404 × 2640 entries, dtype: (r: float32, i: float32)
- VV [📋]: 15404 × 2640 entries, dtype: (r: float32, i: float32)
- acquiredCenterFrequency [📋]: scalar entries, dtype: float64
- acquiredRangeBandwidth [📋]: scalar entries, dtype: float64
- listOfPolarizations [📋]: 4 entries, dtype: 2-byte ASCII string
- nominalAcquisitionPRF [📋]: scalar entries, dtype: float64
- numberOfSubSwaths [📋]: scalar entries, dtype: uint8
- processedAzimuthBandwidth [📋]: scalar entries, dtype: float64
- processedCenterFrequency [📋]: scalar entries, dtype: float64
- processedRangeBandwidth [📋]: scalar entries, dtype: float64
- sceneCenterAlongTrackSpacing [📋]: scalar entries, dtype: float64
- sceneCenterGroundRangeSpacing [📋]: scalar entries, dtype: float64
- slantRange [📋]: 2640 entries, dtype: float64
- slantRangeSpacing [📋]: scalar entries, dtype: float64
- validSamplesSubSwath1 [📋]: 15404 × 2 entries, dtype: uint16
- HH [📋]: 15404 × 660 entries, dtype: (r: float32, i: float32)
- HV [📋]: 15404 × 660 entries, dtype: (r: float32, i: float32)
- VH [📋]: 15404 × 660 entries, dtype: (r: float32, i: float32)
- VV [📋]: 15404 × 660 entries, dtype: (r: float32, i: float32)
- acquiredCenterFrequency [📋]: scalar entries, dtype: float64
- acquiredRangeBandwidth [📋]: scalar entries, dtype: float64
- listOfPolarizations [📋]: 4 entries, dtype: 2-byte ASCII string
- nominalAcquisitionPRF [📋]: scalar entries, dtype: float64
- numberOfSubSwaths [📋]: scalar entries, dtype: uint8
- processedAzimuthBandwidth [📋]: scalar entries, dtype: float64
- processedCenterFrequency [📋]: scalar entries, dtype: float64
- processedRangeBandwidth [📋]: scalar entries, dtype: float64
- sceneCenterAlongTrackSpacing [📋]: scalar entries, dtype: float64
- sceneCenterGroundRangeSpacing [📋]: scalar entries, dtype: float64
- slantRange [📋]: 660 entries, dtype: float64
- slantRangeSpacing [📋]: scalar entries, dtype: float64
- validSamplesSubSwath1 [📋]: 15404 × 2 entries, dtype: uint16
- zeroDopplerTime [📋]: 15404 entries, dtype: float64
- zeroDopplerTimeSpacing [📋]: scalar entries, dtype: float64
- absoluteOrbitNumber [📋]: scalar entries, dtype: uint32
- boundingPolygon [📋]: scalar entries, dtype: 95-byte ASCII string
- cycleNumber [📋]: scalar entries, dtype: 1-byte ASCII string
- diagnosticModeFlag [📋]: scalar entries, dtype: 11-byte ASCII string
- frameNumber [📋]: scalar entries, dtype: uint16
- isUrgentObservation [📋]: 5 entries, dtype: 5-byte ASCII string
- listOfFrequencies [📋]: 2 entries, dtype: 2-byte ASCII string
- lookDirection [📋]: scalar entries, dtype: 19-byte ASCII string
- missionId [📋]: scalar entries, dtype: 13-byte ASCII string
- orbitPassDirection [📋]: scalar entries, dtype: 23-byte ASCII string
- plannedDatatakeId [📋]: 3 entries, dtype: 29-byte ASCII string
- plannedObservationId [📋]: 5 entries, dtype: 3-byte ASCII string
- processingType [📋]: scalar entries, dtype: 17-byte ASCII string
- productType [📋]: scalar entries, dtype: 17-byte ASCII string
- productVersion [📋]: scalar entries, dtype: 16-byte ASCII string
- trackNumber [📋]: scalar entries, dtype: 5-byte ASCII string
- zeroDopplerEndTime [📋]: scalar entries, dtype: 27-byte ASCII string
- zeroDopplerStartTime [📋]: scalar entries, dtype: 27-byte ASCII string
Convert the Data
Use gdal to convert HH polarization for frequencyA to ENVI SLC format and visualize it. (More example notebooks are provided here: https://github.com/isce-framework/sds-ondemand)
Before we go ahead and convert the HH polarization, check out the metadata information associated with it.
[11]:
path = f"HDF5:{nisar_list[0]}://science/LSAR/SLC/swaths/frequencyA/HH"
!gdalinfo {path}
Driver: HDF5Image/HDF5 Dataset
Files: ./data/Haywrd_14501_21043_012_210602_L090_CX_129_02.h5
Size is 2640, 15404
Metadata:
contact=nisarops@jpl.nasa.gov
Conventions=CF-1.7
institution=NASA JPL
mission_name=NISAR
reference_document=TBD
title=NISAR L1 SLC Product
Corner Coordinates:
Upper Left ( 0.0, 0.0)
Lower Left ( 0.0,15404.0)
Upper Right ( 2640.0, 0.0)
Lower Right ( 2640.0,15404.0)
Center ( 1320.0, 7702.0)
Band 1 Block=128x128 Type=CFloat32, ColorInterp=Undefined
Metadata:
science_LSAR_SLC_swaths_frequencyA_HH_description=Focused SLC image (HH)
science_LSAR_SLC_swaths_frequencyA_HH_units=DN
Translate the HH polarization into SLC files. SLC files are Single Look Complex files. More information can be found in the product description here.
[12]:
path = f"HDF5:{nisar_list[0]}://science/LSAR/SLC/swaths/frequencyA/HH HH.slc"
!gdal_translate -of ENVI {path}
Input file size is 2640, 15404
0...10...20...30...40...50...60...70...80...90...100 - done.
Check the metadata again.
[13]:
!gdalinfo HH.slc
Driver: ENVI/ENVI .hdr Labelled
Files: HH.slc
HH.slc.aux.xml
HH.hdr
Size is 2640, 15404
Metadata:
contact=nisarops@jpl.nasa.gov
Conventions=CF-1.7
institution=NASA JPL
mission_name=NISAR
reference_document=TBD
title=NISAR L1 SLC Product
Image Structure Metadata:
INTERLEAVE=BAND
Corner Coordinates:
Upper Left ( 0.0, 0.0)
Lower Left ( 0.0,15404.0)
Upper Right ( 2640.0, 0.0)
Lower Right ( 2640.0,15404.0)
Center ( 1320.0, 7702.0)
Band 1 Block=2640x1 Type=CFloat32, ColorInterp=Undefined
Metadata:
science_LSAR_SLC_swaths_frequencyA_HH_description=Focused SLC image (HH)
science_LSAR_SLC_swaths_frequencyA_HH_units=DN
Visualize
Finally, lets plot amplitude and phase.
[14]:
import numpy as np
import rasterio
import matplotlib.pyplot as plt
# Extract a subset of the SLC to display
x0 = 0
y0 = 10
x_offset = 1000
y_offset = 1000
with rasterio.open("HH.slc") as ds:
# Define the window of data to read
window = rasterio.windows.Window(x0, y0, x_offset, y_offset)
# Read the data from the specified window
slc = ds.read(1, window=window)
print(slc)
fig = plt.figure(figsize=(14, 12))
# Display amplitude of the slc
ax = fig.add_subplot(2, 1, 1)
ax.imshow(np.abs(slc), vmin=-2, vmax=2, cmap="gray")
ax.set_title("amplitude")
# Display phase of the slc
ax = fig.add_subplot(2, 1, 2)
ax.imshow(np.angle(slc))
ax.set_title("phase")
plt.show()
/opt/conda/lib/python3.10/site-packages/rasterio/__init__.py:304: NotGeoreferencedWarning: Dataset has no geotransform, gcps, or rpcs. The identity matrix will be returned.
dataset = DatasetReader(path, driver=driver, sharing=sharing, **kwargs)
[[ 2.46178992e-02-0.01162272j 2.06204206e-02+0.0438217j
2.01302450e-02-0.00866299j ... -3.71712632e-03+0.00754739j
-3.99701595e-02+0.01584719j 4.39935476e-02+0.03462401j]
[-5.99635430e-02-0.04907523j -1.78322736e-02-0.04193708j
4.85753343e-02-0.00152001j ... -3.92718357e-04+0.03611586j
3.35564204e-02+0.0750533j -2.58343443e-02+0.00963823j]
[-1.27040278e-02+0.00876454j 9.38015990e-03+0.01417367j
-1.09148119e-02-0.00828656j ... -1.72818732e-02-0.08329829j
5.43021038e-03-0.03845887j -2.69295573e-02-0.03714512j]
...
[-1.18659958e-02-0.04372251j -4.90541667e-01-0.04433485j
-6.41287342e-02+0.12093218j ... -8.06556106e-01+0.35332313j
2.51429707e-01-0.24497119j 2.09877625e-01+0.43311965j]
[ 1.27028033e-01+0.08081798j -1.80030346e-01-0.05467323j
-1.05361664e+00-0.45684314j ... -8.28622878e-01-0.33322746j
-1.37368396e-01-0.42675552j 3.07213575e-01-0.02091259j]
[ 1.09616600e-01-0.02663859j -1.23310655e-01+0.22490016j
-9.74314928e-01+0.17205611j ... -4.84358639e-01-0.2353034j
-8.60485807e-02+0.06121469j 2.02866718e-02+0.14383014j]]

[ ]: