| Crates.io | rstiff |
| lib.rs | rstiff |
| version | 0.2.0 |
| created_at | 2025-12-19 09:38:55.911995+00 |
| updated_at | 2026-01-14 14:54:46.469245+00 |
| description | A Rust library for high-precision, type-preserving GeoTiff I/O powered by GDAL. |
| homepage | |
| repository | https://github.com/csuhqf/rstiff |
| max_upload_size | |
| id | 1994472 |
| size | 2,824,589 |
A high-precision, type-preserving Rust library for GeoTiff I/O and processing, powered by GDAL.
ndarray::Array3<f64> for accurate scientific computing.Byte, UInt16, Float32) when writing.NoData values and preserves transparency in output files.rstiff relies on GDAL bindings. You must have GDAL installed on your system.
brew install gdal
sudo apt-get install libgdal-dev
Download and install GDAL from GISInternals or use conda:
conda install -c conda-forge gdal
Add this to your Cargo.toml:
[dependencies]
rstiff = "0.1"
Or install via cargo:
cargo add rstiff
use rstiff::GeoTiff;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read a GeoTiff file
let mut tif = GeoTiff::read("input.tif")?;
println!("Dimensions: {:?}", tif.data.dim()); // (bands, height, width)
println!("Projection: {}", tif.projection);
// Modify pixel data (it's an ndarray::Array3<f64>)
tif.data[[0, 100, 100]] = 255.0;
// Write back - original data type is preserved
tif.write("output.tif")?;
Ok(())
}
| Type | Description |
|---|---|
GeoTiff |
Main struct containing raster data, geotransform, projection, and metadata |
RasterInfo |
Lightweight metadata struct (no pixel data loaded) |
TiffError |
Error type for all operations |
GeoTiff::read() - Read entire filelet tif = GeoTiff::read("input.tif")?;
GeoTiff::info() - Get metadata without loading pixelslet info = GeoTiff::info("large_file.tif")?;
println!("Size: {}x{}", info.width, info.height);
println!("Bands: {}", info.bands);
println!("Bounds: {:?}", info.bounds()); // (min_x, min_y, max_x, max_y)
println!("Resolution: {:?}", info.res()); // (pixel_width, pixel_height)
GeoTiff::read_window() - Read by pixel coordinatesOnly load the pixels you need - perfect for large files:
// Read a 256x256 tile starting at pixel (1000, 2000)
let tile = GeoTiff::read_window("large.tif", 1000, 2000, 256, 256)?;
GeoTiff::read_bounds() - Read by geographic coordinates// Read data within a geographic bounding box
let roi = GeoTiff::read_bounds("large.tif", (116.0, 39.0, 117.0, 40.0))?;
// path min_x min_y max_x max_y
GeoTiff::read_by_vector() - Read by vector file extent// Read only the area covered by a KML/Shapefile/GeoJSON
let roi = GeoTiff::read_by_vector("large.tif", "area.kml", true)?;
// raster vector apply_mask
// apply_mask = true -> pixels outside polygon set to NoData
// apply_mask = false -> just clip to bounding box
Use RasterInfo for coordinate transformations:
let info = GeoTiff::info("input.tif")?;
// Pixel to geographic coordinates
let (x, y) = info.pixel_to_geo(100, 200); // col, row -> x, y
// Geographic to pixel coordinates
let (col, row) = info.geo_to_pixel(116.5, 39.5); // x, y -> col, row
// Convert geographic bounds to pixel window
let (x_off, y_off, width, height) = info.bounds_to_window((116.0, 39.0, 117.0, 40.0))?;
crop() - Crop by pixel coordinateslet cropped = tif.crop(100, 100, 500, 500)?; // x_off, y_off, width, height
crop_by_vector() - Crop by vector file (loads full raster first)let cropped = tif.crop_by_vector("boundary.shp", true)?;
// Use read_by_vector() instead for large files!
reproject() - Reproject to different CRS// Reproject from WGS84 to UTM Zone 50N
let utm = tif.reproject(32650)?; // EPSG code
tif.write("output.tif")?;
// - Original data type is preserved (Byte, UInt16, Float32, etc.)
// - LZW compression is applied automatically
// - NoData values are handled correctly
For a 10GB raster file, window reading dramatically reduces memory usage:
| Method | Memory Usage |
|---|---|
GeoTiff::read() |
~10 GB (loads everything) |
GeoTiff::read_window(..., 256, 256) |
~0.5 MB (loads only needed area) |
GeoTiff::read_by_vector() |
Variable (loads only ROI) |
Run the included examples:
# Basic read/write
cargo run --example basic_io
# Reprojection
cargo run --example reproject
# Vector cropping (traditional)
cargo run --example vector_crop
# Window reading (memory efficient)
cargo run --example window_read
# Vector window reading
cargo run --example vector_window_read
| rasterio | rstiff |
|---|---|
rasterio.open() |
GeoTiff::read() |
dataset.read(window=Window(...)) |
GeoTiff::read_window() |
dataset.read() with bounds |
GeoTiff::read_bounds() |
rasterio.mask.mask() |
crop_by_vector() / read_by_vector() |
rasterio.warp.reproject() |
reproject() |
dataset.xy() |
RasterInfo::pixel_to_geo() |
dataset.index() |
RasterInfo::geo_to_pixel() |
dataset.bounds |
RasterInfo::bounds() |
dataset.res |
RasterInfo::res() |
See ROADMAP.md for planned features:
Built on these excellent projects:
MIT License - see LICENSE for details.