Commit 6bf9270
committed
Fix COG cubic overview poisoned by nodata sentinel (#1623)
`to_geotiff(cog=True, overview_resampling='cubic', nodata=<finite>)` on a
float raster with NaN regions produced overview pixels with severe
ringing near nodata borders. Same class of bug as #1613, but for the
`cubic` branch: the writer rewrites NaN to the finite sentinel before
reduction, then `_block_reduce_2d(method='cubic')` handed the
sentinel-poisoned array straight to `scipy.ndimage.zoom(order=3)`. The
cubic spline blended the sentinel into neighbouring cells (values like
1133.44 and -10290.08 appeared where the source data was a constant
100).
Fix: when `nodata` is supplied on a float dtype and the sentinel is
present in the input, mask sentinel to NaN, run cubic with
`prefilter=False` so a single NaN does not poison the entire row/column
(the default B-spline prefilter is global), then rewrite any NaN in the
result back to the sentinel. `prefilter=False` only fires when a
sentinel is actually found in the input, so the default cubic semantics
still apply to inputs without nodata.
GPU side: `_block_reduce_2d_gpu` previously raised `ValueError` on
`method='cubic'`. Added a CPU fallback the same way the helper already
handles `mode`, so the GPU writer path produces byte-equivalent
overviews to the CPU writer when cubic + nodata are combined.
`GPU_OVERVIEW_METHODS` now includes `'cubic'`.
12 regression tests in `test_cog_cubic_overview_nodata_1623.py`:
helper no-ringing, poisoning repro, no-nodata path unchanged, end-to-end
COG round-trip, GPU fallback, CPU/GPU byte-match, +/-inf nodata
masking, NaN-sentinel no-op, and the `GPU_OVERVIEW_METHODS` contract.
All 1256 existing geotiff tests still pass.
Found by deep-sweep-accuracy 2026-05-11. State CSV updated with the
pass-16 record.1 parent 9a5f55e commit 6bf9270
4 files changed
Lines changed: 343 additions & 8 deletions
File tree
- .claude
- xrspatial/geotiff
- tests
0 commit comments