tiff-writer 0.4.0

Pure-Rust TIFF/BigTIFF encoder with compression, tiling, and streaming writes
Documentation

geotiff-rust

Pure-Rust TIFF/BigTIFF and GeoTIFF/COG readers and writers. No C libraries, no build scripts, no unsafe beyond memmap2.

Crates

Crate Description
tiff-core Shared TIFF types: ByteOrder, tags, sample traits, compression/predictor enums, and color-model metadata
tiff-reader TIFF/BigTIFF decoder with mmap, strip/tile reads, storage-domain reads, and explicit decoded pixel access
tiff-writer TIFF/BigTIFF encoder with streaming writes, compression, predictors, and BigTIFF
geotiff-core Shared GeoTIFF types: GeoKeyDirectory, CRS, GeoTransform, tag constants
geotiff-reader GeoTIFF reader with CRS/transform extraction, overview discovery, and optional HTTP COG access
geotiff-writer GeoTIFF/COG writer with fluent builder, tile-wise GeoTIFF writes, and overview generation

Reading

use geotiff_reader::GeoTiffFile;

let file = GeoTiffFile::open("dem.tif")?;
println!("EPSG: {:?}, bounds: {:?}", file.epsg(), file.geo_bounds());
let raster: ndarray::ArrayD<f32> = file.read_raster()?;

Use read_decoded_raster / read_decoded_window on GeoTiffFile and read_decoded_image / read_decoded_window on TiffFile when you want palette expansion or color-space conversion (for example palette TIFF, YCbCr, or CMYK) instead of storage-domain samples.

Writing

use geotiff_writer::{GeoTiffBuilder, Compression};
use ndarray::Array2;

let data = Array2::<f32>::zeros((256, 256));
GeoTiffBuilder::new(256, 256)
    .epsg(4326)
    .pixel_scale(0.01, 0.01)
    .origin(-180.0, 90.0)
    .nodata("-9999")
    .compression(Compression::Deflate)
    .write_2d("output.tif", data.view())?;

For separate-planar multiband output, set planar_configuration(PlanarConfiguration::Planar) on ImageBuilder or GeoTiffBuilder.

Streaming tile writes

use geotiff_writer::GeoTiffBuilder;
use ndarray::Array2;

let builder = GeoTiffBuilder::new(512, 512)
    .tile_size(256, 256)
    .epsg(4326);
let mut writer = builder.tile_writer_file::<f32, _>("large.tif")?;
for (x, y, tile) in tiles {
    writer.write_tile(x, y, &tile.view())?;
}
writer.finish()?;

COG with overviews

use geotiff_writer::{GeoTiffBuilder, CogBuilder, Resampling, Compression};
use ndarray::Array2;

let data = Array2::<u8>::zeros((1024, 1024));
CogBuilder::new(
    GeoTiffBuilder::new(1024, 1024)
        .tile_size(256, 256)
        .compression(Compression::Deflate)
        .epsg(4326)
)
.overview_levels(vec![2, 4, 8])
.resampling(Resampling::Average)
.write_2d("output.tif", data.view())?;

For multi-band COG output, use write_3d/write_3d_to or write_tile_3d with bands(...) and optional planar_configuration(PlanarConfiguration::Planar).

Features

Read

  • Classic TIFF and BigTIFF
  • Little-endian and big-endian byte orders
  • Strip and tile data access with windowed reads
  • Chunky and separate planar sample layouts
  • Compression: Deflate, LZW, PackBits, LERC, LERC+DEFLATE, JPEG (optional), ZSTD (optional), LERC+ZSTD (optional)
  • Parallel decompression via Rayon
  • Storage-domain typed sample reads via read_* / read_*_samples
  • Explicit decoded pixel reads via read_decoded_* for standard TIFF color models, including palette expansion, YCbCr/CMYK conversion, and sub-byte grayscale/palette decode
  • Structured photometric/color-model metadata: palette ColorMap, ExtraSamples, CMYK, and YCbCr
  • GeoKey directory, structured CRS metadata (projected, geographic, geocentric, vertical, compound), transforms, NoData
  • Overview discovery from both reduced-resolution top-level IFDs and recursive base-image SubIFD-backed overview trees
  • Optional HTTP range-backed remote COG access

Write

  • Classic TIFF and BigTIFF with auto-detection
  • Strip and tile layouts
  • Compression: Deflate, LZW, JPEG (optional), LERC, LERC+DEFLATE, ZSTD (optional), LERC+ZSTD (optional)
  • Predictors: horizontal differencing, floating-point byte interleaving
  • Chunky and separate planar multi-band layouts and all sample types (u8 through f64)
  • Photometric/color-model tags: palette ColorMap, ExtraSamples alpha, CMYK (Separated + InkSet), and YCbCr 4:4:4
  • Streaming tile-by-tile GeoTIFF writes for large rasters
  • GeoTIFF metadata: projected/geographic/geocentric/vertical compound CRS keys, pixel scale, origin, affine transforms, NoData
  • COG output with GDAL-compatible ghost-area metadata, overview generation (nearest-neighbor, average), and multi-band chunky/planar rasters
  • Disk-backed tile-wise COG assembly via CogTileWriter (base tiles are staged in a temporary raw tile store before final emission)

Codec Notes

JPEG-in-TIFF write uses standard compression code 7 with full JPEG interchange streams per strip/tile. The supported interoperable layouts are single-band chunky output and multi-band separate-planar output, which keeps TIFF, GeoTIFF, and COG files compatible with GDAL/libtiff without requiring TIFF-side shared JPEGTables.

Feature flags

Flag Default Description
local yes Local file reading via tiff-reader (geotiff-reader)
rayon yes Parallel strip/tile decompression (tiff-reader, geotiff-reader)
jpeg yes JPEG-in-TIFF read/write support (tiff-reader, tiff-writer)
zstd yes ZSTD compression, including TIFF LERC+ZSTD read/write support (tiff-reader, tiff-writer)
cog no HTTP range-backed remote COG open (geotiff-reader)

Testing

cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings

Reference-library parity tests are included for tiff-reader and geotiff-reader. They compare this workspace against GDAL/libtiff when those tools are available locally; otherwise they self-skip. Lossless codecs use exact byte and hash parity. The JPEG fixture uses a strict bounded-delta check because compliant decoders can differ by +/-1 in a small number of samples.

For a reproducible reference environment, run the Docker harness:

./scripts/run-reference-parity.sh

For reference comparisons and current benchmark results against GDAL/libtiff, see docs/benchmark-report.md.

For the workspace release order and package verification notes, see docs/publishing.md.

License

MIT OR Apache-2.0