reproject: handle (band, y, x) 3-D inputs (#2182)#2189
Merged
Conversation
The per-chunk worker assumed the band axis was trailing -- it sliced ``source_data[r:, c:]`` and read ``window.shape[2]`` for the band count. A rasterio/rioxarray-style ``(band, y, x)`` input sliced the band/y axes instead of y/x and either crashed with a band coord-length mismatch or returned wrong-shape data. The entry point now transposes 3-D inputs to the canonical ``(y, x, band)`` layout before dispatch, runs the existing pipeline, then transposes the output back to the input's dim order so downstream rioxarray/rasterio code keeps working. Adds a ``TestReproject3DBandFirst`` class covering ``(band, y, x)`` inputs on the numpy, dask+numpy, cupy, and dask+cupy backends, plus a pixel-equality check that the two layouts agree.
brendancol
commented
May 20, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
PR Review: reproject handles (band, y, x) 3-D inputs (#2182)
Blockers
None.
Suggestions
None.
Nits
xrspatial/reproject/__init__.py:670: the_canonicaltuple is built with_band_dim_ineven when it isNone. The guard_band_dim_in is not None and _input_dims != _canonicalsaves the call, but a tuple containingNoneis a little odd to read. Building the tuple inside the guard would make the intent clearer.xrspatial/tests/test_reproject.py:3996:test_band_first_dask_matches_numpycalls_make_band_first_raster()three times. Caching the raster in a local would save two RNG passes (cosmetic; the test is fast either way).
What looks good
- The fix is localized to the entry point: transpose to canonical (y, x, band) before dispatch, transpose back after the output DataArray is built. Downstream workers and the per-block dask adapter are unchanged, which keeps the blast radius small.
- The set-equality check on the final transpose (
set(_input_dims) == set(result.dims)) is a defensive guard against crashing if the band-dim name ever differs between input and output. - Tests cover all four backends (numpy, dask, cupy, dask+cupy) with both layout-preservation and pixel-equality assertions.
xr.DataArray.transposeis metadata-only on dask arrays, so no extra materialization is introduced for the band-first dask path.
Checklist
- Algorithm matches reference/paper -- N/A (pure layout fix)
- All backends produce consistent results -- verified by test_band_first_matches_band_last and test_band_first_dask_matches_numpy
- NaN handling is correct -- unchanged from the (y, x, band) path
- Edge cases covered -- integer dtype roundtrip included
- Dask chunk boundaries handled -- lazy transpose preserves chunking semantics
- No premature materialization or unnecessary copies -- transpose is metadata-only
- Benchmark not needed -- no perf-sensitive code touched
- README feature matrix -- N/A (no new API)
- Docstrings -- existing docstring still accurate
…er (#2182) - Move the canonical (y, x, band) tuple construction inside the `_band_dim_in is not None` guard so the tuple never contains None. - Cache `_make_band_first_raster()` in `test_band_first_dask_matches_numpy` so the raster is built once instead of three times.
# Conflicts: # xrspatial/tests/test_reproject.py
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 #2182
Summary
source_data[r:, c:]and readwindow.shape[2]for the band count. A rasterio/rioxarray-style(band, y, x)input sliced the band/y axes instead of y/x and either crashed with a band coord-length mismatch or returned wrong-shape data.(y, x, band)layout before dispatch and transposes the output back to the input's dim order, so a(band, y, x)raster round-trips as(band, y, x).Backends covered
numpy, dask+numpy, cupy, dask+cupy. New tests run on all four (cupy/dask+cupy paths gated on GPU availability via
skipif).Test plan
pytest xrspatial/tests/test_reproject.py::TestReproject3DBandFirst-- 9 passedpytest xrspatial/tests/test_reproject.py-- 277 passed, no regressions(band, y, x)and(y, x, band)inputs produce the same data after a common transpose