diff --git a/xrspatial/polygonize.py b/xrspatial/polygonize.py index 35d6ac0d..39277c8c 100644 --- a/xrspatial/polygonize.py +++ b/xrspatial/polygonize.py @@ -550,6 +550,15 @@ def _polygonize_numpy( ) -> Tuple[List[Union[int, float]], List[List[np.ndarray]]]: ny, nx = values.shape + + # Mask NaN pixels for float arrays, matching the CuPy backend. + if np.issubdtype(values.dtype, np.floating): + nan_mask = ~np.isnan(values) + if mask is not None: + mask = mask & nan_mask + else: + mask = nan_mask + if nx == 1: # Algorithm requires nx > 1 to differentiate between facing E # (forward == 1) and facing N (forward == nx), so add extra column to diff --git a/xrspatial/tests/test_polygonize.py b/xrspatial/tests/test_polygonize.py index c9674f50..0ad2c01f 100644 --- a/xrspatial/tests/test_polygonize.py +++ b/xrspatial/tests/test_polygonize.py @@ -540,6 +540,39 @@ def test_polygonize_1008_geopandas_batch_with_holes(): assert_allclose(df.geometry.area.sum(), 36.0) +@pytest.mark.parametrize("connectivity", [4, 8]) +def test_polygonize_nan_pixels_excluded(connectivity): + """NaN pixels in float rasters should not produce polygons (#1190).""" + data = np.array([ + [1.0, np.nan], + [np.nan, 1.0], + ], dtype=np.float64) + raster = xr.DataArray(data) + values, polygons = polygonize(raster, connectivity=connectivity) + # No polygon should have a NaN value. + assert not any(np.isnan(v) for v in values), ( + f"NaN-valued polygons emitted: {values}" + ) + assert all(v == 1.0 for v in values) + # 4-connectivity: two separate 1-pixel regions; 8-connectivity: one region. + expected = 2 if connectivity == 4 else 1 + assert len(polygons) == expected + + +@pytest.mark.skipif(da is None, reason="dask not installed") +def test_polygonize_nan_pixels_excluded_dask(): + """Dask backend also masks NaN pixels (#1190).""" + data = np.array([ + [1.0, np.nan, 2.0], + [np.nan, 1.0, np.nan], + [3.0, np.nan, 1.0], + ], dtype=np.float64) + raster = xr.DataArray(da.from_array(data, chunks=(2, 2))) + values, polygons = polygonize(raster, connectivity=4) + assert not any(np.isnan(v) for v in values) + assert set(values) == {1.0, 2.0, 3.0} + + class TestSimplifyHelpers: """Tests for internal simplification helper functions."""