diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d54f0be..1fb50728d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,12 @@ #### Fixed +- `polygonize` now rejects non-finite values for `simplify_tolerance` + (`nan`, `+inf`, `-inf`) with a clear `ValueError`. Previously `nan` + silently disabled simplification (because `nan > 0` is False) and + `+inf` collapsed every polygon to empty output. Matches the existing + `atol` / `rtol` validation contract: finite and non-negative. (#2575) + - `reproject(..., bounds_policy="auto")` no longer crops valid edge data on ordinary geographic-to-projected reprojections. The old blow-up heuristic compared source span (e.g. degrees for EPSG:4326) against diff --git a/xrspatial/polygonize.py b/xrspatial/polygonize.py index 20946dc54..d26bb3a9e 100644 --- a/xrspatial/polygonize.py +++ b/xrspatial/polygonize.py @@ -2280,10 +2280,15 @@ def polygonize( raise ValueError( f"Incorrect transform length of {len(transform)} instead of 6") - # Check simplification parameters. - if simplify_tolerance is not None and simplify_tolerance < 0: + # Check simplification parameters. Mirror the atol/rtol validation + # below: reject NaN and +/-inf along with negative values. NaN would + # silently disable simplification because ``nan > 0`` is False, and + # +inf would collapse every polygon to empty output -- neither is + # what a caller asking for simplification wants. + if simplify_tolerance is not None and ( + not np.isfinite(simplify_tolerance) or simplify_tolerance < 0): raise ValueError( - "simplify_tolerance must be non-negative, " + "simplify_tolerance must be a non-negative finite number, " f"got {simplify_tolerance}") if simplify_method not in ("douglas-peucker", "visvalingam-whyatt"): raise ValueError( diff --git a/xrspatial/tests/test_polygonize.py b/xrspatial/tests/test_polygonize.py index 65abcaf08..c9c7328da 100644 --- a/xrspatial/tests/test_polygonize.py +++ b/xrspatial/tests/test_polygonize.py @@ -954,6 +954,34 @@ def test_negative_tolerance_raises(self): with pytest.raises(ValueError, match="simplify_tolerance"): polygonize(data, simplify_tolerance=-1.0) + @pytest.mark.parametrize("bad", [ + float("nan"), float("inf"), float("-inf")]) + def test_non_finite_tolerance_raises(self, bad): + """nan/inf/-inf for simplify_tolerance must raise ValueError. + + Regression for #2575: ``nan`` silently disabled simplification + (because ``nan > 0`` is False) and ``inf`` collapsed every + polygon to empty output. Mirror the atol/rtol validation + contract: require finite + non-negative. + """ + raster = np.array([[1, 1], [1, 1]], dtype=np.int64) + data = xr.DataArray(raster) + with pytest.raises(ValueError, match="simplify_tolerance"): + polygonize(data, simplify_tolerance=bad) + + def test_zero_and_small_positive_tolerance_still_work(self): + """0.0 and small positive tolerances must still be accepted. + + Regression for #2575: guard against an over-eager fix that + bans non-positive values along with non-finite ones. + """ + raster = np.array([[1, 1, 2, 2], + [1, 1, 2, 2]], dtype=np.int64) + data = xr.DataArray(raster) + # Both calls should succeed without raising. + polygonize(data, simplify_tolerance=0.0) + polygonize(data, simplify_tolerance=1e-9) + def test_invalid_method_raises(self): """Unknown method should raise ValueError.""" raster = np.array([[1, 1], [1, 1]], dtype=np.int64)