Skip to content
Open
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
1 change: 1 addition & 0 deletions .claude/sweep-error-handling-state.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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)."
convolution,2026-07-02,,HIGH,1;2;3;4,"convolve_2d/convolution_2d skipped kernel + DataArray validation: None/1D/3D/list kernel -> numba TypingError, even kernel silently off-center (custom_kernel rejects it), numpy agg -> memoryview astype error. Fixed via _validate_kernel + _validate_raster; branch deep-sweep-error-handling-convolution-2026-07-02 pushed to fork; issue/PR create blocked by auto-mode, open from parent. MEDIUM(unfixed): annulus_kernel inner>outer -> cryptic np.pad 'index cant contain negative values'. LOW: circle_kernel cellsize=0 ZeroDivisionError, cellsize<0 cryptic linspace; calc_cellsize non-DataArray -> AttributeError attrs. cupy verified."
edge_detection,2026-07-18,3676,MEDIUM,3,"All 5 funcs validate via _validate_raster; convolve_2d validates boundary/kernel; observed failure messages name func/param/offending value, consistent across siblings. MEDIUM(fixed #3676): NaN input propagates to 3x3 footprint, undocumented in docstrings -> added Notes sections + exact-footprint test. LOW(unfixed): (0,0) raster diverges by backend (numpy empty result; dask ValueError 'overlapping depth 1 is larger than your array 0'; cupy CudaAPIError INVALID_VALUE) - engine-level in convolve_2d, adversarial input. LOW(unfixed): int->float32 promotion documented only in convolve_2d docstring. Battery executed on numpy/dask+numpy/cupy/dask+cupy (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."
pathfinding,2026-07-08,3649,CRITICAL,1;2;3;4,"CRITICAL: string barriers (['0']) silently ignored -> path crosses wall, no error/warning (numba float==unicode always False); scalar barriers=0 -> numba TypingError. HIGH: search_radius unvalidated: -1 silently all-NaN 'no path' (numpy+cupy), other geometries 'negative dimensions are not allowed'; float radius -> slice TypeError; via multi_stop -> misleading 'no path between waypoints'. MEDIUM: multi_stop_search missing dims check -> bare KeyError 'y' vs a_star ValueError; scalar start/goal -> 'float object is not subscriptable' in _get_pixel_id. All fixed: _validate_barriers/_validate_search_radius/_validate_point/_validate_surface_dims + tests. LOW (unfixed): (0,0)-size raster -> 'zero-size array to reduction' from calc_res. All 4 backends executed (CUDA present). Battery: numpy/dask/cupy/dask+cupy."
35 changes: 35 additions & 0 deletions xrspatial/edge_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ def sobel_x(agg, name='sobel_x', boundary='nan'):
-------
xarray.DataArray
Horizontal gradient with the same shape and backend as the input.

Notes
-----
NaN cells in the input propagate: every output cell whose 3x3
neighborhood (as extended by the boundary mode) contains a NaN
becomes NaN. With the default ``boundary='nan'``, the outer
one-cell ring of the output is also NaN.
"""
_validate_raster(agg, func_name='sobel_x', name='agg')
out = convolve_2d(agg.data, SOBEL_X, boundary)
Expand Down Expand Up @@ -79,6 +86,13 @@ def sobel_y(agg, name='sobel_y', boundary='nan'):
-------
xarray.DataArray
Vertical gradient with the same shape and backend as the input.

Notes
-----
NaN cells in the input propagate: every output cell whose 3x3
neighborhood (as extended by the boundary mode) contains a NaN
becomes NaN. With the default ``boundary='nan'``, the outer
one-cell ring of the output is also NaN.
"""
_validate_raster(agg, func_name='sobel_y', name='agg')
out = convolve_2d(agg.data, SOBEL_Y, boundary)
Expand Down Expand Up @@ -108,6 +122,13 @@ def laplacian(agg, name='laplacian', boundary='nan'):
-------
xarray.DataArray
Laplacian response with the same shape and backend as the input.

Notes
-----
NaN cells in the input propagate: every output cell whose 3x3
neighborhood (as extended by the boundary mode) contains a NaN
becomes NaN. With the default ``boundary='nan'``, the outer
one-cell ring of the output is also NaN.
"""
_validate_raster(agg, func_name='laplacian', name='agg')
out = convolve_2d(agg.data, LAPLACIAN_KERNEL, boundary)
Expand Down Expand Up @@ -137,6 +158,13 @@ def prewitt_x(agg, name='prewitt_x', boundary='nan'):
-------
xarray.DataArray
Horizontal gradient with the same shape and backend as the input.

Notes
-----
NaN cells in the input propagate: every output cell whose 3x3
neighborhood (as extended by the boundary mode) contains a NaN
becomes NaN. With the default ``boundary='nan'``, the outer
one-cell ring of the output is also NaN.
"""
_validate_raster(agg, func_name='prewitt_x', name='agg')
out = convolve_2d(agg.data, PREWITT_X, boundary)
Expand Down Expand Up @@ -166,6 +194,13 @@ def prewitt_y(agg, name='prewitt_y', boundary='nan'):
-------
xarray.DataArray
Vertical gradient with the same shape and backend as the input.

Notes
-----
NaN cells in the input propagate: every output cell whose 3x3
neighborhood (as extended by the boundary mode) contains a NaN
becomes NaN. With the default ``boundary='nan'``, the outer
one-cell ring of the output is also NaN.
"""
_validate_raster(agg, func_name='prewitt_y', name='agg')
out = convolve_2d(agg.data, PREWITT_Y, boundary)
Expand Down
7 changes: 5 additions & 2 deletions xrspatial/tests/test_edge_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,11 @@ def test_nan_in_input_propagates(self, func):
data[2, 3] = np.nan
agg = create_test_raster(data, backend='numpy')
result = func(agg, boundary='reflect')
# NaN should propagate to neighbors
assert np.isnan(result.data[2, 3])
# per the docstring: every output cell whose 3x3 neighborhood
# contains the NaN becomes NaN, and no other cell does
expected_nan = np.zeros((5, 6), dtype=bool)
expected_nan[1:4, 2:5] = True
np.testing.assert_array_equal(np.isnan(result.data), expected_nan)

@pytest.mark.parametrize('func', [sobel_x, sobel_y, laplacian, prewitt_x, prewitt_y])
def test_all_nan_input(self, func):
Expand Down
Loading