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-documentation-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,doc_coverage
classify,2026-06-25,3506,MEDIUM,1;3,"Cat3: reclassify (numpy/dask/cupy blocks) + equal_interval example outputs were stale/wrong, binary used np.nan in array repr; corrected to actual output (tests confirm code is correct). Cat1: added missing Examples to std_mean, head_tail_breaks, percentiles, maximum_breaks, box_plot. Fixed in deep-sweep-documentation-classify-2026-06-25 (PR for #3506). Cat2 natural_breaks num_sample-None omission already tracked in #3501 (left alone). All 10 public funcs listed in reference/classification.rst (no Cat4 gap). CUDA available: ran numpy examples; cupy/dask reprs reviewed statically.",10/10
convolution,2026-07-02,,MEDIUM,1;3;5,"Deep-sweep 2026-07-02 (deep-sweep-documentation-convolution). Public API per reference/focal.rst 'Focal Statistics': convolution_2d, annulus_kernel, calc_cellsize, circle_kernel, custom_kernel (via xrspatial.focal); convolve_2d listed in metadata public_funcs but not in any reference page. Cat1 MEDIUM: custom_kernel had a one-line stub (added Parameters/Returns/Examples); convolve_2d had NO docstring (added numpydoc). Cat5 MEDIUM: convolution_2d agg backend line omitted Dask+CuPy (code dispatches _convolve_2d_dask_cupy) and had typos 'to processed'/'CuPybacked' -> fixed to name all 4 backends. Cat3 MEDIUM: annulus_kernel example print used comma-array format + stray trailing ')' (real print output has no commas/paren) -> corrected to measured output; convolution_2d dask .compute() example claimed dtype=float32 but np.ones input stays float64 -> removed label; cupy type repr 'cupy.core.core.ndarray' -> 'cupy.ndarray'. Cat3 LOW (documented, not fixed): calc_cellsize km example writes output line as '>>> (1000.0, 1000.0)' (stray prompt on an output line; harmless, no error). Cat2/Cat4 clean: params match signatures; all 5 reference-listed funcs present, no stale/dup entries. Verified: numpy+dask+cupy examples executed (CUDA available on host, cupy example ran), values match; test_convolution.py 6 pass; flake8 unchanged (1 pre-existing F401 not_implemented_func baseline).",6/6
fire,2026-06-25,,MEDIUM,1;5,"all 7 public funcs (dnbr, rdnbr, burn_severity_class, fireline_intensity, flame_length, rate_of_spread, kbdi) lacked Examples section (Cat1 MEDIUM) and backend-support note (Cat5 MEDIUM); fixed in deep-sweep-documentation-fire-2026-06-25-01; repo issues disabled so no issue number; examples run and outputs match numpy backend; all 7 listed in reference/fire.rst; no Cat2/Cat3/Cat4 issues",7/7
flood,2026-06-25,,HIGH,1;4;5,"Cat4 HIGH: vegetation_roughness, vegetation_curve_number, flood_depth_vegetation public but absent from reference/flood.rst; Cat1 MEDIUM: no Examples on any of 7 public funcs; Cat5 MEDIUM: backend support undocumented (all 4 backends) + NaN propagation undocumented for curve_number_runoff/travel_time. Fixed in deep-sweep-documentation-flood-2026-06-25: added 3 rst entries, Examples+backend Notes to all 7 funcs (examples executed OK on CUDA host), NaN notes. PR #3502 opened with the fix; gh issue create blocked by auto-mode classifier so no issue number.",7/7
geotiff,2026-07-01,3592,MEDIUM,5,"Cat5 MEDIUM: PAM categorical sidecar behavior undocumented -- open_geotiff merges category_names/category_colors from <src>.aux.xml on all 4 read paths and to_geotiff writes the sidecar keyed on attrs alone (no kwarg); absent from both docstrings and attrs_contract.rst. Doc-only fix on deep-sweep-documentation-geotiff-2026-07-01: open_geotiff Notes paragraph, to_geotiff description paragraph, 2 attrs_contract rows; issue #3592. Cat1/2/3/4 clean: 25 signature-contract parity tests pass incl. tier table vs SUPPORTED_FEATURES; examples executed copy-paste (plain/cog/vrt/color_ramp) and gpu=True->cupy / chunks->dask / gpu+chunks->dask+cupy verified on CUDA host; both funcs in reference/geotiff.rst autosummary, no dupes. LOW noted not fixed: categorical RAT + color_ramp symbology sidecars have no SUPPORTED_FEATURES key or release-contract row (runtime-constant change, out of doc-only scope).",2/2
Expand Down
90 changes: 73 additions & 17 deletions xrspatial/convolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,20 +281,20 @@ def annulus_kernel(cellsize_x, cellsize_y, outer_radius, inner_radius):
>>> # Create Kernel
>>> kernel = annulus_kernel(1, 1, 3, 1)
>>> print(kernel)
[[0., 0., 0., 1., 0., 0., 0.],
[0., 1., 1., 1., 1., 1., 0.],
[0., 1., 1., 0., 1., 1., 0.],
[1., 1., 0., 0., 0., 1., 1.],
[0., 1., 1., 0., 1., 1., 0.],
[0., 1., 1., 1., 1., 1., 0.],
[0., 0., 0., 1., 0., 0., 0.]]
[[0. 0. 0. 1. 0. 0. 0.]
[0. 1. 1. 1. 1. 1. 0.]
[0. 1. 1. 0. 1. 1. 0.]
[1. 1. 0. 0. 0. 1. 1.]
[0. 1. 1. 0. 1. 1. 0.]
[0. 1. 1. 1. 1. 1. 0.]
[0. 0. 0. 1. 0. 0. 0.]]
>>> kernel = annulus_kernel(1, 2, 5, 2)
>>> print(kernel)
[[0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
[0., 1., 1., 1., 1., 0., 1., 1., 1., 1., 0.],
[1., 1., 1., 0., 0., 0., 0., 0., 1., 1., 1.],
[0., 1., 1., 1., 1., 0., 1., 1., 1., 1., 0.],
[0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])
[[0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 1. 1. 1. 1. 0. 1. 1. 1. 1. 0.]
[1. 1. 1. 0. 0. 0. 0. 0. 1. 1. 1.]
[0. 1. 1. 1. 1. 0. 1. 1. 1. 1. 0.]
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]
"""
# Get the two circular kernels for the annulus
kernel_outer = circle_kernel(cellsize_x, cellsize_y, outer_radius)
Expand All @@ -317,7 +317,35 @@ def annulus_kernel(cellsize_x, cellsize_y, outer_radius, inner_radius):

def custom_kernel(kernel):
"""
Validates a custom kernel. If the kernel is valid, returns itself.
Validates a custom convolution kernel. If the kernel is valid, returns
it unchanged; otherwise raises ValueError. A valid kernel is a 2D NumPy
array with an odd number of rows and columns.

Parameters
----------
kernel : numpy.ndarray
2D array with an odd number of rows and columns.

Returns
-------
kernel : numpy.ndarray
The input kernel, unchanged.

Examples
--------
.. sourcecode:: python

>>> import numpy as np
>>> from xrspatial.convolution import custom_kernel
>>> kernel = custom_kernel(np.array([
... [0, 1, 0],
... [1, 1, 1],
... [0, 1, 0],
... ]))
>>> kernel
array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]])
"""

if not isinstance(kernel, np.ndarray):
Expand Down Expand Up @@ -478,6 +506,33 @@ def _convolve_2d_dask_cupy(data, kernel, boundary='nan'):


def convolve_2d(data, kernel, boundary='nan'):
"""
Applies a 2D convolution kernel to a raw array. This is the array-level
engine behind :func:`convolution_2d`; it takes and returns a raw array
rather than an ``xarray.DataArray``. Edges where the kernel would extend
beyond the array are handled per the ``boundary`` mode.

Parameters
----------
data : numpy.ndarray, cupy.ndarray, or dask.array.Array
2D array of values to convolve. Integer inputs are promoted to at
least float32.
kernel : numpy.ndarray
Impulse kernel with an odd number of rows and columns.
boundary : str, default='nan'
How to handle edges where the kernel extends beyond the array.
``'nan'`` -- fill missing neighbours with NaN (default).
``'nearest'`` -- repeat edge values.
``'reflect'`` -- mirror at boundary.
``'wrap'`` -- periodic / toroidal.

Returns
-------
out : same array type as ``data``
2D array of convolved values with the same shape as ``data``. Under
``boundary='nan'`` the outer ring of width ``kernel_size // 2`` is
filled with NaN.
"""
# Wrap raw arrays so _validate_raster can check dtype/ndim consistently
# across numpy, cupy, and dask backends before the kernel runs.
agg = xr.DataArray(data)
Expand All @@ -504,8 +559,9 @@ def convolution_2d(agg, kernel, name='convolution_2d', boundary='nan'):
Parameters
----------
agg : xarray.DataArray
2D array of values to processed. Can be NumPy backed, CuPybacked,
or Dask with NumPy backed DataArray.
2D array of values to be processed. Can be a NumPy-backed,
CuPy-backed, Dask-with-NumPy-backed, or Dask-with-CuPy-backed
DataArray.
kernel : array-like object
Impulse kernel, determines area to apply impulse function for
each cell.
Expand Down Expand Up @@ -581,7 +637,7 @@ def convolution_2d(agg, kernel, name='convolution_2d', boundary='nan'):
array([[nan, nan, nan, nan, nan, nan],
[nan, 4., 4., 4., 4., nan],
[nan, 4., 4., 4., 4., nan],
[nan, nan, nan, nan, nan, nan]], dtype=float32)
[nan, nan, nan, nan, nan, nan]])

convolution_2d() works with CuPy backed DataArray.
.. sourcecode:: python
Expand All @@ -604,7 +660,7 @@ def convolution_2d(agg, kernel, name='convolution_2d', boundary='nan'):
Dimensions without coordinates: dim_0, dim_1
>>> convolved_agg = convolution_2d(raster_cupy, kernel)
>>> type(convolved_agg.data)
<class 'cupy.core.core.ndarray'>
<class 'cupy.ndarray'>
>>> convolved_agg
<xarray.DataArray 'convolution_2d' (dim_0: 4, dim_1: 6)>
array([[ nan, nan, nan, nan, nan, nan],
Expand Down
Loading