diff --git a/.claude/sweep-style-state.csv b/.claude/sweep-style-state.csv index 1e03170ce..d47002958 100644 --- a/.claude/sweep-style-state.csv +++ b/.claude/sweep-style-state.csv @@ -1,6 +1,7 @@ module,last_inspected,issue,severity_max,categories_found,notes geotiff,2026-05-27,2481,HIGH,1;3;4,"Bundled 387 flake8 + ~30 isort fixes since #2285/#2430. F401 x9, F811 x6, F841 x3. E501 x250 (mostly wrapped, 3 file-scope imports keep noqa: E402+E501). E252 x62, blank-line cluster, E128/E127 indents. importorskip imports use # noqa: E402. Cat 5 grep clean." polygonize,2026-05-27,2534,HIGH,1;3;4,"F401 line 58 (is_cupy_array unused, not re-exported). E127 lines 83/88 (overload continuation indent in generated_jit). isort: 5-line .utils import block collapses to one line at 100-char limit. Cat 2 clean. Cat 5 grep clean." +proximity,2026-05-29,2725,HIGH,1;3;4;5,"F841 line 1274 original_chunks dead local in unbounded dask+cupy branch (refactor leftover). Cat 5 mutable default target_values: list = [] in proximity/allocation/direction -> None sentinel, normalized to [] in body (never mutated, behaviour preserved). E128 line 291 np.where continuation under-indent in _vectorized_calc_direction. isort: re-sorted xrspatial import block + blank line after inline import cupy as cp. flake8+isort clean after fix; 69 proximity tests pass + new parametrized regression test. Pre-existing E127 (test_proximity.py 726/752) + test-file isort drift left untouched (out of module scope)." rasterize,2026-05-27,2503,HIGH,1;3,F401 line 15 + F811 line 1193 (paired: local import warnings shadowed unused module-level import); E306 line 1775 (nested @cuda.jit). isort clean. Cat 5 grep clean. Fix in PR #2507. resample,2026-05-27,2543,MEDIUM,4,isort drift only: 4 multi-line parenthesised imports collapsed to single/one-per-line under line_length=100 (top-of-file scipy.ndimage + xrspatial.utils; local cupyx imports in _nan_aware_interp_cupy and _interp_block_cupy); two blank-line nits after import math in _run_dask_numpy/_run_dask_cupy. flake8 clean. Cat 5 grep clean. 169 resample tests pass. zonal,2026-05-27,2522,HIGH,1;3;4,"F401 not_implemented_func (line 42, only present on import line). E501 line 455 (dd.concat one-liner, 117 chars) wrapped across 3 lines. isort: consolidated xrspatial.utils block (merged has_dask_array, dropped not_implemented_func, alphabetised, trimmed extra blank line). Cat 5 grep clean. 125 zonal tests pass." diff --git a/xrspatial/proximity.py b/xrspatial/proximity.py index 76750c527..4d6481e16 100644 --- a/xrspatial/proximity.py +++ b/xrspatial/proximity.py @@ -24,13 +24,10 @@ class cupy(object): ndarray = False -from xrspatial.pathfinding import _available_memory_bytes -from xrspatial.utils import ( - _validate_raster, - cuda_args, get_dataarray_resolution, has_cuda_and_cupy, - is_cupy_array, is_dask_cupy, ngjit, -) from xrspatial.dataset_support import supports_dataset +from xrspatial.pathfinding import _available_memory_bytes +from xrspatial.utils import (_validate_raster, cuda_args, get_dataarray_resolution, + has_cuda_and_cupy, is_cupy_array, is_dask_cupy, ngjit) EUCLIDEAN = 0 GREAT_CIRCLE = 1 @@ -288,7 +285,7 @@ def _vectorized_calc_direction(x1, x2, y1, y2): dy = y2 - y1 d = np.arctan2(-dy, dx) * 57.29578 result = np.where(d < 0, 90.0 - d, - np.where(d > 90.0, 360.0 - d + 90.0, 90.0 - d)) + np.where(d > 90.0, 360.0 - d + 90.0, 90.0 - d)) result[(x1 == x2) & (y1 == y2)] = 0.0 return result.astype(np.float32) @@ -1269,9 +1266,9 @@ def _process_dask(raster, xs, ys): was_dask_cupy = has_cuda_and_cupy() and is_dask_cupy(raster) if was_dask_cupy: import cupy as cp + # Unbounded: convert to dask+numpy for KDTree/line-sweep # (KDTree is CPU-only; O(N log T) beats brute-force O(NT)) - original_chunks = raster.data.chunks raster = raster.copy( data=raster.data.map_blocks( lambda x: x.get(), dtype=raster.dtype, @@ -1344,7 +1341,7 @@ def proximity( raster: xr.DataArray, x: str = "x", y: str = "y", - target_values: list = [], + target_values: list = None, max_distance: float = np.inf, distance_metric: str = "EUCLIDEAN", ) -> xr.DataArray: @@ -1460,6 +1457,9 @@ def proximity( * x (x) int64 0 1 2 3 4 """ + if target_values is None: + target_values = [] + _validate_raster(raster, func_name='proximity', name='raster') proximity_img = _process( @@ -1487,7 +1487,7 @@ def allocation( raster: xr.DataArray, x: str = "x", y: str = "y", - target_values: list = [], + target_values: list = None, max_distance: float = np.inf, distance_metric: str = "EUCLIDEAN", ): @@ -1600,6 +1600,9 @@ def allocation( * x (x) int64 0 1 2 3 4 """ + if target_values is None: + target_values = [] + _validate_raster(raster, func_name='allocation', name='raster') allocation_img = _process( @@ -1627,7 +1630,7 @@ def direction( raster: xr.DataArray, x: str = "x", y: str = "y", - target_values: list = [], + target_values: list = None, max_distance: float = np.inf, distance_metric: str = "EUCLIDEAN", ): @@ -1746,6 +1749,9 @@ def direction( * x (x) int64 0 1 2 3 4 """ + if target_values is None: + target_values = [] + _validate_raster(raster, func_name='direction', name='raster') direction_img = _process( diff --git a/xrspatial/tests/test_proximity.py b/xrspatial/tests/test_proximity.py index 41aa69af9..8b2f99808 100644 --- a/xrspatial/tests/test_proximity.py +++ b/xrspatial/tests/test_proximity.py @@ -961,3 +961,26 @@ def test_no_scipy_dask_unbounded_memory_guard(): proximity(raster, x='lon', y='lat') finally: prox_mod.cKDTree = original_ckdtree + + +@pytest.mark.parametrize("func", [proximity, allocation, direction]) +def test_target_values_none_default_matches_empty_list(func): + # target_values default switched from [] to a None sentinel; passing + # None (the default) must behave exactly like passing an empty list. + data = np.asarray([[0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0.], + [0., 0., 0., 0., 0.], + [0., 0., 2., 0., 0.], + [0., 0., 0., 0., 0.]]) + n, m = data.shape + raster = xr.DataArray(data, dims=['y', 'x']) + raster['y'] = np.arange(n)[::-1] + raster['x'] = np.arange(m) + + default_result = func(raster) + explicit_result = func(raster, target_values=[]) + + np.testing.assert_array_equal( + np.nan_to_num(default_result.data), + np.nan_to_num(explicit_result.data), + )