COOLJAPAN
← All posts

OxiGeo 0.2.2 Released — `Dataset::read_band` Finally Returns One Band, and DEFLATE Decoding Is Up to 1.79× Faster

OxiGeo 0.2.2 fixes GitHub issue #14 — Dataset::read_band silently returned the whole multi-band image instead of one band, a defect pattern found and fixed in a dozen crates. Also ships 1.45-1.79x faster DEFLATE decoding and zero-allocation typed raster readers.

release oxigeo gdal geospatial pure-rust geotiff

A geospatial library that hands you the wrong pixels without telling you is worse than one that crashes.

Today we released OxiGeo 0.2.2 — a correctness release built around GitHub issue #14: Dataset::read_band silently ignored its band argument on multi-band rasters and returned the entire pixel-interleaved image instead. Root-causing it inside the GeoTIFF driver’s block-decode engine turned up the identical defect — wrong interleaving assumptions, or the wrong byte order — independently re-implemented in a dozen other crates. 192 files changed; 33 new issue_14_*-named regression tests, benchmarks, and examples guard against it coming back.

No C. No C++. No Fortran. OxiGeo 0.2.2 still compiles to a single static binary (or WASM) and runs everywhere Rust runs — and it ships a substantially faster DEFLATE decoder without picking up a single new native dependency to get there.

Why 0.2.2 is a game changer

A raster library’s one job is to hand back the pixels you asked for. When it doesn’t, the failure mode is the worst kind:

OxiGeo 0.2.2 ends all of that:

Technical Deep Dive: what issue #14 actually touched

  1. A purpose-built band-decode engine. The GeoTIFF driver gains a real band-aware, low-allocation read API: read_band_into/read_band_into_typed, read_window_into/read_window_into_typed, read_bands_into_typed/read_window_bands_into_typed (one block decode shared across every requested band), and a new opt-in parallel feature that fans block decode out across rayon workers — bit-identical to the serial path.

  2. A typed, zero-copy raster-element layer in oxigeo-core. The sealed RasterElement trait (implemented for every integer/float raster type) defines on-disk byte width, RasterDataType tag, and native-endian conversion, plus exact integer-to-integer conversion through an i128 bridge — replacing a per-pixel f64 round-trip that silently lost precision above 2^53 on UInt64/Int64. It also closed a latent alignment-UB bug in as_slice/as_slice_mut/row_slice that stayed invisible only because production allocators happen to over-align.

  3. New interleaved and zero-allocation readers. read_interleaved/read_interleaved_into and read_window_interleaved/read_window_interleaved_into are the supported replacement for the old (buggy) read_band behavior — bands: Option<&[u32]> selects, reorders, or subsets bands, and the *_into forms bound peak memory to one strip rather than the whole raster. A new data_type() reads the on-disk pixel type before any raster read at all.

  4. The same defect pattern, hunted down workspace-wide. Once the root cause was named, it turned up independently reinvented in oxigeo-qc (nodata/radiometric scanners), oxigeo-server (WMS/WMTS/XYZ handlers assuming power-of-two overview pyramids), oxigeo-services (WCS GetCoverage truncating multi-band responses), oxigeo-mobile, oxigeo-wasm, oxigeo-node, oxigeo-cli, oxigeo-ml-foundation, oxigeo-jupyter, and oxigeo-drivers-vrt — ten crates, one shared correction.

Getting Started

[dependencies]
oxigeo = "0.2"  # GeoTIFF + GeoJSON + Shapefile by default
use oxigeo::Dataset;

fn main() -> oxigeo::Result<()> {
    let dataset = Dataset::open("scene.tif")?;
    let (width, height) = (dataset.width() as usize, dataset.height() as usize);

    // Single band: exactly width * height samples, always (as of 0.2.2).
    let red: Vec<u8> = dataset.read_band(0)?;
    assert_eq!(red.len(), width * height);

    // Want every band together? Ask for it explicitly and pick the order.
    let mut rgb = vec![0u8; width * height * 3];
    dataset.read_interleaved_into(Some(&[2, 1, 0]), &mut rgb)?; // read as BGR

    // Know the pixel type before you commit to a read.
    println!("dtype: {:?}", dataset.data_type());
    Ok(())
}

What’s New in 0.2.2

Tips

This is the foundation

OxiGeo 0.2.2 leans on the same Pure Rust COOLJAPAN stack as every release before it: CRS transforms via OxiProj, HDF5/NetCDF read-write through oxih5 and oxinetcdf, SQLite via oxisql-sqlite-compat (Limbo), TLS via OxiTLS, compression across every format driver via the OxiArc family (now at 0.4.0 for the DEFLATE rewrite this release leans on), ML tensor math via SciRS2-Core, and model export validated against OxiONNX. Every one of those is itself Pure Rust — which is how a 75-crate, ~791K-SLoC workspace ships both a correctness fix and a decode-speed win without picking up a single new native dependency.

Repository: https://github.com/cool-japan/oxigeo

Star the repo if you’d rather your geospatial dependency return exactly the band you asked for than quietly hand you three times too much data laid out wrong. One root cause, ten crates fixed, and a faster decoder along the way — that’s what a correctness release is supposed to look like.

The era of “it probably decoded right” is over. Pure Rust geospatial is here — fast, safe, and sovereign.

KitaSan at COOLJAPAN OÜ
July 30, 2026

↑ Back to all posts