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.15.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.15.0
File Size Uploaded
laterite-0.15.0.tar.gz 852.3 kB Details

Built distributions (wheels)

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

Total release size: 28.4 MB

Release files / laterite-0.15.0.tar.gz

Download URL laterite-0.15.0.tar.gz
Size 852.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a078d6a14953df40304eb3078ed14f87c14a4a3724f0cb02d35b1137a171510b
BLAKE2b-256 checksum
How to use checksums
cc9eaeb03435d562b3e25dc5999b2466cc63b82e5c785c118faa78396ed52603
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 Oct 12, 2026.

Transparency log

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

Download URL laterite-0.15.0-cp312-abi3-win_amd64.whl
Size 7.4 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
9c0ebc3e52aeb8e7940f935e1f040a21bf6a1230f35710da7c1d83cf6eab2ee4
BLAKE2b-256 checksum
How to use checksums
7f0d30df3495cf576391cc7a32f4a0c75348a6b49563679e563f8f4d1c0559dd
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 Oct 12, 2026.

Transparency log

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

Download URL laterite-0.15.0-cp312-abi3-manylinux2014_x86_64.whl
Size 7.1 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
f2530b4f46fbd8d9e429dfab4737a2128678dca972efe6746fb4788b8891edfb
BLAKE2b-256 checksum
How to use checksums
ec43a3f5d23fe8ff95d007246b5265df8eb00618013f898c3230dc7546952f98
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 Oct 12, 2026.

Transparency log

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

Download URL laterite-0.15.0-cp312-abi3-manylinux2014_aarch64.whl
Size 6.7 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
da05cc1b0b5b098394a494c58c98c67a6f675d4835868635c1ee4547a6ab35ad
BLAKE2b-256 checksum
How to use checksums
01f6f303bb955d74a4660a4459dc4eb6aa283c940ff0eee9c9f8538658c1c14e
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 Oct 12, 2026.

Transparency log

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

Download URL laterite-0.15.0-cp312-abi3-macosx_11_0_arm64.whl
Size 6.4 MB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
2b55c432498fbec2c821d35b9cf528eadfd56448e9ba0894815c70d02c7b68f7
BLAKE2b-256 checksum
How to use checksums
dae5b14ab433f9e9d591d2bdd98cdb91acdfd00fa51b6433bf422df2453572bc
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 Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.15.0 This release

5 release files

0.14.1

5 release files

0.13.0

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