Recompute res attribute for downsampled preview() output#3570
Merged
Conversation
preview() rebuilds the x/y coords for the smaller grid but copied attrs verbatim, so attrs['res'] still described the full-resolution input. Recompute res from the output coordinate spacing before returning, for every method and backend. Leave it absent when the input had no res.
brendancol
commented
Jun 29, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: Recompute res attribute for downsampled preview() output
Blockers
None.
Suggestions
preview()docstring (xrspatial/preview.py:350-353) still says the output has "updated coordinates" but not updated resolution. Worth a half-sentence so readers knowresnow tracks the downsampled grid.
Nits
_res_from_coords(xrspatial/preview.py:285-300) reads spacing from the first two coordinates only. That is exact for the uniform gridspreviewproduces (interp over a linspace, or a strided isel), so it is fine here — just noting it assumes uniform spacing.
Observations (not action items)
nearestreports a slightly differentresthanmean/bilinear(5.0 vs 5.115 in my checks). That traces to how each path builds coords:neareststrides (spacing = factor x original) while the reduce/bilinear paths interpolate across the full original extent. The PR faithfully reflects whatever coords each path emits; the coord-construction difference predates this change.- If an input carries
resbut no x/y coords, the recompute is skipped andresstays stale. That input is self-contradictory (res describes coord spacing) and there is no coordinate to measure, so leaving it alone is the right call.
What looks good
- One recompute point after the second-pass refinement covers all six methods and all four backends, instead of patching each helper.
- Builds a fresh attrs dict, so the input is not mutated (covered by a test).
- Only touches
reswhen it was present and measurable; absent-res inputs and no-op passthroughs keep their original attrs, both tested. - Tests assert against the returned array's real coordinate spacing rather than a hardcoded number, and confirm the stale source value does not survive.
Checklist
- Result reflects the actual output coordinates
- All implemented backends produce consistent results (numpy/cupy/dask+numpy/dask+cupy; bilinear+dask+cupy excluded by a pre-existing unrelated bug)
- No input mutation
- Edge cases covered (passthrough, absent res, dask refinement)
- No premature materialization or unnecessary copies
- Docstrings present (minor wording suggestion above)
- No README/benchmark change needed (bug fix, no API or perf change)
brendancol
commented
Jun 29, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Follow-up review (after docstring commit 1227f85)
Re-checked the diff after the follow-up commit.
- Suggestion from the first pass (document the
resrecompute) — fixed. Thepreview()Returns section now statesattrs['res']is recomputed from the output spacing when the input carried ares. - Nit (first-two-coords spacing) and the two observations — left as written; they describe correct, intended behavior, not defects.
No new findings. flake8 clean on preview.py; the 17 res tests still pass. No blockers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3569.
preview()downsamples a raster and rebuilds thex/ycoordinates for the smaller grid, but it copied the input'sattrsverbatim. Soattrs['res']kept describing the original full-resolution grid, and any downstream code readingresfor pixel-size math or georeferencing got the wrong value.Change
_res_from_coords()inxrspatial/preview.pyto read the pixel size off the output coordinate spacing (positive magnitude, matching how templates and terrain storeres).resonce, right beforepreview()returns, after the second-pass refinement has settled the final coordinates. This covers every method (mean, median, max, min, nearest, bilinear) and all four backends in one place.reswhen the input already had one, and only when both axes have at least two coordinates. A raster withoutresstays without it; a no-op passthrough keeps its original.Backends
numpy, cupy, dask+numpy, dask+cupy — verified on all four. The one combination not covered is
dask+cupy+bilinear, which already fails before this change in_zoom_block(scipyzoomon a cupy block); that is a separate pre-existing bug, untouched here.Test plan
resmatches the returned array's coordinate spacing for all six methodsresdoes not survive a downsampleattrsis not mutatedresresstays withoutrestest_preview.pysuite: 59 passed