Problem
RasterBuilder only supports constructing rasters/bands field-by-field (start_raster_nd, start_band_nd). Any consumer that wants to produce an output raster that's mostly a copy of an input — overriding one field — has to manually destructure every field and re-append it. That's fragile: the copy is only as complete as the author remembered to make it, and it silently rots as the band/raster schema grows.
RS_EnsureLoaded (rust/sedona-raster-functions/src/rs_ensure_loaded.rs) is the cautionary case. It's a copy-with-substitution — output = input raster, except OutDb bands get their data column filled in — implemented as a hand rebuild:
let dim_names = band.dim_names()...;
let source_shape = band.raw_source_shape().to_vec();
let data_type = band.data_type();
let nodata = band.nodata()...;
let outdb_uri = band.outdb_uri()...;
let outdb_format = band.outdb_format()...;
builder.start_band_nd(name, &dim_names, &source_shape, data_type, nodata, outdb_uri, outdb_format)?;
This list is missing band.view() — so the rebuild silently drops non-identity views. (Latent today: RasterRefImpl::band() rejects non-identity views, so they're unconstructible — but the moment view composition lands, this rebuild corrupts viewed rasters. Tracked separately.) Any field added to bands in the future hits the same trap.
Proposed API
Copy-everything-by-default, override what you're changing:
// raster-level: transform, spatial_dims, spatial_shape, crs
builder.copy_raster_from(&raster)?;
for band_idx in 0..raster.num_bands() {
let band = raster.band(band_idx)?;
// copies name, dim_names, source_shape, view, dtype, nodata,
// outdb_uri, outdb_format — complete by construction
builder.copy_band_from(&band)?; // copies metadata + data
// ...or copy metadata and substitute the data:
// builder.copy_band_from_with_data(&band, resolved_bytes)?;
}
The exact override surface (a with_data-style follow-up vs. a metadata-only copy + separate data write) is open for design. The invariant that matters: a copy stays complete as the schema grows, so consumers like RS_EnsureLoaded can't silently drop fields.
Notes
Construction ergonomics: reduce None noise (BandBuilder / default args)
start_band_nd takes seven positional args, up to four of which (name, nodata, outdb_uri, outdb_format) are routinely None. Across the test suite this yields dozens of start_band_nd(None, &["x"], &[3], BandDataType::UInt8, None, None, None) calls where the meaningful fields are buried in noise.
This sits at — not above — the clippy::too_many_arguments threshold (seven args), so it is not lint-forced, unlike start_band_with_view / with_view (eight args), which were bundled into StartBandWithViewArgs / WithViewArgs. It is a discretionary readability item. Raised in PR #813 review.
Options:
- A fluent
BandBuilder that takes the required fields (dim_names, shape, data_type) up front and exposes .name() / .nodata() / .outdb() setters, so the optional tail defaults to None and the required fields can't be defaulted away.
- A
StartBandNdArgs struct with Default on the optional tail (..Default::default()). Simpler, but Default is all-or-nothing per struct, so it needs a guard against defaulting dim_names / shape / data_type into empty or placeholder values.
Either composes with copy_band_from above: the copy path covers "mostly a clone," the builder path covers "construct fresh with sensible defaults."
Problem
RasterBuilderonly supports constructing rasters/bands field-by-field (start_raster_nd,start_band_nd). Any consumer that wants to produce an output raster that's mostly a copy of an input — overriding one field — has to manually destructure every field and re-append it. That's fragile: the copy is only as complete as the author remembered to make it, and it silently rots as the band/raster schema grows.RS_EnsureLoaded(rust/sedona-raster-functions/src/rs_ensure_loaded.rs) is the cautionary case. It's a copy-with-substitution — output = input raster, except OutDb bands get theirdatacolumn filled in — implemented as a hand rebuild:This list is missing
band.view()— so the rebuild silently drops non-identity views. (Latent today:RasterRefImpl::band()rejects non-identity views, so they're unconstructible — but the moment view composition lands, this rebuild corrupts viewed rasters. Tracked separately.) Any field added to bands in the future hits the same trap.Proposed API
Copy-everything-by-default, override what you're changing:
The exact override surface (a
with_data-style follow-up vs. a metadata-only copy + separate data write) is open for design. The invariant that matters: a copy stays complete as the schema grows, so consumers likeRS_EnsureLoadedcan't silently drop fields.Notes
Buffer-sharing path from RasterBuilder: zero-copy band-data transfer via BinaryView buffer sharing #894 rather than re-copying bytes.RS_EnsureLoaded(removes its manual rebuild).band.view()through a copy is the mechanism the view-round-trip correctness fix will rely on (separate issue, forthcoming).Construction ergonomics: reduce
Nonenoise (BandBuilder / default args)start_band_ndtakes seven positional args, up to four of which (name,nodata,outdb_uri,outdb_format) are routinelyNone. Across the test suite this yields dozens ofstart_band_nd(None, &["x"], &[3], BandDataType::UInt8, None, None, None)calls where the meaningful fields are buried in noise.This sits at — not above — the
clippy::too_many_argumentsthreshold (seven args), so it is not lint-forced, unlikestart_band_with_view/with_view(eight args), which were bundled intoStartBandWithViewArgs/WithViewArgs. It is a discretionary readability item. Raised in PR #813 review.Options:
BandBuilderthat takes the required fields (dim_names,shape,data_type) up front and exposes.name()/.nodata()/.outdb()setters, so the optional tail defaults toNoneand the required fields can't be defaulted away.StartBandNdArgsstruct withDefaulton the optional tail (..Default::default()). Simpler, butDefaultis all-or-nothing per struct, so it needs a guard against defaultingdim_names/shape/data_typeinto empty or placeholder values.Either composes with
copy_band_fromabove: the copy path covers "mostly a clone," the builder path covers "construct fresh with sensible defaults."