Skip to content

feat(rust/sedona-raster): read a bare band array via BandStructArray - #1179

Closed
james-willis wants to merge 1 commit into
apache:mainfrom
james-willis:jw/band-struct-array-reader
Closed

feat(rust/sedona-raster): read a bare band array via BandStructArray#1179
james-willis wants to merge 1 commit into
apache:mainfrom
james-willis:jw/band-struct-array-reader

Conversation

@james-willis

Copy link
Copy Markdown
Contributor

What

Adds BandStructArray — a public reader for a bare band StructArray (one band per row, no enclosing raster). try_new(&StructArray) borrows it; get(i) returns a real Box<dyn BandRef>.

Why

#1161 made band rows writable without a raster envelope (BandArrayBuilder), but left no way to read them back: BandRefImpl is private, and the only public readers (RasterStructArray/RasterRefImpl) require an enclosing raster. This is the symmetric read half.

Without it, an out-of-tree type reusing the Band physical layout (e.g. a tensor/chunk value that carries its own georeferencing rather than a raster's) has to reimplement the view→byte-stride derivation downstream — including compose_byte_strides' overflow hardening — where it would silently drift from this crate.

How

Reading a band depends only on (band-level columns, row index) — nothing about it is raster-specific. So the column references and the construction logic move into an internal BandColumns: view decode + validation, 0-D and unknown-dtype rejection, InDb/OutDb resolution, byte-stride composition, and the view-buffer bounds check.

  • RasterRefImpl::band resolves its band's absolute row through the raster's flattened bands list offsets, then delegates.
  • BandStructArray::get delegates with the row index directly.

Neither reimplements any of it, so the two paths can't diverge.

Behavior change

None. RasterRefImpl::band is equivalent — proven directly by bare_band_and_in_raster_band_agree, which reads the same bands array both ways and asserts every accessor plus the full nd_buffer shape/strides/offset agree.

Verification

  • cargo test -p sedona-raster: 182 passed (178 existing and unmodified, +4 new — bare round-trip through BandArrayBuilder, non-identity view decode with correct strides/offset, the raster-vs-bare equivalence test, and rejection of an out-of-range index and a non-band struct).
  • sedona-raster-functions (254), sedona-raster-zarr (66), sedona-spatial-join-raster (13), sedona-testing (93): all pass, zero regressions.
  • clippy --all-targets -- -D warnings and fmt --all -- --check: clean.
  • sedona-raster-gdal not buildable on this machine (pre-existing local GDAL/gdal-sys version mismatch, fails identically on clean main); it uses only unchanged public API and doesn't implement BandRef.

apache#1161 made band rows *writable* without a raster envelope
(`BandArrayBuilder`), but left no way to read them back: `BandRefImpl`
is private and the only public readers (`RasterStructArray` /
`RasterRefImpl`) require an enclosing raster. This adds the symmetric
read half.

`BandStructArray::try_new(&StructArray)` borrows a bare band array --
one band per row, no `crs`/`transform`/`spatial_dims`/`spatial_shape`
and no per-raster `bands` list -- and `get(i)` returns a real
`Box<dyn BandRef>`.

The band read path is *shared*, not duplicated. Reading a band depends
only on (band-level columns, row index) -- nothing about it is
raster-specific -- so the column references plus the construction logic
move to a new internal `BandColumns`: view decode and validation,
0-D and unknown-dtype rejection, InDb/OutDb resolution, byte-stride
composition via `compose_byte_strides`, and the view-buffer bounds
check. `RasterRefImpl::band` now resolves its band's absolute row
through the raster's flattened `bands` list offsets and delegates;
`BandStructArray::get` delegates with the row index directly. Neither
reimplements any of it, so a band cannot drift between the two paths.

This is what lets an out-of-tree type that reuses the Band physical
layout (e.g. a tensor/chunk value carrying its own georeferencing
rather than a raster's) read its data back through the same
already-reviewed stride machinery instead of copying it downstream,
where it would silently diverge -- including the overflow hardening
`compose_byte_strides` carries.

No public behavior change: `RasterRefImpl::band` is byte-for-byte
equivalent (proven directly by `bare_band_and_in_raster_band_agree`,
which reads the *same* bands array both ways and asserts every accessor
plus the full `nd_buffer` shape/strides/offset match).

Verification:
- cargo test -p sedona-raster: 182 passed (178 existing, unmodified, +4
  new: bare round-trip through BandArrayBuilder, non-identity view
  decode with correct strides/offset, the raster-vs-bare equivalence
  test, and rejection of an out-of-range index and a non-band struct).
- sedona-raster-functions (254), sedona-raster-zarr (66),
  sedona-spatial-join-raster (13), sedona-testing (93): all pass, no
  regressions.
- clippy --all-targets -- -D warnings and fmt --all -- --check: clean.
@github-actions
github-actions Bot requested a review from paleolimbot August 19, 2026 19:54
@james-willis
james-willis marked this pull request as draft August 19, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant