Skip to main content

laterite — a Rust-backed AGS4 reader, writer and validator

laterite

A Rust-backed AGS4 toolchain for the AGS4 geotechnical data format — validate, read as typed data, query, build, fix, diff, certify, and convert ↔ Excel — with a modern, born-typed polars API.

Coming from python-ags4? laterite.compat is a faithful, faster stand-in for its AGS4, check, utils and data modules — one token changes: from python_ags4 import AGS4 becomes from laterite.compat import AGS4, and your code keeps working.

ci python cov PyPI Python versions License: MIT

Install

pip install laterite                     # base AGS4 (polars + duckdb, pyarrow-free)
pip install "laterite[compat]"           # + pandas (python-ags4 drop-in) — still pyarrow-free
pip install "laterite[compat,pyarrow]"   # + the optional pyarrow accelerator (or [all])

Requires Python ≥ 3.12. The wheel is abi3, so one binary covers 3.12 / 3.13 / 3.14. Installing it also puts the lat CLI on your PATH.

The [compat] drop-in is pyarrow-free and fast on its own; adding pyarrow swaps the pandas step for pyarrow's to_pandas and unlocks the Arrow-backed string dtype — an accelerator, never a requirement.

Use

import laterite

# Validate — errors + warnings by default (FYI is opt-in)
report = laterite.validate("delivery.ags")
report.is_valid
for rule, findings in report.by_rule().items():
    print(rule, len(findings))

# Read born-typed columns: a 2DP heading is a float, a DT a datetime
ags = laterite.read("delivery.ags")
ags.groups                       # ['PROJ', 'LOCA', 'SAMP', …]
ags["LOCA"]["LOCA_GL"][0]        # → 12.3  (a polars DataFrame per group)

# SQL across groups, no conversion step
ags.sql("SELECT loca_id, count(*) FROM SAMP GROUP BY 1")

# Repair a dirty file into a fresh handle, then keep working with it
fixed = ags.fix(risky=True)      # pads short rows, transliterates non-ASCII, …

# Typed graph: PROJ → LOCA → SAMP → …
from laterite.ags4 import read_typed
for loca in read_typed("delivery.ags").locas:
    print(loca.loca_id, loca.loca_gl)

# python-ags4 drop-in — swap the import, keep your code
from laterite import compat as AGS4
tables, headings = AGS4.AGS4_to_dataframe("delivery.ags")
AGS4.dataframe_to_AGS4(tables, headings, "round-trip.ags")

read returns born-typed polars frames by default (or pandas with read(..., backend="pandas")) — both pyarrow-free, read back from a Python-owned in-memory DuckDB engine.

More than a faster python-ags4

python-ags4 is the reference Python library for AGS4 — validation plus pandas read/write — and it inspired this project. laterite matches that surface and adds a toolchain on top:

laterite python-ags4
Validate — numbered AGS4 rules ✅ ✅
Read → data frames ✅ born-typed polars or pandas pandas, all strings
Build / write AGS4 · Excel ↔ AGS4 ✅ ✅
Repair engine (fix) ✅ —
SQL across groups · revision diff ✅ —
Validity certificates (.ags.idx) ✅ —
Transport — zstd compress + age encrypt ✅ —
Typed PROJ → LOCA → SAMP graph ✅ —
pyarrow required no (optional accelerator) via pandas' own deps

Performance

Synthetic, spec-valid AGS4 from ags4-forge — the wide scaffold: 123 groups, realistic type mix, zero findings. macOS arm64, hot files, mean of 5 warm runs, python-ags4 1.2.0 vs laterite 0.12.0. Both agree on the findings.

Validation

File python-ags4 check_file laterite.validate speedup
4.9 MB 1.5 s 41 ms 36.8×
25.0 MB 3.7 s 188 ms 19.5×
103.0 MB 12.2 s 781 ms 15.7×
276.5 MB 31.4 s 2.0 s 15.9×
551.6 MB 67.1 s 4.1 s 16.4×

Read → typed — the honest comparison for real work. python-ags4 needs AGS4_to_dataframe + convert_to_numeric on every group to get numbers, and still leaves dates as text; laterite.read is born-typed, dates included.

File python-ags4 + convert_to_numeric laterite.read speedup
4.9 MB 188 ms 15 ms 12.3×
25.0 MB 785 ms 74 ms 10.6×
103.0 MB 3.2 s 300 ms 10.7×
276.5 MB 8.7 s 780 ms 11.2×
551.6 MB 18.3 s 1.6 s 11.1×

Read → strings — like for like, both returning pandas frames of text.

File python-ags4 AGS4_to_dataframe laterite.compat speedup
4.9 MB 141 ms 38 ms 3.7×
25.0 MB 672 ms 148 ms 4.6×
103.0 MB 2.7 s 589 ms 4.6×
276.5 MB 7.2 s 1.5 s 4.9×
551.6 MB 15.3 s 3.0 s 5.1×

Validation — peak RSS

File python-ags4 check_file peak RSS laterite.validate peak RSS ratio
4.9 MB 171 MB 95 MB 1.80×
25.0 MB 343 MB 175 MB 1.96×
103.0 MB 1019 MB 470 MB 2.17×
276.5 MB 2664 MB 1121 MB 2.38×

Read → typed — peak RSS

File python-ags4 + convert_to_numeric peak RSS laterite.read peak RSS ratio
4.9 MB 156 MB 95 MB 1.64×
25.0 MB 315 MB 182 MB 1.73×
103.0 MB 938 MB 505 MB 1.86×
276.5 MB 2446 MB 1266 MB 1.93×

Read → strings — peak RSS

File python-ags4 AGS4_to_dataframe peak RSS laterite.compat peak RSS ratio
4.9 MB 149 MB 198 MB 0.75×
25.0 MB 314 MB 388 MB 0.81×
103.0 MB 918 MB 1098 MB 0.84×
276.5 MB 2205 MB 2556 MB 0.86×

Peak RSS of one fresh process per cell; the ratio is python-ags4's peak over laterite's, so above 1 laterite holds less. The largest rung is time-only. Read → strings measured on the pyarrow accelerator hop.

The compat door holds more than python-ags4 — those ratios sit below 1 on purpose: that is the cost of the pandas bridge, and publishing it is the point. laterite.validate and laterite.read are the recommended paths, on memory as on time.

The time ratio holds as files grow — the gap is a constant factor, not a head start that erodes. Reproduce any of this with uv run python tools/bench-vs-python-ags4.py in the repo: it generates the rungs, verifies each against a pinned SHA-256 so a change to the generator can't move the numbers unnoticed, and prints these exact tables.

Parity + clean-room

121 / 131 of python-ags4 1.2.0's own test suite passes through laterite.compat (92 %); the 10 remaining are deliberate non-closures, documented rule by rule. A weekly job compares the two public surfaces, so a function added upstream can't quietly go missing here.

compat is a package mirroring upstream's own layout, so upstream's import shapes work with one token changed:

from python_ags4 import AGS4          # becomes:
from laterite.compat import AGS4

from python_ags4.AGS4 import AGS4_to_dataframe   # becomes:
from laterite.compat.AGS4 import AGS4_to_dataframe

AGS4, check, utils and data are real, distinct submodules — so code that depends on module identity (monkeypatching check.X and expecting AGS4 not to see it) behaves as it does upstream. from laterite import compat as AGS4 also still works: the flat namespace is unchanged.

Two caveats before you swap. No top-level python_ags4 package ships, and that is deliberate and permanent — inside this wheel it would collide with the real distribution in site-packages. So the import token changes; the rest of the line does not. And compat mirrors the library API only: python-ags4's ags4_cli command is not mirrored, because laterite ships lat instead with its own JSON shapes.

The validator is clean-room from the published AGS4 specification, not adapted from another library's source — python-ags4 is LGPL-3.0, and that separation is what lets laterite ship under MIT. Details: COMPAT.md · OBSERVATIONS.md.

One engine, every stack

laterite on PyPI is the Python surface of one Rust AGS4 engine, shared across:

Surface Package Get it
Python laterite — PyPI pip install laterite
Node.js laterite — npm npm install laterite
CLI lat bundled with this wheel
DuckDB laterite_ags4 — community extension INSTALL laterite_ags4 FROM community;
Browser @laterite/ags4-wasm — npm, or the validator + data explorer npm install @laterite/ags4-wasm

Scriptable output is byte-identical across all of them, so a CI gate and a notebook can't disagree.

Docs

Full documentation — Learn, Cookbook, Concepts, and the Python API reference — at https://docs.laterite.dev/.

Metadata

Release files for laterite 0.13.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for laterite 0.13.0
File Size Uploaded
laterite-0.13.0.tar.gz 782.4 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for laterite 0.13.0
File
laterite-0.13.0-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
laterite-0.13.0-cp312-abi3-manylinux2014_x86_64.whl CPython 3.12 abi3 Linux glibc 2.17+ x86-64 Details
laterite-0.13.0-cp312-abi3-manylinux2014_aarch64.whl CPython 3.12 abi3 Linux glibc 2.17+ ARM64 Details
laterite-0.13.0-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details

Total release size: 27.7 MB

Release files / laterite-0.13.0.tar.gz

Download URL laterite-0.13.0.tar.gz
Size 782.4 kB
Tags Source
SHA-256 checksum
How to use checksums
c920f685b4981764f84c98cfe9afb81d6ba34497d35ef01e2745b39a690f510d
BLAKE2b-256 checksum
How to use checksums
b9d270c8ee6a8ae7fecc346a166ef74b8ab1f918582f76e805c3cbe188c08699
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / laterite-0.13.0-cp312-abi3-win_amd64.whl

Download URL laterite-0.13.0-cp312-abi3-win_amd64.whl
Size 7.2 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
9ffcf8dc91e14848d2614459d46a2ff0eef505f460589cb71cb63fbd24d24b5c
BLAKE2b-256 checksum
How to use checksums
14fa7d808e27f66fbb66f0f0e4822b2dc04018343fbc0ed4e229398d5ad37074
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / laterite-0.13.0-cp312-abi3-manylinux2014_x86_64.whl

Download URL laterite-0.13.0-cp312-abi3-manylinux2014_x86_64.whl
Size 6.9 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
4bb051a45773239e30ec4b7848a7890deb7bcc702a1812c412aff7f79f3f3298
BLAKE2b-256 checksum
How to use checksums
7933c23af085e6a1ffaff55de7479ed04add7e00b15d01ed64b8cfb86dba6a0f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / laterite-0.13.0-cp312-abi3-manylinux2014_aarch64.whl

Download URL laterite-0.13.0-cp312-abi3-manylinux2014_aarch64.whl
Size 6.5 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
dd88888d47cb1b7ae13c6ada83260243fe09bd4d61c720ce51a434aef4814e6a
BLAKE2b-256 checksum
How to use checksums
3e1990edb82589c15504c2f6947be6ef30a9330ca954d3033df393f759635672
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / laterite-0.13.0-cp312-abi3-macosx_11_0_arm64.whl

Download URL laterite-0.13.0-cp312-abi3-macosx_11_0_arm64.whl
Size 6.3 MB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
4bf40fafede621f811f7d45130a52c1ca1e660e4ea32c075fd74098bc346af4b
BLAKE2b-256 checksum
How to use checksums
0fa3ca00ebfbef53f792f469d9c2ecd25cd10b0e46946c88c96e8d4e095c9da3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

0.15.0

5 release files

0.14.1

5 release files

This release

0.13.0 This release

5 release files

0.12.0

5 release files

0.11.0

5 release files

0.9.0

4 release files

0.8.0

4 release files

0.7.0

4 release files

0.6.2

4 release files

0.6.1

4 release files

0.6.0

4 release files

0.5.1

4 release files

0.5.0

4 release files

0.4.0

3 release files

0.2.0

3 release files

0.1.0

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page