COOLJAPAN
← All posts

OxiProj 0.1.5 Released — Twelve EPSG Authority-Path Defects, Fixed and Measured Against PROJ

OxiProj 0.1.5 is a correctness release for the EPSG authority (proj.db) coordinate path: twelve defects — from a 3.28x-oversized ellipsoid to a 380 m operation-selection miss — found and fixed against the Homebrew PROJ 9.7.0 oracle, every number measured, not derived. 2,855 tests passing, 0 failing.

release oxiproj proj gis cartography geospatial pure-rust epsg correctness

Twelve defects. Twelve fixes. Every single one measured against the real PROJ binary, not assumed away.

Today we released OxiProj 0.1.5 — a correctness release for the EPSG authority (proj.db) path, the part of OxiProj that turns an EPSG:xxxx code into a real coordinate reference system and datum transformation, as opposed to a hand-written +proj= string. The PROJ-string path was already faithful in every tested case; the authority path was not, and this release is the fix.

OxiProj is still exactly what it was on day one: a 100% Pure-Rust reimplementation of PROJ 9.8.0 — no proj-sys, no C toolchain, every crate #![forbid(unsafe_code)], compiles to a single static binary (or WASM) and runs everywhere. 0.1.5 doesn’t touch that foundation; it hardens the one code path that reads from a 9.5 MiB bundled SQLite-compatible EPSG database instead of parsing a proj-string directly — and the honesty here matters beyond OxiProj itself: oxigeo-proj 0.2.4 had already demoted Crs::from_epsg to an unregistered-code fallback because of these bugs. 0.1.5 is what lets a downstream consumer trust it again.

Why a “boring” correctness release is the one that matters most

A coordinate library doesn’t usually fail loudly. It fails by returning a plausible-looking number that’s quietly wrong — and every bug below shipped that way:

OxiProj 0.1.5 ends all of that — twelve defects in total, five originally reported and seven more found during the audits that followed each fix, every one closed and pinned with a regression test that reproduces the oracle’s own number.

Technical Deep Dive: falsifying every fix before believing it

The methodology is the headline as much as the fixes: every number in the CHANGELOG was measured against Homebrew PROJ 9.7.0 (proj, cs2cs, projinfo, cct) — the same proj.db OxiProj bundles, verified byte-identical by SHA-256. Nothing here is derived from reading PROJ’s source; it’s read off the reference binary running on real coordinates.

The regression suite that came out of this is new and specific — authority_unit_fidelity.rs (20 tests), wgs84_hub_operations.rs (6), pm_datum_chains.rs (9), lcc_1sp.rs (11), mixed_pair_datum_composition.rs (16), operation_area_of_use_selection.rs (10), operation_fallback_policy.rs (10, each carrying the exact PROJ_DEBUG=3 cs2cs transcript that established the rule it encodes), molodensky_badekas.rs (9), and more. And each fix was falsified before being believed: disabling the chain route, the unified DMS decoder, or the Molodensky-Badekas pivot in turn was verified to turn a named set of tests red — nine tests red for the pivot alone — before the fix was trusted and shipped.

One more thing disclosed rather than hidden: building a Transformer on the authority path is materially slower in 0.1.5 than in 0.1.4 — twenty Transformer::new(EPSG:4267, EPSG:4326) calls (the widest candidate set in the bundled registry) cost 2.5 s of CPU on published 0.1.4 and 20.7 s on 0.1.5, because each retained candidate now resolves a real authority extent and recompiles a pipeline instead of skipping that work. It’s the direct cost of the correctness fixes above, and it’s called out in the CHANGELOG with a specific follow-up (operation_endpoints memoization) rather than left for someone else to discover.

Getting Started

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

fn main() -> Result<(), oxiproj::TransformError> {
    // WGS84 (lon, lat) -> EPSG:6933, Lambert Cylindrical Equal Area.
    let wgs84 = Crs::wgs84()?;
    let ease_grid = Crs::from_epsg(6933)?;
    let t = Transformer::new(wgs84, ease_grid)?;

    let input = Coordinate { x: -144.0, y: -68.8 };
    let output = t.transform(&input)?;
    println!("EPSG:6933: x = {}, y = {}", output.x, output.y);
    // 0.1.5: x = -13894024.356129, y = -6841313.452864 (matches `cs2cs`)
    // 0.1.4: x = -16030006.674231, y = -5929715.294180  (2.1 km off — the +lat_ts bug above)
    Ok(())
}

What’s New in 0.1.5

Tips

This is the foundation

0.1.5 leans on the same COOLJAPAN stack as always — 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, oxiarc-deflate/lzw for compressed grids, and oxihttp for optional network grid fetch — with the same north star: an EPSG authority path that OxiGeo (the renamed OxiGDAL) and every other downstream consumer can trust without a fallback.

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

Star the repo if you think “we measured it against the real binary” should be the minimum bar for a coordinate library, not a nice-to-have. The era of trusting a geospatial library’s silent output is over — Pure Rust means you get to verify every number yourself, and now every number in this release has been.

KitaSan at COOLJAPAN OÜ August 18, 2026

↑ Back to all posts