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.14.1

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.14.1
File Size Uploaded
laterite-0.14.1.tar.gz 850.4 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for laterite 0.14.1
File
laterite-0.14.1-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
laterite-0.14.1-cp312-abi3-manylinux2014_x86_64.whl CPython 3.12 abi3 Linux glibc 2.17+ x86-64 Details
laterite-0.14.1-cp312-abi3-manylinux2014_aarch64.whl CPython 3.12 abi3 Linux glibc 2.17+ ARM64 Details
laterite-0.14.1-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.14.1.tar.gz

Download URL laterite-0.14.1.tar.gz
Size 850.4 kB
Tags Source
SHA-256 checksum
How to use checksums
3dcea23e7d5bbc1d4e99984735f19950a783573eb1b7a04806a78d0642fdba3d
BLAKE2b-256 checksum
How to use checksums
a6e474169b2880bd9f4daad0ce6b2b7eff6f658ea76a2f8c4fca285f053c8e4e
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 11, 2026.

Transparency log

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

Download URL laterite-0.14.1-cp312-abi3-win_amd64.whl
Size 7.4 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
d293557cac757e90a94a7e51fc1eeebf1f65d6c7970edd21e3714c07c0005445
BLAKE2b-256 checksum
How to use checksums
d2c5f405180b1b72c5ed1494a5ba0ba9cb3469ca81da82c008a33b5a636b5458
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 11, 2026.

Transparency log

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

Download URL laterite-0.14.1-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
9b9d4f718ade7da7c17e819e00d963293dd963d933bac1b53c961f8332a4a8a0
BLAKE2b-256 checksum
How to use checksums
d5ba5d216a7411c5a372b66c36ddb08fdda19de7b037af53c7b0b6a09b48218f
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 11, 2026.

Transparency log

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

Download URL laterite-0.14.1-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
da6b92b6966c4f08eaa2679792d8ed70be7703190dc10fc38af7cfb66dace3f0
BLAKE2b-256 checksum
How to use checksums
26fc3b25f4426840eb0629490337a08556621d08a6ed9e47b7251f2c9004ba1c
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 11, 2026.

Transparency log

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

Download URL laterite-0.14.1-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
4f5287ef8eabbbb26ad7a771ff60434d8628a4606359c16fd992f07403e4dae6
BLAKE2b-256 checksum
How to use checksums
5b063910f1e66c546c4c86194caf1e313de696d9c6692cdf45c1d8d55b9916a4
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 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.15.0

5 release files

This release

0.14.1 This release

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