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:
- A reachable
unreachable!()panic inqsc’s Right/Back/Left equatorial cube faces, hit by ordinary public-API calls. - A sign error in ellipsoidal Oblique Mercator (
omerc) that flipped easting and produced garbage southern-hemisphere coordinates. - Dimensionless-output projections silently scaled by the semi-major axis (
s2,calcofi), diverging from PROJ by a factor of the sphere radius. - A ~19° longitude offset in
spilhausfrom conflating the derived rotated-frame origin with the user’s+lon_0. - Orbital angles read as raw numbers instead of degrees in
som/lsat, diverging by megametres. - A SQL-injection path in the EPSG database layer (
resolve_alias/is_deprecated/superseded_byinterpolated a caller-controlled table name raw into SQL).
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
- T3.1: automatic epoch-aware pathfinding — plate-motion propagation plus multi-hop frame-change Helmert chaining, evaluated at the target epoch.
- T7.1: exact-autodiff Tissot distortion factors reachable from the public engine API, gated per projection by an empirical numeric-consistency check.
- T7.4: guaranteed-bounds interval-arithmetic Mercator/Transverse Mercator/Helmert transforms,
no_std-compatible. - Fixed:
qsccube-face panic,omercsign error,s2/calcofioutput scaling,spilhauslongitude offset,som/lsatangle units, Krovak/NZMG ellipsoid-axis scaling, Cassini inverse non-convergence handling. - Fixed: SIMD batch lane-correctness, 0/360-longitude grid normalization, multi-IFD GeoTIFF sub-grid resolution, EPSG Helmert UOM normalization.
- Security: SQL-injection fix in the EPSG database layer (closed table-name whitelist), a 2 GiB grid-download cap with
Content-Lengthverification, parser recursion caps (WKT,defmodelJSON) and allocation bounds against adversarial input. - Differential-parity corpus grew from 707 to 1009 records, now spanning 149 of the 157
proj -lmap projections. - Python/WASM binding upgrades toward pyproj/CRS-introspection parity, CLI robustness fixes (
EPIPE, UTF-8 boundary panics,--helpconsistency), and packaging/licensing cleanup (a properLICENSE-THIRD-PARTYnotice file for PROJ/GeographicLib).
Tips
- Call
Pj::factors_exactif you need exact (not finite-difference) Tissot distortion — it’s now reachable onlcc/stere/laea/aea/mollon real ellipsoids, not just unit spheres. - Use
transform_epoch_awareinstead of manually chaining plate-motion and frame-change Helmert steps for time-dependent CRS pairs. - Reach for
oxiproj-core::intervalwhen you need a mathematically guaranteed error bound on a Mercator/Transverse Mercator/Helmert transform, not just an empirically-accurate one. - Double-check
cs2csaxis order if you useEPSG:NNNN/URN/WKT CRS specs — 0.1.2 madecs2cshonor each side’s authority-declared axis order (e.g.EPSG:4326is nowlat,lon), matching real PROJ. Bare PROJ-string CRS specs are unaffected and still use GISlon,lat. - Upgrade if you touch the EPSG database layer — the SQL-injection fix is the kind of thing you want even if you don’t think your table names are attacker-controlled today.
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