The last big -sys liability in COOLJAPAN geospatial just got a Pure-Rust answer.
Today we released OxiProj 0.1.0 — a near-complete, 100% Pure-Rust port of PROJ 9.8.0 that reimplements coordinate reference systems, datum transformations, and map projections with no C, no FFI, and no native dependencies.
No C. No C++. No Fortran. No proj-sys, no libproj, no PROJ toolchain dragged into your build. OxiProj is not a binding — it is a faithful reimplementation of PROJ 9.8.0 in Rust: the same proj-string semantics, the same projection math, the same EPSG codes. The default build is 100% Pure Rust with no database and no FFI whatsoever. The optional EPSG proj.db is served by the Pure-Rust oxisql-sqlite-compat engine — no native SQLite, no C library. It compiles to a single static binary (and to WASM), and it runs everywhere a Rust target does.
Why OxiProj 0.1.0 — and why now
The COOLJAPAN ecosystem is committed to being C/C++/Fortran-free. By mid-2026, proj-sys — the bindings to the C PROJ library — was the single largest remaining FFI liability in the entire stack. Every geospatial CI run had to drag libproj and PROJ’s full C toolchain along just to reproject a coordinate. That pain landed hardest on OxiGDAL, our Pure-Rust GDAL effort, where proj-sys was the last thing standing between it and a fully sovereign build.
OxiProj exists to purge that dependency. And at 0.1.0 it already arrives close to complete:
- 1345 tests passing (0 failed, 5 skipped), validating projections, geodesics, transforms, and CRS handling against PROJ reference behavior.
- ~197 projections — the full PROJ 9.8.0 catalog, sphere and ellipsoid, forward and inverse where applicable.
- 68,007 lines of Pure-Rust code (76,995 total including comments and blanks) across 10 crates.
- The full EPSG
proj.db(~9.5 MiB) bundled and read through the Pure-Rustoxisql-sqlite-compatengine — no native SQLite. - A 544-case differential-parity corpus with hardcoded reference values, plus a live suite vs an installed PROJ-C binary.
- Zero clippy warnings under
-D warnings, nounwrap()in production code, and#![forbid(unsafe_code)]in every single crate.
This is a 0.1.0, and we say so honestly — but it is an ambitious one.
Technical Deep Dive: how the port is laid out
OxiProj is split into ten crates, each mirroring a layer of PROJ’s responsibilities.
The numerical and geodesic core. oxiproj-core holds the coordinate types, ProjError with the full PROJ_ERR_* taxonomy, the Ellipsoid type with derived parameters, the ellps/units/prime-meridian/datum tables, DMS parsing, the PROJ numeric helpers (pj_mlfn, pj_msfn, pj_tsfn, pj_phi2, pj_gauss, proj_mdist), and the Operation trait. On top of that, oxiproj-geodesic is a faithful port of the Karney geodesic algorithms from PROJ 9.8.0 / GeographicLib — direct, inverse, and area, with GeodesicLine and PolygonArea.
The engine and the projection catalog. oxiproj-engine is the proj-string tokenizer and parser, ellipsoid setup, forward/inverse prepare-and-finalize, the operation registry, and +proj=pipeline with +step and per-step +inv. oxiproj-projections carries the whole ~197-projection PROJ 9.8.0 catalog. The 0.1.0 release completed it from a 21-projection core to the full set — the Eckert/Wagner/Putnins/McBryde pseudocylindricals, van der Grinten, the Adams/Guyou/Peirce elliptic-integral conformals, Krovak, the HEALPix/rHEALPix/QSC/S2/ISEA polyhedral family, interrupted Goode and Mollweide, and Space Oblique Mercator.
Transformations and grids. oxiproj-transformations covers the conversions (noop, axisswap, unitconvert, cart, geocent, geoc, topocentric, set) and the datum transforms: Helmert 3/4/7-parameter (including position-vector and coordinate-frame conventions), time-dependent Helmert, Molodensky-Badekas, Molodensky, affine, geogoffset, vertoffset, and the grid-based operations (hgridshift, vgridshift, xyzgridshift, gridshift, tinshift, deformation). The tenth crate, oxiproj-grids, supplies the grid readers — NTv2 .gsb, GTX, GeoTIFF via hand-rolled TIFF-tag parsing with OxiARC deflate, plus TIN/tinshift — with bilinear interpolation and an LRU grid cache. It is Pure-Rust by default; CDN grid fetch is opt-in behind the network feature.
CRS and the operation factory. oxiproj-crs is the ISO-19111 CRS model — Geographic, Geodetic, Projected, Vertical, Compound, Temporal (TIMECRS), and Engineering (ENGINEERINGCRS) — with WKT1 (GDAL), WKT2:2019, the ESRI dialect, and PROJJSON I/O behind the projjson feature. Its CoordinateOperationFactory does real pathfinding: direct DB lookup, then a datum-hub pivot, then candidate ranking, then a ballpark fallback, with AreaOfUse enforcement and CRS-native axis order. Finally, oxiproj-db bundles the full EPSG proj.db, read through the Pure-Rust oxisql-sqlite-compat engine behind the opt-in epsg feature — replacing the curated ~130-code subset with the complete authority database. The oxiproj facade and oxiproj-cli round out the ten.
Getting Started
Add it to your project — the default build is Pure Rust, with no DB and no FFI:
cargo add oxiproj
Want EPSG codes? Turn on the opt-in epsg feature, which pulls in the full proj.db via oxisql-sqlite-compat:
cargo add oxiproj --features epsg
Then reproject a coordinate. Note the GIS lon, lat (degrees) convention by default — and that there is no Transformer::from_proj: you build a Crs first, then construct a Transformer from a source and target.
use oxiproj::{Crs, Transformer, Coordinate};
fn main() -> Result<(), oxiproj::ProjError> {
// WGS84 geographic (lon, lat 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 }; // x = lon, y = lat
let out = t.transform(&input)?;
println!("Web Mercator: x = {}, y = {}", out.x, out.y);
// Build a CRS from a proj-string, then transform into it
let utm32 = Crs::from_proj("+proj=utm +zone=32 +datum=WGS84")?;
let to_utm = Transformer::new(Crs::wgs84()?, utm32)?;
let projected = to_utm.transform(&input)?;
println!("UTM 32N: x = {}, y = {}", projected.x, projected.y);
Ok(())
}
Prefer the command line? The facade CLI ships a transform subcommand:
oxiproj transform "+proj=utm +zone=32 +datum=WGS84" 9.0 48.0
What’s inside
- A near-complete reimplementation of PROJ 9.8.0 — proj-strings, projections, datum transforms, and CRS, all in Pure Rust.
- The facade types you’ll reach for first:
Coordinate { x, y },Coordinate3D { x, y, z }, andBoundingBox. - CRS constructors
Crs::from_proj,from_wkt, andfrom_epsg, plus shortcutswgs84,web_mercator,nad83,etrs89,gda94, andjgd2000. - A full
TransformerAPI:new/from_epsg, andtransform/transform_3d/transform_batch/transform_bbox, with builder optionswith_strictandnormalize_for_visualization. - A complete CLI suite that speaks PROJ’s language:
proj,cs2cs,cct,projinfo,geod, andgie— a test-harness runner that passes PROJ’s ownbuiltins.giecorpus. - A 544-case differential-parity harness that surfaced and drove 3 fidelity fixes; remaining divergences are documented PROJ 9.7-vs-9.8 version differences.
Tips
- Build a
Crs, then aTransformer. There is noTransformer::from_proj— callCrs::from_proj(...)first, thenTransformer::new(source, target). - Enable
epsgfor authority codes.Crs::from_epsg(...)andTransformer::from_epsg(...)need the bundledproj.db, which lives behind theepsgfeature. - Reproject in bulk with
transform_batchinstead of looping per point — it’s the right tool for whole datasets. - Validate fidelity with the
gierunner, which checks OxiProj against PROJ’sbuiltins.giecorpus the same way upstream PROJ does. - Grid CDN fetch is opt-in. Network grid downloads live behind the
networkfeature; the default build is fully offline and Pure Rust. - Mind the projection math version.
eqcfollows the PROJ 9.8.0 ellipsoidal (EPSG:1028) form and may differ from older PROJ releases.
This is the foundation
OxiProj fits into COOLJAPAN’s broader push to purge every -sys crate from the ecosystem. It already leans on real siblings: OxiARC (oxiarc-deflate) compresses its grid data, oxisql-sqlite-compat serves the EPSG database without native SQLite, and oxihttp powers CDN grid fetch behind the network feature.
The north star, though, is OxiGDAL. OxiProj’s whole reason for being is to let OxiGDAL drop proj-sys and finally build C-free. That cutover — swapping OxiGDAL onto OxiProj and removing proj-sys — remains as future work, blocked pending sign-off. We are calling it out honestly: it’s the headline goal still ahead of us, not something already done.
Repository: https://github.com/cool-japan/oxiproj
Star the repo if you want a geospatial stack that builds without a C toolchain. Pure Rust cartography is here — no libproj, no FFI, no C toolchain.
— KitaSan at COOLJAPAN OÜ June 19, 2026