Two days in, OxiProj already speaks Python, runs in the browser, and reprojects on the GPU.
Today we released OxiProj 0.1.1 — a fast follow-up to the 0.1.0 debut that adds Python and WASM bindings, cartographic-distortion analysis, and SIMD/GPU-accelerated reprojection, on the heels of the Pure-Rust PROJ port we shipped two days ago.
If you missed it: OxiProj is a Pure-Rust reimplementation of PROJ 9.8.0 — not a binding to libproj, no proj-sys, no C toolchain. 0.1.1 doesn’t change that one bit; the optional EPSG proj.db still runs on the Pure-Rust oxisql-sqlite-compat engine, and the crate still compiles to a single static binary or to WASM. This post is about the deltas.
What’s new in 0.1.1
proj-sys remains the last big -sys liability dragging libproj and the C toolchain into every geospatial CI run — and it still hurts OxiGDAL most. 0.1.1 keeps closing the gap, and widens OxiProj’s reach well beyond Rust:
- 1819 tests passing (0 failed, 5 skipped) — up from 1345.
- 88,307 lines of Pure-Rust code (100,778 total) across 12 crates, two more than 0.1.0’s ten — the new pair are the Python and WASM bindings.
- A 707-case differential-parity harness at
1e-9°tolerance, up from 544. - Python and WASM bindings, Tissot indicatrix + covariance + autodiff cartographic analysis, and GPU-accelerated reprojection via the Pure-Rust
oxicudastack.
Technical Deep Dive: the 0.1.1 deltas
Bindings: Python and WASM. Two new crates extend OxiProj past Rust. oxiproj-py exposes Crs/Transformer/Coordinate via PyO3, mirroring pyproj, with a pip install / maturin publish workflow — and the epsg feature on by default, so authority strings work out of the box (Transformer('EPSG:4326', 'EPSG:3857')). oxiproj-wasm brings coordinate transformation to the browser and Node.js via wasm-bindgen.
Cartographic-analysis math. 0.1.1 adds a real distortion toolkit. The Tissot indicatrix (TissotParams) computes semi-major/minor axes, angular distortion, areal scale, and a conformality test using Snyder §4-14 formulas. Underpinning it is a forward-mode autodiff path: dual numbers Dual1<N> (first order) and Dual2<N> (second order), a Scalar trait, and a ProjectGeneric<S> surface that evaluates projections over dual numbers — feeding exact Jacobians into covariance propagation (CovMatrix2x2, computing Σ_out = J·Σ_in·Jᵀ) and second-order distortion via HessianFactors (Dual2<2>). DistortionRaster::compute sweeps TissotParams across an extent and exports to GeoTIFF or CSV.
Acceleration: SIMD, rayon, and GPU. Columnar reprojection goes wide: reproject_interleaved() / reproject_interleaved_xyz() batch with f64x4, and SIMD kernels land across the stack (inverse_batch/direct_batch in oxiproj-geodesic, project_fwd_batch_x4 4-lane in oxiproj-projections, Helmert SIMD batch, bilinear batch sampling). Transformer::transform_xy_par() adds rayon parallel batching behind the rayon feature. And there’s optional CUDA support behind the gpu feature: an oxiproj::gpu module powered by the Pure-Rust oxicuda stack — libcuda.so loaded at runtime, with no CUDA Toolkit at build time. Web Mercator (EPSG:4326↔3857) forward/inverse and the distortion field run as hand-written f64 PTX kernels, including a from-scratch f64 transcendental library (ln/exp/tan/atan/asinh/sinh, validated to ~1e-14 on hardware) since PTX only ships f32 approximations. There’s a helmert7_batch_gpu pure-arithmetic 3-D Helmert datum batch, transparent CPU fallback when no device is present, and an oxiproj transform --gpu flag.
Interop and geodesy. OxiProj now meets the Rust geo stack where it lives: geo-types interop (TransformGeoTypes for Point/LineString/Polygon/Multi*), geozero streaming reprojection (ReprojectionProcessor<P> with Arc<Transformer>), GeoArrow/GeoParquet CRS metadata via a hand-rolled JSON parser, and a Cloud-Optimized GeoTIFF reader (CogLayout, CogGridSampler) with a zero-dependency TIFF IFD parser. On the geodesy side: EpochTaggedCrs ([email protected], EPSG:[email protected]), an epoch-aware pipeline (transform_epoch_aware(), PlateMotionModel), a plate-motion frame chain (ITRF2020→ITRF2014→ITRF2008), GeoidGrid vertical-datum conversion, and SH geoid synthesis from ICGEM .gfc files (GeoidCoeffs::from_gfc() for EGM2008/EGM96).
Getting Started
The Rust path is unchanged — cargo add oxiproj (plus --features epsg for authority codes). What’s new in 0.1.1 is Python:
pip install oxiproj
from oxiproj import Transformer
t = Transformer("EPSG:4326", "EPSG:3857") # epsg feature on by default in oxiproj-py
x, y = t.transform(9.0, 48.0) # lon, lat -> Web Mercator meters
print(x, y)
Or analyze map distortion straight from the CLI with the new distort subcommand:
oxiproj distort --point 9.0 48.0
What’s New in 0.1.1
- Python (PyO3) and WASM bindings —
pyproj-style Python and in-browser/Node.js transforms. - Tissot indicatrix, covariance propagation, and forward-mode autodiff (Dual1/Dual2), with
DistortionRaster::computeexporting GeoTIFF/CSV. - New CLIs:
oxiproj distort(point + raster modes),oxiproj transform --jobs Nparallel batch, and a standaloneoxiggxffor OGC GGXF grids. - geo-types / geozero / GeoParquet / COG interop, so OxiProj drops into the Rust geo stack.
- Epoch-aware geodesy:
EpochTaggedCrs, plate-motion model chains,GeoidGridand SH geoid synthesis, bicubic (Catmull-Rom) interpolation. - SIMD + rayon + oxicuda GPU acceleration, plus Helmert batch/LSQ estimation (via oxiblas GEMM), GGXF read/write (via oxih5), and
oxizSMT verification of Helmert bounds. - no_std + alloc support for the core/geodesic/projections crates (std remains default),
TypedCoordphantom types, new EPSG Vertical/Compound CRS (e.g. EPSG:5703 NAVD88, 5773 EGM96), and aTransformer::inverse()convenience. - Fixes worth noting:
wideupgraded 0.7→1.x,crs_for_bboxrewritten O(n³)→O(12) queries,ProjError::suggestion()added for 7 variants, thegeodCLI flags (--help/-h/-l/-v, listing all 46 built-in ellipsoids) now work, the hardcoded/tmp/projpath replaced withstd::env::temp_dir().join("proj"), and the covariance test tightened to pass Miri’s strict-float mode.
Tips
pip install oxiprojif you wantpyproj-style transforms in Python —EPSG:strings work because theepsgfeature is on by default inoxiproj-py.- Reach for
oxiproj distortto study where a projection stretches, shears, or inflates area — point query or full raster grid. - Turn on
rayonorgpufor big batches —Transformer::transform_xy_par()for CPU parallelism,--gpu(oxicuda) when a device is present, with automatic CPU fallback when it isn’t. - Use
Transformer::inverse()to swap source and target without rebuilding the transformer. - Tag epochs with
EpochTaggedCrs(e.g.[email protected]) for time-dependent, plate-motion-aware transforms. - Verify Helmert bounds with the
oxizfeature —verify_helmert_scale_bounds()andverify_invertible_at()add SMT-checked guarantees.
This is the foundation, widened
0.1.1 leans hard on the COOLJAPAN ecosystem, all grounded in real new dependencies: oxih5 backs the GGXF/HDF5 grid format, oxiblas powers the Helmert LSQ GEMM, oxicuda is the Pure-Rust GPU/PTX stack, oxiz does SMT verification, and oxiarc-deflate handles compressed grids — alongside the oxisql-sqlite-compat EPSG engine and oxihttp CDN fetch from 0.1.0.
The north star hasn’t moved: the OxiGDAL cutover — letting OxiGDAL drop proj-sys and build fully C-free — is still the goal we’re driving toward.
Repository: https://github.com/cool-japan/oxiproj
Star the repo if you want geospatial that runs in Rust, Python, the browser, and the GPU — all from one Pure-Rust core. Pure Rust cartography is here — no libproj, no FFI, no C toolchain.
— KitaSan at COOLJAPAN OÜ June 21, 2026