Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d1c4d7f
disable polarimetric symmetrization by default
gshiroma Jul 3, 2025
0ef09a8
Merge branch 'isce-framework:develop' into develop
gshiroma Jul 9, 2025
2ac2694
revert changes to `symmetrize_cross_pol_channels`
gshiroma Jul 22, 2025
05d7fda
Update GCOV and GSLC specification XMLs
gshiroma Jul 22, 2025
749058d
Revert changes to the GCOV and GSLC specification XMLs
gshiroma Jul 23, 2025
fc8ac53
Merge branch 'isce-framework:develop' into develop
gshiroma Jul 24, 2025
064d073
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 7, 2025
22464ef
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 7, 2025
a75e7f1
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 11, 2025
ab21d36
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 12, 2025
d61d489
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 18, 2025
0af53dd
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 20, 2025
d454e6a
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 21, 2025
6e7e9d1
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 28, 2025
dd80c38
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 29, 2025
63853b2
Merge branch 'isce-framework:develop' into develop
gshiroma Aug 29, 2025
7c7272f
Merge branch 'isce-framework:develop' into develop
gshiroma Sep 2, 2025
dd18a84
Merge branch 'isce-framework:develop' into develop
gshiroma Sep 17, 2025
d43ca22
Merge branch 'isce-framework:develop' into develop
gshiroma Oct 6, 2025
fa38bc0
Make the radar grid azimuth and range spacing parameters configurable…
gshiroma Oct 6, 2025
7b88217
Make the radar grid azimuth and range spacing parameters configurable…
gshiroma Oct 7, 2025
fd7453b
Make the radar grid azimuth and range spacing parameters configurable…
gshiroma Nov 10, 2025
4ee53eb
Expose sensing start/end times and starting/ending ranges in the STAT…
gshiroma Jan 21, 2026
70355b3
Merge branch 'isce-framework:develop' into improve_static_layers_work…
gshiroma Jan 21, 2026
31a98f0
Expose sensing start/end times and starting/ending ranges in the STAT…
gshiroma Jan 21, 2026
78a018d
rename some radar grid parameters to match focus
gshiroma Jan 26, 2026
5948de6
expose optional input RSLC into the STATIC workflow
gshiroma Jan 26, 2026
98b8c76
enable use of an optional RSLC product to generate static layers
gshiroma Jan 29, 2026
7cf5a1a
improve annotations
gshiroma Jan 29, 2026
f171e22
add default parameter values to get_cropped_orbit_and_attitude()
gshiroma Jan 29, 2026
97129a3
enable fractional precision for radargrid start and end times
gshiroma Feb 8, 2026
3383442
convert start & end date-time string to isce3.core.DateTime before ad…
gshiroma Feb 8, 2026
5b6a9ee
Merge branch 'isce-framework:develop' into improve_static_layers_work…
gshiroma Apr 23, 2026
4e7ec17
Merge branch 'develop' into improve_static_layers_workflow_optional_rslc
gshiroma Apr 23, 2026
a707f4f
revert some changes
gshiroma Apr 27, 2026
fa22e66
Revert changes to the docstrings
gshiroma May 6, 2026
548b263
Merge branch 'develop' into improve_static_layers_workflow_select_layers
gshiroma May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 48 additions & 12 deletions python/packages/nisar/static/ephemeris.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from datetime import datetime

from nisar.products.readers.attitude import load_attitude_from_xml
from nisar.products.readers.orbit import load_orbit_from_xml
from nisar.products.readers.orbit import load_orbit_from_xml, load_orbit
from nisar.products.readers import SLC

import isce3

Expand All @@ -13,10 +14,11 @@


def get_cropped_orbit_and_attitude(
orbit_xml_file: str | os.PathLike,
pointing_xml_file: str | os.PathLike,
start_time: str | datetime | None,
end_time: str | datetime | None,
input_file_path: str | os.PathLike | None = None,
orbit_xml_file: str | os.PathLike | None = None,
pointing_xml_file: str | os.PathLike | None = None,
start_time: str | datetime | None = None,
end_time: str | datetime | None = None,
*,
padding: float = 0.0,
) -> tuple[isce3.core.Orbit, isce3.core.Attitude]:
Expand All @@ -31,10 +33,12 @@ def get_cropped_orbit_and_attitude(

Parameters
----------
orbit_xml_file : path-like
input_file_path : str or os.PathLike or None
Path to the input NISAR L1 RSLC formatted HDF5 file.
orbit_xml_file : path-like or None
Path to the input orbit ephemeris XML file. Must be an existing XML file
conforming to the NISAR Orbit Ephemeris Product Specification\ [1]_.
pointing_xml_file : path-like
pointing_xml_file : path-like or None
Path to the input radar pointing XML file. Must be an existing XML file
conforming to the NISAR Radar Pointing Product Specification\ [2]_.
start_time : str or datetime.datetime or None
Expand Down Expand Up @@ -70,16 +74,48 @@ def get_cropped_orbit_and_attitude(

logger = get_logger()

# Load ephemeris data from input XML files.
logger.info(f"Load orbit data from file {orbit_xml_file}")
orbit_full = load_orbit_from_xml(orbit_xml_file)
if orbit_xml_file is not None:
# Load ephemeris data from input XML files.
logger.info(f"Load orbit data from file {orbit_xml_file}")

if input_file_path is not None:
# Ensure the orbit is referenced to the RSLC radar grid
# reference epoch.
rslc_product = SLC(hdf5file=str(input_file_path))
rslc_radar_grid = rslc_product.getRadarGrid()
orbit_full = load_orbit(rslc_product, orbit_xml_file,
rslc_radar_grid.ref_epoch)
else:
orbit_full = load_orbit_from_xml(orbit_xml_file)

elif input_file_path is not None:
# Load ephemeris data from input RSLC HDF5 file.
logger.info(f"Load orbit data from RSLC file {input_file_path}")
rslc_product = SLC(hdf5file=str(input_file_path))
orbit_full = rslc_product.getOrbit()
else:
raise ValueError(
"Either the RSLC HDF5 or the orbit XML file must be provided"
)

logger.info(
"Original orbit data spans time interval"
f" [{orbit_full.start_datetime, orbit_full.end_datetime}]"
)

logger.info(f"Load attitude data from file {pointing_xml_file}")
attitude_full = load_attitude_from_xml(pointing_xml_file)
if pointing_xml_file is not None:
logger.info(f"Load attitude data from file {pointing_xml_file}")
attitude_full = load_attitude_from_xml(pointing_xml_file)
elif input_file_path is not None:
# Load attitude data from input RSLC HDF5 file.
logger.info(f"Load attitude data from RSLC file {input_file_path}")
rslc_product = SLC(hdf5file=str(input_file_path))
attitude_full = rslc_product.getAttitude()
else:
raise ValueError(
"Either the RSLC HDF5 or the pointing XML file must be provided"
)

logger.info(
"Original attitude data spans time interval"
f" [{attitude_full.start_datetime, attitude_full.end_datetime}]"
Expand Down
9 changes: 6 additions & 3 deletions python/packages/nisar/static/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def populate_grids_group(
local_incidence_angle: isce3.io.Raster,
line_of_sight_x: isce3.io.Raster,
line_of_sight_y: isce3.io.Raster,
water_mask: isce3.io.Raster,
water_mask: isce3.io.Raster | None,
rtc_gamma_to_sigma_factor: isce3.io.Raster,
rtc_gamma_to_beta_factor: isce3.io.Raster,
geo_grid: isce3.product.GeoGridParameters,
Expand Down Expand Up @@ -439,8 +439,11 @@ def create_raster_layer_dataset(name: str, raster: isce3.io.Raster) -> h5py.Data
dem_dataset = create_raster_layer_dataset("digitalElevationModel", reprojected_dem)
dem_dataset.attrs["disclaimer"] = to_bytes(dem_disclaimer)

water_mask_dataset = create_raster_layer_dataset("waterMask", water_mask)
water_mask_dataset.attrs["disclaimer"] = to_bytes(water_mask_disclaimer)
if water_mask is not None:
water_mask_dataset = create_raster_layer_dataset("waterMask",
water_mask)
water_mask_dataset.attrs["disclaimer"] = to_bytes(
water_mask_disclaimer)

create_raster_layer_dataset("layoverShadowMask", layover_shadow_mask)
create_raster_layer_dataset("localIncidenceAngle", local_incidence_angle)
Expand Down
93 changes: 71 additions & 22 deletions python/packages/nisar/workflows/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from collections.abc import Sequence
from datetime import datetime, timezone
from pathlib import Path
from xmlrpc.client import DateTime

import h5py
import nisar
Expand All @@ -33,6 +34,8 @@
import isce3
from isce3.geometry import make_geo_grid_bounding_polygon, load_dem_from_proj
from isce3.core import normalize_look_side, normalize_data_interp_method
from nisar.products.readers import SLC
import numpy as np


def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
Expand Down Expand Up @@ -62,6 +65,7 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
output_params = groups["output"]

# Open the input DEM and water mask raster datasets.
input_file_path = dynamic_ancillary_files["input_file_path"]
dem_raster_file = dynamic_ancillary_files["dem_raster_file"]
water_mask_raster_file = dynamic_ancillary_files["water_mask_raster_file"]
logger.info(f"Open DEM raster file {dem_raster_file}")
Expand All @@ -75,6 +79,8 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
geo_grid = get_output_geo_grid(dem_raster=dem_raster, **geo_grid_params)
logger.info(f"Output geo grid: {geo_grid}")

flag_save_water_mask = output_params["layers"]["save_water_mask"]

proj = isce3.core.make_projection(geo_grid.epsg)

# dem = isce3.geometry.DEMInterpolator(dem_raster)
Expand All @@ -86,13 +92,13 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
geo_grid.end_y,
geo_grid.start_y,
normalize_data_interp_method(dem_interp_method),
proj,
)
proj)

# Parse the orbit and attitude data from the input XML files. Crop the
# data to the time interval of interest to avoid possible geo2rdr
# convergence errors due to ambiguity between orbit periods.
# Load the orbit and attitude data from the input RSLC or XML files.
# Crop the data to the time interval of interest to avoid possible
# geo2rdr convergence errors due to ambiguity between orbit periods.
orbit, attitude = get_cropped_orbit_and_attitude(
input_file_path=input_file_path,
orbit_xml_file=dynamic_ancillary_files["orbit_xml_file"],
pointing_xml_file=dynamic_ancillary_files["pointing_xml_file"],
**processing_params["ephemeris"],
Expand Down Expand Up @@ -125,7 +131,7 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:

if rg_spacing is not None and not (rg_spacing > 0.0):
raise ValueError(f"Runconfig {rg_spacing=}, must be > 0")

bounding_box_params = radar_grid_params["bounding_box"]
start_datetime_str = bounding_box_params["start_time"]
end_datetime_str = bounding_box_params["end_time"]
Expand Down Expand Up @@ -154,16 +160,53 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
end_time = isce3.core.DateTime(end_datetime_str)

if start_range is not None:
logger.info(f' start range [m]: {start_range}')
logger.info(f' start range: {start_range}')

if end_range is not None:
logger.info(f' end range [m]: {end_range}')
logger.info(f' end range: {end_range}')

if rg_spacing is not None:
logger.info(f' range spacing: {rg_spacing}')
if az_spacing is not None:
logger.info(f' azimuth time interval: {az_spacing}')

# Load radar grid parameters from RSLC (if provided)
if (input_file_path is not None and
(start_time is None or end_time is None or
start_range is None or end_range is None or
rg_spacing is None or az_spacing is None)):
logger.info("Load radar grid parameters from input RSLC file:")
rslc_product = SLC(hdf5file=str(input_file_path))
rslc_radar_grid = rslc_product.getRadarGrid()
rslc_orbit = rslc_product.getOrbit()

if start_time is None:
start_time = (rslc_orbit.reference_epoch +
isce3.core.TimeDelta(
rslc_radar_grid.sensing_start))
logger.info(f" start time: {start_time.isoformat()}")

if end_time is None:
end_time = (rslc_orbit.reference_epoch +
isce3.core.TimeDelta(
rslc_radar_grid.sensing_stop))
logger.info(f" end time: {end_time.isoformat()}")

if start_range is None:
start_range = rslc_radar_grid.starting_range
logger.info(f" start range: {start_range}")
if end_range is None:
end_range = rslc_radar_grid.end_range
logger.info(f" end range: {end_range}")

if rg_spacing is None:
rg_spacing = rslc_radar_grid.range_pixel_spacing
logger.info(f" range spacing: {rg_spacing}")

if az_spacing is None:
az_spacing = 1.0 / rslc_radar_grid.prf
logger.info(f" azimuth time interval: {az_spacing}")

if start_time is not None and az_margin != 0.0:
start_time -= isce3.core.TimeDelta(az_margin)
logger.info(f' start time (adjusted for az. margin) {az_margin}:'
Expand Down Expand Up @@ -312,15 +355,18 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
max_block_size=geocode_params["max_block_size"],
)

logger.info("Compute re-projected binary water mask layer")
with log_elapsed_time(logger.info,
"Computing re-projected binary water mask"):
binary_water_mask = binarize_and_reproject_water_mask(
water_distance_raster_file=water_mask_raster_file,
geo_grid=geo_grid,
scratch_dir=scratch_dir,
**processing_params["water_mask"],
)
if flag_save_water_mask:
logger.info("Compute re-projected binary water mask layer")
with log_elapsed_time(logger.info,
"Computing re-projected binary water mask"):
binary_water_mask = binarize_and_reproject_water_mask(
water_distance_raster_file=water_mask_raster_file,
geo_grid=geo_grid,
scratch_dir=scratch_dir,
**processing_params["water_mask"],
)
else:
binary_water_mask = None

# Compute radiometric terrain correction (RTC) area normalization
# factor (ANF) layers. Results are stored as GeoTIFF files in the
Expand Down Expand Up @@ -452,11 +498,14 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
dem_description = get_raster_dataset_metadata_item(
dem_raster_file, "dem_description", default="(NOT SPECIFIED)"
)
water_mask_description = get_raster_dataset_metadata_item(
water_mask_raster_file,
"water_mask_description",
default="(NOT SPECIFIED)",
)
if flag_save_water_mask:
water_mask_description = get_raster_dataset_metadata_item(
water_mask_raster_file,
"water_mask_description",
default="(NOT SPECIFIED)",
)
else:
water_mask_description = "(NOT APPLICABLE)"

# Populate the 'grids' group.
logger.info("Populate raster layers and grid coordinates in output"
Expand Down
17 changes: 13 additions & 4 deletions share/nisar/defaults/static.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
runconfig:
groups:
dynamic_ancillary_file_group:

# [OPTIONAL] One NISAR L1 RSLC formatted HDF5 file
input_file_path:
# [REQUIRED] File path or URL of the input Digital Elevation Model (DEM) raster
# file.
# Must be an existing file in a GDAL-compatible raster format that spans the
Expand All @@ -14,12 +17,14 @@ runconfig:
# region of interest and conforms to the NISAR Water Mask Product Specification
# (JPL D-107710).
water_mask_raster_file:
# [REQUIRED] Path to the input orbit ephemeris XML file.
# [REQUIRED] if `input_file_path` is not provided, otherwise [OPTIONAL].
# Path to the input orbit ephemeris XML file.
# Must be an existing XML file conforming to the NISAR Orbit Ephemeris Product
# Specification (JPL D-102253) and spanning the desired radar observation time
# interval.
orbit_xml_file:
# [REQUIRED] Path to the input radar pointing XML file.
# [REQUIRED] if `input_file_path` is not provided, otherwise [OPTIONAL].
# Path to the input radar pointing XML file.
# Must be an existing XML file conforming to the NISAR Radar Pointing Product
# Specification (JPL D-102264) and spanning the desired radar observation time
# interval.
Expand Down Expand Up @@ -250,8 +255,8 @@ runconfig:
start_time:

# [OPTIONAL] Azimuth ending UTC date and time of the end of the radar
# observation, as a string in ISO 8601. format with up to nanosecond precision.
# Must be >= `start_time`.
# observation, as a string in ISO 8601. Format with up to nanosecond
# precision. Must be >= `start_time`.
# If not provided, it will be inferred from the specified geographic grid.
# If provided, must be a valid azimuth time within the observation time
# interval.
Expand Down Expand Up @@ -434,6 +439,10 @@ runconfig:
extraiter: 10

output:
layers:
# [OPTIONAL] Whether to save the water mask layer in the output product.
# Defaults to true.
save_water_mask: true
dataset:
# [OPTIONAL] Chunk dimensions of 2-D raster datasets in the output product.
# Setting `chunk_size` to [-1, -1] will disable chunked storage.
Expand Down
11 changes: 8 additions & 3 deletions share/nisar/schemas/static.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
runconfig:
groups:
dynamic_ancillary_file_group:
input_file_path: str(required=False)
dem_raster_file: str()
water_mask_raster_file: str()
orbit_xml_file: str()
pointing_xml_file: str()
water_mask_raster_file: str(required=False)
orbit_xml_file: str(required=False)
pointing_xml_file: str(required=False)
product_path_group: include('product_path_group_options', required=False)
primary_executable: include('primary_executable_options', required=False)
geometry: include('geometry_options', required=False)
Expand Down Expand Up @@ -152,9 +153,13 @@ rdr2geo_options:
extraiter: int(min=1, required=False)

output_options:
layers: include('output_layers_options', required=False)
dataset: include('dataset_options', required=False)
file: include('file_options', required=False)

output_layers_options:
save_water_mask: bool(required=False)

dataset_options:
chunk_size: list(int(min=-1), min=2, max=2, required=False)
compression_enabled: bool(required=False)
Expand Down
Loading