Skip to content

Commit 422e7c9

Browse files
authored
bump: add name= parameter and name the output DataArray (#3610)
1 parent 0fd856f commit 422e7c9

3 files changed

Lines changed: 27 additions & 5 deletions

File tree

.claude/sweep-api-consistency-state.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module,last_inspected,issue,severity_max,categories_found,notes
2+
bump,2026-07-02,3608,MEDIUM,1;2;3;5,"Sweep 2026-07-02 (deep-sweep-api-consistency-bump-2026-07-02). 3 MEDIUM, 1 LOW. Compared bump() against sibling synth generators perlin/worley/generate_terrain (all: agg first-positional, name= param, output named, attrs preserved). FIXED (issue #3608, PR #3610): (Cat 2/5) bump returned an unnamed DataArray with no name= param while all 3 siblings name their output; added keyword-only name='bump' set on both agg and width/height return paths. Bundled (Cat 3) count docstring said required int but signature is Optional[int]=None with width*height//10 default (capped 10M) -- corrected. Added test_bump_names_output_like_siblings; test_bump.py 19 pass incl cupy+dask+cupy (GPU host). DOCUMENTED not fixed (breaking): (Cat 1 MEDIUM) agg is keyword-only/last in bump but positional-first in siblings, so bump(some_dataarray) raises TypeError where perlin(some_dataarray) works; moving agg positional would break the historical bump(width,height) signature -- needs its own deprecation plan, not a clean shim. (Cat 3 LOW) height_func has no type hint and a freeform docstring type line; left alone per LOW=document-only. Not findings: Cat 4 no default drift within bump; Cat 5 bump is re-exported in __init__.py (no __all__ but matches module convention). attrs=dict(res=1) left untouched (metadata-sweep concern, and documented in the plot example)."
23
classify,2026-06-20,3398,MEDIUM,1;3,"Sweep 2026-06-20 (deep-sweep-api-consistency-classify-2026-06-20). 1 MEDIUM Cat 1 finding filed as #3398 and fixed on this branch. (MEDIUM Cat 1 positional-order drift) natural_breaks ordered its params (agg, num_sample, name, k) while the other two classifiers that take the same trio order them (agg, k, num_sample, name): quantile(agg, k=4, num_sample, name), maximum_breaks(agg, k=5, num_sample, name). So natural_breaks(raster, 5) silently set num_sample=5 instead of k=5. Fix reorders natural_breaks to (agg, k=5, num_sample=20000, name) and adds a _natural_breaks_legacy_order shim: when k= is a keyword AND a second positional is present (the only way pre-1.0 callers passed num_sample, since k was last and always keyword), the positional is treated as the old num_sample with a DeprecationWarning. Keeps the one example notebook call natural_breaks(raster, 20000, k=4) working. Bundled trivial Cat 3 fix in same PR: binary() was the only public classifier with no type hints -- added agg: xr.DataArray, name: Optional[str], -> xr.DataArray to match the other 9. Tests: test_natural_breaks_positional_k_matches_siblings (new positional k == keyword k) and test_natural_breaks_legacy_positional_num_sample_warns (legacy order warns + maps identically). Full test_classify.py (now 91) + test_validation.py pass. Cat 4 considered NOT a finding: quantile k=4 (quartiles) vs k=5 (quintiles) elsewhere is the documented PySAL/mapclassify convention, not drift. No Cat 2 return drift (all 10 publics return xr.DataArray/Dataset via @supports_dataset, coords/dims/attrs preserved). No Cat 5 orphan API (all 10 re-exported in __init__.py; no __all__ but consistent with module convention). Cross-cutting, notes only: first-arg agg (classify family) vs raster (reproject/rasterize/polygonize) is library-wide drift, out of per-module scope. cuda-validated: CUDA_AVAILABLE=True on this host; natural_breaks new order + legacy shim smoke-tested on numpy AND cupy entry points (both warn + remap), dataset path binds name correctly, binary verified on cupy."
34
focal,2026-06-10,3215;3216,MEDIUM,3;4,"Sweep 2026-06-10 (deep-sweep-api-consistency-focal-2026-06-10). 2 MEDIUM findings filed, fixed on branches -01/-02 off this one. (#3215, MEDIUM Cat 4 cross-backend default parity, branch -01) apply() default func=_calc_mean is an @ngjit CPU function but the cupy/dask+cupy paths launch func as a CUDA kernel via _focal_stats_func_cupy func[griddim, blockdim], so apply(cupy_agg, kernel) raises TypeError 'CPUDispatcher' object is not subscriptable (dask+cupy builds the graph and fails at compute). Prior 2026-05-29 sweep dispositioned this LOW as 'documented in the docstring', but the docstring covers explicit funcs -- the default itself is unusable on 2 of 4 backends. Fix: func=None sentinel resolved per backend (_calc_mean CPU, _focal_mean_cuda GPU), explicit-func behavior unchanged; same PR adds the missing name= param to the apply() docstring (signature has name='focal_apply'; mean/focal_stats/hotspots document theirs). (#3216, MEDIUM Cat 3, branch -02) hotspots() docstring lists 3 backends but dask_cupy_func=_hotspots_dask_cupy is dispatched and works; kernel param documented as binary ('values of 1 indicate the kernel') while hotspots accepts weighted kernels and the Gi* formula in the same docstring uses weights w_ij (apply/focal_stats reject non-binary via _validate_binary_kernel, hotspots deliberately does not). Docs-only fix. LOW documented, not fixed: among the 4 focal publics only mean() has @supports_dataset (Dataset-support drift; feature gap, not an API bug). Cross-cutting, notes only per template: emerging_hotspots(raster=), viewshed(raster=), calc_cellsize(raster) still use raster while focal standardized on agg with a DeprecationWarning shim (#2689/PR #2699); library-wide first-arg drift, belongs to those modules' sweeps. No Cat 1 in-module (agg canonical, raster alias warns, both-args raises). No Cat 2 return drift (mean/apply/hotspots 2D same-type, focal_stats 3D (stats,y,x) as documented). No Cat 5 orphan API (apply/focal_stats/hotspots documented in focal.rst autosummary and consumed via xrspatial.focal module path; only mean re-exported top-level; emerging_hotspots top-level vs hotspots module-level asymmetry noted, additive export would be a design call, not filed). cuda-validated: CUDA_AVAILABLE=True on this host; mean/apply/focal_stats/hotspots smoke-tested on cupy with kwarg parity; the apply default crash reproduced on GPU; hotspots weighted-kernel acceptance verified empirically."
45
geotiff,2026-07-02,3593,MEDIUM,3,"Re-sweep 2026-07-02 (deep-sweep-api-consistency-geotiff-2026-07-02); prior pass 2026-07-01 (#3593 -> merged c2bf13c0/#3596). No new API-consistency issues; prior findings re-confirmed. Public surface unchanged since prior pass: commits since 2026-07-01 touch geotiff (#3595/#3599 PAM overwrite, #3592/#3598 PAM docs, #3600 color_ramp streaming stats, #3588 isort, #3601/#3602 removed unused analytics module) but none alter open_geotiff (28 params) / to_geotiff (24 params) signatures. Verified this pass: 222 signature+contract tests pass (geotiff/tests/unit/test_signatures.py + parity/test_signature_contract.py, which enforces read-entry docstring/param parity and the release-contract tier table incl. the #3593 extra_tags[experimental] fix); all 3 deprecated read aliases emit DeprecationWarning empirically (name->default_name, mask_nodata->masked, mask_and_scale->unpack); xarray backend declares open_dataset_parameters=('filename_or_obj','drop_variables') and raises pointed ValueErrors for like=/bare coregister/auto_reproject. Cross-cutting, notes only (offender out of geotiff scope, not filed): reproject/merge use chunk_size= where geotiff/templates use chunks= (dask/xarray convention); open_geotiff kept name= as a deprecated alias for default_name= (rioxarray parity). cuda-validated: CUDA_AVAILABLE=True; cupy write round-trip + all 4 read paths (eager/gpu/dask/dask+gpu) accept identical public kwargs (masked=/gpu=/chunks=), no backend signature drift."

xrspatial/bump.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ def bump(width: int = None,
170170
height_func=None,
171171
spread: int = 1,
172172
*,
173-
agg: xr.DataArray = None) -> xr.DataArray:
173+
agg: xr.DataArray = None,
174+
name: str = 'bump') -> xr.DataArray:
174175
"""
175176
Generate a simple bump map to simulate the appearance of land
176177
features.
@@ -187,8 +188,9 @@ def bump(width: int = None,
187188
height : int, optional
188189
Total height, in pixels, of the image.
189190
Not required when ``agg`` is provided.
190-
count : int
191-
Number of bumps to generate.
191+
count : int, optional
192+
Number of bumps to generate. Defaults to ``width * height // 10``
193+
(capped at 10,000,000) when not provided.
192194
height_func : function which takes x, y and returns a height value
193195
Function used to apply varying bump heights to different
194196
elevations.
@@ -198,6 +200,10 @@ def bump(width: int = None,
198200
Template raster whose shape, chunks, and backend (NumPy, CuPy,
199201
Dask, Dask+CuPy) determine the output type. When provided,
200202
``width`` and ``height`` are inferred from ``agg.shape``.
203+
name : str, default='bump'
204+
Name of the output DataArray, for consistency with the other
205+
synthetic-terrain generators (``perlin``, ``worley``,
206+
``generate_terrain``).
201207
202208
Returns
203209
-------
@@ -408,7 +414,9 @@ def heights(locations, src, src_range, height = 20):
408414
return DataArray(out,
409415
coords=agg.coords,
410416
dims=agg.dims,
411-
attrs=dict(res=1))
417+
attrs=dict(res=1),
418+
name=name)
412419
else:
413420
bumps = _finish_bump(w, h, locs, heights, spread)
414-
return DataArray(bumps, dims=['y', 'x'], attrs=dict(res=1))
421+
return DataArray(bumps, dims=['y', 'x'], attrs=dict(res=1),
422+
name=name)

xrspatial/tests/test_bump.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ def test_bump_agg_infers_shape():
138138
np.testing.assert_array_equal(result.values, result2.values)
139139

140140

141+
def test_bump_names_output_like_siblings():
142+
"""bump() names its output DataArray, consistent with perlin/worley/
143+
generate_terrain (API-consistency sweep 2026-07-02)."""
144+
# default name
145+
result = bump(10, 10)
146+
assert result.name == 'bump'
147+
148+
# custom name, both with and without agg
149+
agg = xr.DataArray(np.zeros((10, 10)), dims=['y', 'x'])
150+
assert bump(agg=agg, name='ridges').name == 'ridges'
151+
assert bump(10, 10, name='ridges').name == 'ridges'
152+
153+
141154
def test_bump_decay_strongest_at_center_1102():
142155
"""Pixels adjacent to center should be taller than pixels far from center.
143156

0 commit comments

Comments
 (0)