diff --git a/xrspatial/hydro/tests/test_flow_accumulation_d8.py b/xrspatial/hydro/tests/test_flow_accumulation_d8.py index 18af2bcb..778e6e16 100644 --- a/xrspatial/hydro/tests/test_flow_accumulation_d8.py +++ b/xrspatial/hydro/tests/test_flow_accumulation_d8.py @@ -427,3 +427,22 @@ def test_error_message_mentions_dask(self): ): with pytest.raises(MemoryError, match="dask"): flow_accumulation(agg) + + +# --------------------------------------------------------------------------- +# Degenerate-shape tests (issue #2713) +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("shape", [(1, 1), (1, 4), (4, 1)]) +def test_degenerate_shape(shape): + """Single-pixel and single-row/column input. + + Every cell is a pit (code 0) that drains only itself, so each cell + accumulates exactly 1 at the input shape. + """ + flow_dir = np.zeros(shape, dtype=np.float64) + agg = create_test_raster(flow_dir) + result = flow_accumulation(agg) + assert result.shape == shape + assert not np.isnan(result.data).any() + np.testing.assert_array_equal(result.data, 1.0) diff --git a/xrspatial/hydro/tests/test_flow_direction_d8.py b/xrspatial/hydro/tests/test_flow_direction_d8.py index da9686bc..33258a18 100644 --- a/xrspatial/hydro/tests/test_flow_direction_d8.py +++ b/xrspatial/hydro/tests/test_flow_direction_d8.py @@ -391,3 +391,21 @@ def test_valid_output_codes(): if np.isnan(v): continue assert v in VALID_CODES, f"Invalid code: {v}" + + +# --------------------------------------------------------------------------- +# Degenerate-shape tests (issue #2713) +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("shape", [(1, 1), (1, 4), (4, 1)]) +def test_degenerate_shape(shape): + """Single-pixel and single-row/column input. + + Every cell sits on the kernel boundary, so flow_direction returns + all NaN at the input shape. + """ + data = np.arange(np.prod(shape), dtype=np.float64).reshape(shape) + agg = create_test_raster(data) + result = flow_direction(agg) + assert result.shape == shape + assert np.isnan(result.data).all() diff --git a/xrspatial/hydro/tests/test_flow_length_d8.py b/xrspatial/hydro/tests/test_flow_length_d8.py index 5d35fcb3..a6c0b229 100644 --- a/xrspatial/hydro/tests/test_flow_length_d8.py +++ b/xrspatial/hydro/tests/test_flow_length_d8.py @@ -368,3 +368,22 @@ def test_numpy_equals_dask_cupy(self, direction): np.testing.assert_allclose( result_np.data, result_dc.data.compute().get(), equal_nan=True, rtol=1e-10) + + +# --------------------------------------------------------------------------- +# Degenerate-shape tests (issue #2713) +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("direction", ['downstream', 'upstream']) +@pytest.mark.parametrize("shape", [(1, 1), (1, 4), (4, 1)]) +def test_degenerate_shape(shape, direction): + """Single-pixel and single-row/column input. + + Every cell is a pit (code 0) with no downstream/upstream neighbour, + so flow length is zero at the input shape. + """ + flow_dir = np.zeros(shape, dtype=np.float64) + raster = _make_flow_dir_raster(flow_dir) + result = flow_length(raster, direction=direction) + assert result.shape == shape + np.testing.assert_array_equal(result.data, 0.0) diff --git a/xrspatial/hydro/tests/test_hand_d8.py b/xrspatial/hydro/tests/test_hand_d8.py index 68138856..401fa1bc 100644 --- a/xrspatial/hydro/tests/test_hand_d8.py +++ b/xrspatial/hydro/tests/test_hand_d8.py @@ -344,3 +344,23 @@ def test_cupy_huge_raster_raises(self): ): with pytest.raises(MemoryError, match="GPU working memory"): hand(fd_r, fa_r, el_r, threshold=1) + + +# --------------------------------------------------------------------------- +# Degenerate-shape tests (issue #2713) +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("shape", [(1, 1), (1, 4), (4, 1)]) +def test_degenerate_shape(shape): + """Single-pixel and single-row/column input. + + Every cell is a pit (code 0) and a stream (flow_accum >= threshold), + so each cell drains to itself and HAND is 0 at the input shape. + """ + fd = np.zeros(shape, dtype=np.float64) + fa = np.full(shape, 200.0, dtype=np.float64) + el = np.ones(shape, dtype=np.float64) + fd_r, fa_r, el_r = _make_hand_rasters(fd, fa, el) + result = hand(fd_r, fa_r, el_r, threshold=100) + assert result.shape == shape + np.testing.assert_allclose(result.data, 0.0)