Skip to main content

Crate rwarp

Crate rwarp 

Source
Expand description

⚠️ v0.3.1 — early development release. This crate closely follows GDAL’s warp algorithms but is an independent implementation — it is not affiliated with or endorsed by the GDAL project. The logic is validated against GDAL output in the cogcache package but has not been formally verified or tested in production. This crate will change significantly. Feedback and testing are very welcome.


§rwarp

A Rust implementation of the GDAL warp pipeline: coordinate transforms, approximate transforms, source window planning, and resampling kernels.

rwarp is pure Rust with no runtime GDAL dependency. Coordinate reference system transforms go through the crs::CrsTransform trait, with two backends selected by Cargo feature: proj (default; the proj crate over libproj, full PROJ) or proj4rs (pure Rust, proj-string and EPSG-code parametric, builds for wasm32-unknown-unknown). Grid arithmetic uses vaster. The algorithms are independently implemented with reference to GDAL’s source (commit a3b7b01d3e, GDAL 3.13.0dev), not translated from it.

§Design philosophy

GDAL’s GDALWarpOperation bundles planning, I/O, coordinate mapping, and resampling into a single pipeline. It’s not easy for casual users to determine what will be read and to plan out a pipeline. rwarp separates these concerns:

target grid spec
      │
      ▼
[plan]  plan_chunks / compute_source_window
      │   → which chunks, which source pixels does each need,
      │     at which kernel scale (gdalwarp's chunking)?
      │   → the plan is inspectable, serializable, resumable
      │
      ▼
[read]  (caller: fetch bytes from COG / Zarr / parquet byte-ref store)
      │
      ▼
[warp]  warp
        → nearest / bilinear / cubic / cubicspline / lanczos kernels,
          average / mode / min / max / med / q1 / q3 / rms area methods

Before touching any pixels you can inspect how many source blocks each target tile needs, detect antimeridian or pole problems, compare target grid specs, and estimate total I/O cost.

§Modules

ModuleResponsibilityGDAL equivalent
transformTransformer trait, GenImgProjTransformerGDALGenImgProjTransform
gcp_transformGcpTransformer (polynomial order 1–3)GDALGCPTransform
geoloc_transformGeolocArrays, GenImgGeolocTransformer (curvilinear source and/or target)GDALGeoLocTransform, GDALGenImgProjTransform with geolocation arrays
approxApproxTransformer (adaptive interpolation)GDALApproxTransform
source_windowcompute_source_window (one window, quick)ComputeSourceWindow
planplan_chunks, ChunkPlanner, warp_chunks: gdalwarp’s chunk list, source windows and per-chunk kernel scale as valuesGDALWarpOperation::CollectChunkList, ComputeSourceWindow
cutlineCutline: polygon limiting the source pixels used, as a per-pixel densityGDALWarpCutlineMaskerEx, TransformCutlineToSource
weightsexact_weights, Weights: conservative remapping weights from the exact footprint as values (ESMF conserve), normalised (FracArea, DestArea) and appliednone (ESMF ESMF_RegridWeightGen, SCRIP)
warpResampling kernels (one-pass generic Sample kernel with GDAL’s downsampling widening, area-weighted average and mode, and a per-band i32 point-kernel reference)gdalwarpkernel.cpp

§Validation status

It is early days for this tool, we have done some validation but much more is needed.

Components that have been validated against GDAL output on real data:

ComponentTest dataResult
GenImgProjTransformerSentinel-2 B0465536/65536 pixels identical to pixel_extract
ApproxTransformerSamebit-identical to GDAL’s GDALApproxTransform
ComputeSourceWindowGEBCO global, Fiji LCCmatches GDAL source windows
Nearest-neighbour kernelSentinel-2, IBCSO Mawson65536/65536 identical to gdalwarp -r near
Bilinear/cubic/lanczosGEBCO at ~1:1 ratio65536/65536 identical to GDAL
Kernel widening, scale estimatesynthetic 64×64 Float32, 1.5:1 to 4:1, anisotropic, with nodata (tests/kernels.rs)within 1e-6 relative of gdalwarp (GDAL 3.14.0dev-739fcf03bf)
Point kernels at any ratiosame, against gdalwarp -wo XSCALE=1 -wo YSCALE=1within 1e-6 relative
Average (GDAL box)same, plus upsampling, overhanging and 30°-rotated destinationswithin 1e-6 relative of gdalwarp -r average
Average (exact footprint)axis-aligned cases vs the GDAL box; 45° diamond vs the analytic meanwithin 1e-5 (f32 coverage fractions)
Conservative weights (exact_weights)regular to rotated, lon/lat to EPSG:3031 and to LAEA (tests/weights.rs)source-cell sums of frac within 6e-14 of 1 (or of the covered fraction at the grid’s edge); DestArea integral within 4e-15 relative; apply(FracArea) with warp-compatible weights identical to warp()
Mode, all MODE_TIESsynthetic categorical, Byte/Int16/Int32/Float32, 4:1 and 2.78:1, nodata, rotatedidentical to gdalwarp -r mode (26 cases); ties at first occurrence follow GDAL’s Byte path for every type
Nodata: point kernels, UNIFIED_SRC_NODATA unset/YES/NO over three bands, average thresholds and excluded valuessynthetic Float32 single- and three-band (near, bilinear, cubic, lanczos, average, mode)within 1e-6 relative of gdalwarp (30 cases)
Cubic spline; min, max, med, q1, q3, rms; Int64, UInt64, Float16synthetic Float32/Int16/Int64/UInt64/Float16, point and widened, nodata, rotated, three bandswithin 1e-6 relative (integers exact, Float16 within one step) of gdalwarp (50 cases, plus 18 more mode cases)
Integer rounding/clamping, nodata avoidance, [0, n) source boundssynthetic Byte/Int16/Int32/Float32 with .5 means, cubic overshoot, means equal to nodata, edge-straddling gridsidentical to gdalwarp (12 cases)
Antimeridian split-readGEBCO Fiji tile [5,3]70× source pixel reduction vs naive read
Chunk planning, whole-raster chunked warp (plan_chunks, warp_chunks)lon/lat sources to Mercator, polar stereographic and LAEA under -wm 100000 or the default, MEM and tiled targets, near/bilinear/cubic/lanczos (tests/chunks.rs)chunk lists and every output pixel identical to gdalwarp (GDAL 3.14.0dev-739fcf03bf), 16 cases of 1 to 28 chunks, with both CRS backends
Longitude wrapping of a geographic source (CENTER_LONG)sources across 180 degrees stored as 170..190, -190..-170, 140..200 and 0..360, and one wider than 360 under a -te across 180, to LAEA, Mercator, polar stereographic and lon/lat targets across 180 (tests/chunks.rs)centre longitude as GDAL’s; exact transformer within 1e-12 px of GDAL’s (libproj); chunk lists and every output pixel identical to gdalwarp with both CRS backends, 11 cases
GeolocArrays / GenImgGeolocTransformersynthetic curvilinear swath, both georeferencing conventions, curvilinear source and curvilinear target, both inverse methods (tests/geoloc.rs)coordinates within 1e-6 px of GDAL’s transformer (CRS step varies slightly by PROJ version); nearest warp identical to gdalwarp -et 0
GDAL’s backmap geolocation inverse (GeolocInverse::Backmap)swath by centres and by corners to lon/lat and polar stereographic, a polar ring and a swath across 180 degrees, holes in the arrays with source nodata, overlapping scans (tests/geoloc_backmap.rs)success at every target pixel as GDAL’s, positions within 5e-10 px; the backmap estimate the same; 24 warps (near, bilinear, cubic; -et 0 and 0.125) identical to gdalwarp with proj4rs and the fixtures’ PROJ (nearest identical, interpolated within 2e-8 relative, with an older libproj)
Cutline (-cutline, CUTLINE_ALL_TOUCHED, -cblend)synthetic Float32, ties through pixel centres, holes, multipolygons, nodata, every method, rotated and LAEA to lon/lat (tests/cutline.rs)within 1e-6 relative of gdalwarp (67 cases); differs only at centre ties on a diagonal (rounding, 5 pixels) and where GDAL’s all-touched line walker burns corner-touched pixels or uses tolerances (3 cases)

Downsampled interpolated warps: when the output is coarser than the source, GDAL widens the bilinear, cubic and lanczos kernels to cover the source area of each output pixel (antialiasing). rwarp does the same by default (warp::KernelScaling::WidenWhenDownsampling), estimating the scale per call as GDAL does per chunk; warp::KernelScaling::Point keeps fixed support, matching gdalwarp -wo XSCALE=1 -wo YSCALE=1. Overview pre-selection is still worthwhile for I/O: the planning layer knows the ratio and can read a coarser level. See the warp module documentation.

§Extensibility: n-D chunks and vector-quantity warps

The warp kernel operates on one 2D spatial slice at a time. N-dimensional chunks — (band, y, x), (time, y, x), (y, x, rgb) — are handled by the caller iterating over outer axes and calling warp_resample per slice. The Transformer is constructed once and reused; only the spatial axes are involved.

The Transformer trait is intentionally minimal: it maps coordinate arrays without knowing what the data values are. This makes it the right seam for future transformer plugins:

  • Curvilinear grids (ROMS, ocean model output): pixel → geo via 2D lookup table rather than affine + PROJ. Implements Transformer, composes with ApproxTransformer, compute_source_window, and warp_resample unchanged. Implemented in geoloc_transform, for a curvilinear source, a curvilinear target, or both.

  • Displacement-field transformers: pixel offsets from a vector field (DEM orthorectification, InSAR pixel offsets) applied on top of a base GenImgProjTransformer.

  • Vector-quantity-preserving warps: scalar resampling is correct for temperature, elevation, reflectance. Velocity fields (u, v) stored in index-grid coordinates (as ROMS does) require the vectors to be rotated by the local Jacobian of the transform at each point — otherwise reprojected arrows point in the wrong direction, silently. This is the same problem as a north arrow on a projected map: correct only at the calibration point, wrong everywhere else. A future warp_vector() kernel would call Transformer::jacobian per pixel to rotate (u, v) into the output frame. See the cogcache design docs for the full discussion.

§GDAL source lineage

GDAL source commit used for analysis: a3b7b01d3e (GDAL 3.13.0dev, 2026-02-23); kernel widening and the lanczos and nodata rules in warp.rs were read at 739fcf03bf (GDAL 3.14.0dev). GDAL is MIT-licensed. This crate is an independent implementation, not a port. We think it’s also very useful as a way to investigate and carry forward the value of the existing GDAL C++ implementation which has years of battle-tested logic within.

rwarpGDAL source fileLines
transform.rsgdaltransformer.cpp, ogr/ogrct.cpp~L2800, ~L3100, ~L3500; InsertCenterLong ~L1423 and source wrap ~L2444 (at 739fcf03bf)
gcp_transform.rsalg/gdal_crs.cGCP polynomial fit
geoloc_transform.rsalg/gdalgeoloc.cpp, alg/rasterfill.cppPixelLineToXY, Transform, GDALInverseBilinearInterpolation, GenerateBackMap, GDALFillNodata (at 739fcf03bf)
approx.rsgdaltransformer.cpp~L4113, ~L4374
source_window.rsgdalwarpoperation.cpp~L1456, ~L2656, ~L2751
plan.rsgdalwarpoperation.cpp~L977-1670, ~L2640-3390 (at 739fcf03bf)
cutline.rsalg/gdalcutline.cpp, alg/llrasterize.cpp, apps/gdalwarp_lib.cppGDALWarpCutlineMaskerEx, BlendMaskGenerator, GDALdllImageFilledPolygon, TransformCutlineToSource (at 739fcf03bf)
warp.rsgdalwarpkernel.cpp~L3084, ~L3262, ~L3655, ~L5510; widening ~L1041, ~L1092-1319, ~L4027, ~L4188, ~L4840; average ~L6845-7030, ~L7577, ~L7960; mode ~L7147-7235, ~L8115-8170; rounding/nodata/bounds ~L1587, ~L1613, ~L1856, ~L5251 (at 739fcf03bf, 3.14.0dev)

Re-exports§

pub use transform::Transformer;
pub use crs::CrsTransform;

Modules§

approx
Approximate transformer: adaptive interpolation of coordinate transforms.
crs
CRS-to-CRS coordinate transform backends.
cutline
Cutlines: restrict a warp to the source pixels inside a polygon, as gdalwarp’s -cutline does, or weight each source pixel by how much of it the polygon covers.
gcp_transform
GCP-based polynomial transformer.
geoloc_transform
Geolocation-array transformer: pixel/line <-> georeferenced coordinates through 2D coordinate arrays (curvilinear grids, satellite swaths).
plan
Chunk planning: how gdalwarp splits a target into chunks, which source window each chunk reads, and the kernel scale each chunk is warped at, returned as values.
source_window
Compute the source pixel window needed for a given destination window.
transform
Coordinate transformers for the warp pipeline.
warp
Warp kernels: nearest-neighbour, bilinear, cubic, cubic spline, lanczos, and the area methods average, mode, min, max, median, quartiles and RMS.
weights
Conservative remapping weights from the exact footprint, as values.