COOLJAPAN
← All posts

OxiProj 0.1.2 Released — Exact-Autodiff Distortion, Epoch-Aware Pathfinding, and Guaranteed-Bounds Transforms

OxiProj 0.1.2 is a hardening/correctness release for the Pure-Rust PROJ replacement: automatic epoch-aware datum pathfinding, exact-autodiff Tissot distortion reachable from the public engine API, guaranteed-bounds interval-arithmetic transforms, SIMD batch lane-correctness fixes, and an SQL-injection fix — still no C, no FFI.

release oxiproj proj gis cartography geospatial pure-rust map-projections security

A month into OxiProj’s life, the fixes are as interesting as the features.

Today we released OxiProj 0.1.2 — a hardening/correctness release on top of the 0.1.1 baseline. No new roadmap surface area this time; instead, a broad numeric-convention audit across the grid, engine, and projection layers (several of the bugs found were silent wrong-answer or crash bugs), completion of two “Beyond PROJ” flagship items, public wiring of a third, and packaging/licensing/security hardening including an SQL-injection fix.

OxiProj is still exactly what it was on day one: a 100% Pure-Rust reimplementation of PROJ 9.8.0 — not a binding to libproj, no proj-sys, no C toolchain. The optional EPSG proj.db still runs on the Pure-Rust oxisql-sqlite-compat engine, and every crate is #![forbid(unsafe_code)], wasm included. 0.1.2 doesn’t change any of that; it makes the existing surface more correct and more secure.

Why 0.1.2 is a game changer for correctness

A month of real usage against the full PROJ 9.8.0 catalog surfaced exactly the kind of bugs that matter most in a geodesy library — the silent ones:

0.1.2 fixes all of it — each with a regression test — and layers on three flagship “Beyond PROJ” capabilities on top.

Technical Deep Dive: the 0.1.2 deltas

T3.1 — automatic epoch-aware pathfinding (flagship). oxiproj-transformations::epoch_pipeline::transform_epoch_aware chains plate-motion propagation in the source frame (propagate_epoch + resolve_plate, preferring an explicit plate hint over point-in-plate lookup) with a breadth-first multi-hop frame-change Helmert path (frame_chain::find_frame_path, e.g. ITRF2014→ITRF2020→ITRF2000) evaluated at the target epoch. No more manually chaining datum hops by hand.

T7.1 — exact-autodiff distortion, now public. Pj::factors_exact and DistortionRaster::compute can now return TissotMethod::Exact — exact Tissot h/k/areal/convergence from dual numbers, matching PROJ’s factors.cpp — instead of silently falling back to finite differences. The registry activates the exact path per projection, empirically (never a hardcoded name gate): on WGS84 that’s lcc, stere, laea, aea, and moll, each verified against proj -S.

T7.4 — guaranteed-bounds interval-arithmetic transforms (flagship). A standalone, no_std-compatible Interval/IntervalCoord type in oxiproj-core::interval — 1-ULP margin on correctly-rounded basic ops, 4-ULP on transcendentals — backs guaranteed-bounds Mercator, Transverse Mercator, and 7-parameter Helmert transforms, each paired with an f64 reference path validated against PROJ 9.8.0.

Correctness and hardening across the stack. SIMD batch forward kernels now provably match the scalar path (a lane-0 divergence and a success-count overcount were fixed, with lane-parity regression tests added). Bilinear/bicubic grid interpolation now normalizes 0..360 vs. -180..180 longitude conventions. GeoTIFF grids gained PlanarConfiguration=2 support and all six PROJ sample-format combinations. Multi-IFD GeoTIFF grids now resolve their finest-covering child sub-grid, mirroring PROJ’s shift-grid recursion. And on the database side, Helmert parameter rows are now correctly normalized through their UOM codes (milli-arc-second rotations and ppb scales were previously 1000× too large).

Getting Started

Nothing changes in how you reach for OxiProj:

cargo add oxiproj
use oxiproj::{Crs, Transformer, Coordinate};

fn main() -> Result<(), oxiproj::TransformError> {
    // WGS84 geographic (lon, lat in degrees) -> Web Mercator (meters).
    let wgs84 = Crs::wgs84()?;
    let webmerc = Crs::web_mercator()?;
    let t = Transformer::new(wgs84, webmerc)?;

    let input = Coordinate { x: 9.0, y: 48.0 };
    let output = t.transform(&input)?;
    println!("Web Mercator: x = {}, y = {}", output.x, output.y);

    Ok(())
}

A runnable version of this walkthrough — plus a raw +proj=pipeline example — now ships as crates/oxiproj/examples/basic_transform.rs:

cargo run -p oxiproj --example basic_transform

What’s New in 0.1.2

Tips

This is the foundation

0.1.2 leans on the same COOLJAPAN stack as before — oxisql-sqlite-compat for the C-free EPSG engine, oxih5 for GGXF grids, oxiblas for Helmert LSQ, oxicuda for GPU reprojection, oxiz for SMT-verified Helmert bounds, and oxiarc-deflate/lzw for compressed grids — with the same north star: letting OxiGDAL drop proj-sys and build fully C-free.

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

Star the repo if you want geodesy that’s correct, secure, and Pure Rust from the ground up. The era of silent wrong-answer bugs in your coordinate pipeline is over.

KitaSan at COOLJAPAN OÜ July 19, 2026

↑ Back to all posts