Skip to content

Support 3D rasters, expose nodata, document target_resolution tuple in resample#1474

Merged
brendancol merged 2 commits intomainfrom
issue-1466
May 4, 2026
Merged

Support 3D rasters, expose nodata, document target_resolution tuple in resample#1474
brendancol merged 2 commits intomainfrom
issue-1466

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Closes #1466.

Three small API gaps in xrspatial.resample.resample(), all touching the same signature/dispatch surface, bundled into one PR.

Summary

  • 3D rasters (band, y, x) are now accepted. _validate_raster is called with ndim=(2, 3). The 3D dispatch loops over the leading dim, recurses into the 2D path per band, and stacks the results with xr.concat followed by a transpose back to the original layout. Non-spatial coordinates (e.g. band=[1, 2, 3]) are preserved.
  • New nodata=None keyword. Explicit value wins; otherwise it falls back to agg.attrs['_FillValue'], then agg.attrs['nodata']. Sentinel pixels are replaced with NaN before the resample using agg.where(agg != nodata), which dispatches naturally for numpy / cupy / dask backings. The output advertises _FillValue=nan when masking was applied.
  • target_resolution docstring updated to float or (float, float). The tuple form was already accepted at lines 801-803; only the documentation was wrong.

Test plan

  • All 75 tests in test_resample.py pass (62 existing + 13 new).
  • 3D raster shape (3, 8, 8) -> (3, 4, 4) at scale 0.5; band coord preserved.
  • 3D dask path (chunked over (1, 4, 4)) returns expected shape.
  • Per-band output of a 3D resample matches the 2D resample of each band.
  • nodata=-9999 on an int raster produces NaN where the input was -9999, finite floats elsewhere.
  • nodata resolved from _FillValue and from nodata attrs.
  • Explicit param overrides the attr.
  • Without any nodata, _FillValue is not added to the output (no behavior change for existing 2D float callers).
  • target_resolution=(2.0, 4.0) on an 8x8 raster gives shape (4, 2).
  • CuPy 3D test runs only when CUDA is available; verified locally to follow the same shape contract.

Notes on the 3D dispatch

  • The recursive call passes nodata=float('nan') so the per-band path doesn't try to re-mask attrs that were already masked at the 3D level. _apply_nodata_mask short-circuits on NaN sentinels.
  • xr.concat was the cleanest way to stack the per-band outputs while keeping the per-band coordinate. A transpose(*agg.dims) afterwards keeps dim order stable.
  • The identity fast-path (scale_y == scale_x == 1.0) sits above the 3D branch -- 3D identity copies pass through unchanged.

…n resample (#1466)

- _validate_raster now allows ndim=(2, 3); 3D inputs are dispatched per
  band via a recursive call and stacked back along the leading dim.
  Non-spatial coordinates (e.g. band=[1,2,3]) are preserved.
- Add nodata=None keyword. Explicit value wins; otherwise falls back to
  agg.attrs['_FillValue'] then agg.attrs['nodata']. Sentinels are
  replaced with NaN before the resample. Output advertises
  _FillValue=nan when masking was applied.
- Document target_resolution as float or (float, float) -- the tuple
  form was already accepted but undocumented.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 4, 2026
@brendancol
Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: brendancol <433221+brendancol@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented May 4, 2026

@copilot resolve the merge conflicts in this pull request

Merge conflicts resolved in commit 7d4833d. The only conflict was in test_resample.py — both sides added new test classes at the end of the file. The resolution keeps all tests from both sides: TestDaskAggregateInlined (from main) plus TestThreeDRasters, TestNodata, and TestTargetResolutionTuple (from this PR). resample.py auto-merged cleanly.

@brendancol brendancol merged commit 7ddb2e8 into main May 4, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support 3D rasters, expose nodata, document target_resolution tuple in resample

2 participants