Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ffd8aa7
Add an option in the GCOV workflow to assign values to regions with m…
gshiroma Jun 30, 2025
d20b793
Merge branch 'isce-framework:develop' into gcov_fill_no_data
gshiroma Jul 1, 2025
a68b4d0
Merge branch 'isce-framework:develop' into gcov_fill_no_data
gshiroma Aug 11, 2025
2043827
Merge branch 'isce-framework:develop' into gcov_fill_no_data
gshiroma Aug 10, 2026
d33f91f
Add parameter to the module
gshiroma Aug 12, 2026
8f08f5d
Add parameter to the module
gshiroma Aug 12, 2026
317c12a
Add parameter to the module
gshiroma Aug 12, 2026
e0d5c44
Add parameter to the module
gshiroma Aug 12, 2026
5271c94
Set layover/shadow mask layer fill value to 255.
gshiroma Aug 12, 2026
9acf85f
Merge remote-tracking branch 'refs/remotes/origin/fill_no_data' into …
gshiroma Aug 12, 2026
eb45442
Set layover/shadow mask layer fill value to 255.
gshiroma Aug 12, 2026
06d1c5b
update data type
gshiroma Aug 12, 2026
25d2808
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
22c9874
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
93c7a1c
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
2cc4af2
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
e4891c3
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
e380388
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
8091ba8
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
396a448
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
f6e6692
add `radar_grid` parameter to L2 metadata cubes
gshiroma Aug 13, 2026
eb21db3
ensure static layers have consistent coverage
gshiroma Aug 13, 2026
21d5e5d
Merge branch 'isce-framework:develop' into ensure_static_layers_have_…
gshiroma Aug 13, 2026
db5b92c
fix binarize_nisar_water_mask unit test
gshiroma Aug 14, 2026
3b55a6e
Merge remote-tracking branch 'refs/remotes/origin/ensure_static_layer…
gshiroma Aug 14, 2026
50f0002
ensure static layers have consistent coverage
gshiroma Aug 14, 2026
9b9425e
revert unnecessary changes
gshiroma Aug 17, 2026
f7c55b0
revert unnecessary changes
gshiroma Aug 17, 2026
0a33270
merge develop into ensure_static_layers_have_consistent_coverage
gshiroma Aug 18, 2026
3c71cbc
Merge branch 'develop' into ensure_static_layers_have_consistent_cove…
gshiroma Aug 18, 2026
295374b
Merge branch 'isce-framework:develop' into ensure_static_layers_have_…
gshiroma Aug 25, 2026
dee8436
Update python/packages/nisar/static/water_mask.py
gshiroma Aug 25, 2026
1c5415f
Update tests/python/packages/nisar/static/water_mask.py
gshiroma Aug 25, 2026
79d3a2f
add comment to the creation of the water mask layer
gshiroma Aug 25, 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
38 changes: 38 additions & 0 deletions python/packages/nisar/static/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,44 @@ def unary_transform_blockwise(
dst[subblock] = transform(src[subblock])


def binary_transform_blockwise(
transform: Callable[[np.ndarray, np.ndarray], np.ndarray],
src1: isce3.io.DatasetReader,
src2: isce3.io.DatasetReader,
dst: isce3.io.DatasetWriter,
*,
chunks: tuple[int, int] = (512, 512),
) -> None:
"""
Transform the contents of two datasets by applying a binary function
block-by-block.

Parameters
----------
transform : callable
The function to apply to each pair of blocks from the input datasets.
It should accept two `numpy.ndarray` objects as positional arguments and
return a `numpy.ndarray` of the same shape.
src1 : isce3.io.DatasetReader
The first input dataset.
src2 : isce3.io.DatasetReader
The second input dataset.
dst : isce3.io.DatasetWriter
The output dataset to write the result to.
chunks : (int, int), optional
The shape of a typical block. The last block along each axis may be smaller.
Each chunk dimension must be positive-valued. Defaults to (512, 512).
"""
if src1.shape != src2.shape:
raise ValueError(f"shape mismatch: {src1.shape=} must be equal to {src2.shape=}")

if src1.shape != dst.shape:
raise ValueError(f"shape mismatch: {src1.shape=} must be equal to {dst.shape=}")

for subblock in block_iterator(src1.shape, chunks):
dst[subblock] = transform(src1[subblock], src2[subblock])


def copy_blockwise(
src: isce3.io.DatasetReader,
dst: isce3.io.DatasetWriter,
Expand Down
30 changes: 21 additions & 9 deletions python/packages/nisar/static/water_mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

import isce3

from .util import make_scratch_file, make_scratch_gtiff, unary_transform_blockwise
from .util import make_scratch_file, make_scratch_gtiff, binary_transform_blockwise


def binarize_nisar_water_mask(water_distance: ArrayLike) -> np.ndarray:
def binarize_nisar_water_mask(water_distance: ArrayLike,
reference_fill_value: ArrayLike) -> np.ndarray:
r"""
Convert the input water distance map into a binary water mask.

Expand All @@ -22,12 +23,15 @@ def binarize_nisar_water_mask(water_distance: ArrayLike) -> np.ndarray:
Product Specification\ [1]_. A value of 0 indicates a not-water pixel. A value
of 255 represents a no-data (invalid) pixel. Values in 1-200 represent water
pixels.
reference_fill_value : array_like
Image to use as a reference for identifying fill pixels and ensuring that the
output layer has the same extent.

Returns
-------
water_mask : numpy.ndarray
Binary mask where 1 indicates a water pixel (ocean or inland water), 0
indicates a not-water pixel, and 255 represents a no-data (invalid) pixel.
indicates a non-water pixel, and 255 represents a no-data (invalid) pixel.

References
----------
Expand All @@ -37,14 +41,16 @@ def binarize_nisar_water_mask(water_distance: ArrayLike) -> np.ndarray:

# Compute a binary mask where the value 1 represents (ocean or inland) water pixels
# and the value 0 represents non-water pixels.
water = (water_distance >= 1) & (water_distance <= 200)
water = ((water_distance >= 1) & (water_distance <= 200)).astype(np.uint8)

# Get a binary mask of invalid pixels (i.e. pixels whose value is equal to the fill
# value of 255).
# Set pixels to the fill value if either the water distance or the
# reference image contains a fill value.
fill_value = 255
invalid = water_distance == fill_value
water[
((water_distance == fill_value) | (reference_fill_value == fill_value))
] = fill_value

return (water + fill_value * invalid).astype(np.uint8)
return water


def reproject_raster(
Expand Down Expand Up @@ -90,6 +96,7 @@ def binarize_and_reproject_water_mask(
*,
scratch_dir: os.PathLike | str | None = None,
resample_algorithm: str = "near",
reference_fill_value: np.ndarray | None = None,
) -> isce3.io.Raster:
"""
Re-project the input water distance map and convert it to a binary mask.
Expand Down Expand Up @@ -121,6 +128,10 @@ def binarize_and_reproject_water_mask(
'mode':
Mode resampling (selects the value which appears most often among sampled
points).
reference_fill_value : numpy.ndarray, optional
Image to use as a reference for `fill_value` to ensure that the layers have
the same extents. Pixels where `reference_fill_value` is `255` are considered
fill values.

Returns
-------
Expand Down Expand Up @@ -156,6 +167,7 @@ def binarize_and_reproject_water_mask(
)

# Convert the water distance map to a binary mask.
unary_transform_blockwise(binarize_nisar_water_mask, water_distance, water_mask)
binary_transform_blockwise(binarize_nisar_water_mask, water_distance,
reference_fill_value, water_mask)

return water_mask
7 changes: 7 additions & 0 deletions python/packages/nisar/workflows/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,20 @@ def run_static_layers_workflow(config_file: os.PathLike | str) -> None:
max_block_size=geocode_params["max_block_size"],
)

# Reproject the water mask to the output geogrid and copy the
# fill values from the layover/shadow mask. The geocoding of the
# layover/shadow mask assigns the fill value to areas outside the
# radar grid, which is then used to identify the corresponding fill
# values in the water mask and limit its classification to the
# radar grid boundaries.
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,
reference_fill_value=layover_shadow_mask,
**processing_params["water_mask"],
)

Expand Down
19 changes: 16 additions & 3 deletions tests/python/packages/nisar/static/water_mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,28 @@ def test_binarize_nisar_water_mask():
# Concatenate the three arrays into a single 20x30 array.
water_distance = np.concatenate([nonwater, water, invalid], axis=1)

# Create reference fill value mask with same shape as `water_distance`
reference_fill_value = np.zeros_like(water_distance)

# Add fill values over non water
reference_fill_value[:, 5:10] = 255

# Add fill values over water
reference_fill_value[:, 15:20] = 255

# Convert the water distance map into a binary water mask
water_mask = binarize_nisar_water_mask(water_distance)
water_mask = binarize_nisar_water_mask(
water_distance, reference_fill_value=reference_fill_value
)

# Check the datatype of the output mask.
assert water_mask.dtype == np.uint8

# Check the mask values.
np.testing.assert_equal(water_mask[:, :10], 0)
np.testing.assert_equal(water_mask[:, 10:20], 1)
np.testing.assert_equal(water_mask[:, :5], 0)
np.testing.assert_equal(water_mask[:, 5:10], 255)
np.testing.assert_equal(water_mask[:, 10:15], 1)
np.testing.assert_equal(water_mask[:, 15:20], 255)
np.testing.assert_equal(water_mask[:, 20:], 255)


Expand Down
Loading