diff --git a/.claude/sweep-documentation-state.csv b/.claude/sweep-documentation-state.csv index 84fe31d0c..c2178ce04 100644 --- a/.claude/sweep-documentation-state.csv +++ b/.claude/sweep-documentation-state.csv @@ -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 .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 diff --git a/xrspatial/convolution.py b/xrspatial/convolution.py index b1b08bfb7..a212a2acf 100644 --- a/xrspatial/convolution.py +++ b/xrspatial/convolution.py @@ -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) @@ -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): @@ -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) @@ -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. @@ -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 @@ -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) - + >>> convolved_agg array([[ nan, nan, nan, nan, nan, nan],