Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions .claude/sweep-error-handling-state.csv
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
module,last_inspected,issue,severity_max,categories_found,notes
geotiff,2026-07-02,3604,MEDIUM,2;4,"to_geotiff 0D/1D DataArray raised opaque IndexError from _coords.py coords_to_transform (dims[-2]) instead of clean 'Expected 2D or 3D' ValueError; numpy path + 4D DataArray already clean. Fixed via early ndim guard before dispatch (eager/vrt/gpu) + 3 tests; PR #3604. Read-side param validation + typed-error hierarchy + allow_rotated/allow_invalid_nodata VRT+chunked opt-in threading verified clean (CUDA available, GPU paths run). gh issue create blocked by auto-mode; PR opened. Cat 2+4."
module,last_inspected,issue,severity_max,categories_found,notes
bump,2026-07-02,,HIGH,1;2;3;4,"bump() agg template unvalidated: plain ndarray -> ArrayTypeFunctionMapping 'Unsupported Array Type'; 3D/1D DataArray -> 'too many values to unpack'. count/spread unvalidated (internal numpy errors / silent). Added _validate_raster(agg,ndim=2) + _validate_scalar for count,spread. all 4 backends verified (CUDA present)."
geotiff,2026-07-02,3604,MEDIUM,2;4,"to_geotiff 0D/1D DataArray raised opaque IndexError from _coords.py coords_to_transform (dims[-2]) instead of clean 'Expected 2D or 3D' ValueError; numpy path + 4D DataArray already clean. Fixed via early ndim guard before dispatch (eager/vrt/gpu) + 3 tests; PR #3604. Read-side param validation + typed-error hierarchy + allow_rotated/allow_invalid_nodata VRT+chunked opt-in threading verified clean (CUDA available, GPU paths run). gh issue create blocked by auto-mode; PR opened. Cat 2+4."
12 changes: 10 additions & 2 deletions xrspatial/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class cupy(object):
except ImportError:
da = None

from xrspatial.utils import (ArrayTypeFunctionMapping, _validate_scalar, has_cuda_and_cupy,
is_cupy_array, ngjit)
from xrspatial.utils import (ArrayTypeFunctionMapping, _validate_raster, _validate_scalar,
has_cuda_and_cupy, is_cupy_array, ngjit)

# Upper bound on bump count to prevent accidental OOM from the default
# w*h//10 heuristic. 16 bytes per bump (int32 loc pair + float64 height),
Expand Down Expand Up @@ -344,7 +344,15 @@ def heights(locations, src, src_range, height = 20):
Description: Example Bump Map
units: km
"""
_validate_scalar(spread, func_name='bump', name='spread',
dtype=int, min_val=0)
if count is not None:
_validate_scalar(count, func_name='bump', name='count',
dtype=int, min_val=0)

if agg is not None:
_validate_raster(agg, func_name='bump', name='agg', ndim=2,
numeric=False)
h, w = agg.shape
else:
_validate_scalar(width, func_name='bump', name='width',
Expand Down
51 changes: 51 additions & 0 deletions xrspatial/tests/test_bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,54 @@ def test_bump_dask_bypasses_raster_guard():
result = bump(agg=agg, count=10, spread=0)
assert result.shape == (100_000, 100_000)
assert isinstance(result.data, da.Array)


# --- Input-validation regression tests ---

def test_bump_agg_must_be_dataarray():
"""A plain ndarray template raises a clear TypeError naming `agg`,
not an inscrutable 'Unsupported Array Type' from the dispatcher."""
import pytest

with pytest.raises(TypeError, match=r"bump\(\): `agg` must be an "
r"xarray\.DataArray"):
bump(agg=np.zeros((10, 10)))


def test_bump_agg_must_be_2d():
"""A 3D or 1D template raises a clear ValueError naming `agg` and the
2D requirement, not 'too many values to unpack'."""
import pytest

with pytest.raises(ValueError, match=r"bump\(\): `agg` must be 2D"):
bump(agg=xr.DataArray(np.zeros((3, 10, 10)), dims=['b', 'y', 'x']))
with pytest.raises(ValueError, match=r"bump\(\): `agg` must be 2D"):
bump(agg=xr.DataArray(np.zeros(10), dims=['x']))


def test_bump_count_validated():
"""`count` gets the same clean validation as width/height instead of
surfacing raw numpy errors."""
import pytest

with pytest.raises(ValueError, match=r"bump\(\): `count` must be >= 0"):
bump(width=10, height=10, count=-5)
with pytest.raises(TypeError, match=r"bump\(\): `count` must be int"):
bump(width=10, height=10, count=5.0)


def test_bump_spread_validated():
"""`spread` is documented as int; negative and non-int values raise
instead of being silently ignored."""
import pytest

with pytest.raises(ValueError, match=r"bump\(\): `spread` must be >= 0"):
bump(width=10, height=10, spread=-3)
with pytest.raises(TypeError, match=r"bump\(\): `spread` must be int"):
bump(width=10, height=10, spread=2.5)


def test_bump_spread_zero_still_allowed():
"""spread=0 (single-pixel bumps) must remain valid."""
result = bump(width=10, height=10, count=5, spread=0)
assert result.shape == (10, 10)
Loading