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: python_ags4 → laterite.compat.
from python_ags4 import AGS4 becomes from laterite.compat import AGS4, and
your code keeps working.
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.8.0. Both agree on the findings.
Validation
| File | python-ags4 check_file |
laterite.validate |
speedup |
|---|---|---|---|
| 4.9 MB · 459 BH | 1.5 s | 50 ms | 30.0× |
| 24.9 MB · 2,219 BH | 3.7 s | 266 ms | 13.9× |
| 102.7 MB · 8,872 BH | 12.3 s | 1.1 s | 11.7× |
| 275.5 MB · 22,813 BH | 34.1 s | 2.6 s | 13.0× |
| 549.7 MB · 45,107 BH | 70.0 s | 5.4 s | 12.9× |
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 | 187 ms | 26 ms | 7.2× |
| 24.9 MB | 811 ms | 136 ms | 6.0× |
| 102.7 MB | 3.4 s | 541 ms | 6.3× |
| 275.5 MB | 8.9 s | 1.4 s | 6.4× |
| 549.7 MB | 17.5 s | 2.9 s | 6.0× |
Read → strings — like for like, both returning pandas frames of text.
| File | python-ags4 AGS4_to_dataframe |
laterite.compat |
speedup |
|---|---|---|---|
| 4.9 MB | 144 ms | 49 ms | 2.9× |
| 24.9 MB | 718 ms | 206 ms | 3.5× |
| 102.7 MB | 2.8 s | 870 ms | 3.2× |
| 275.5 MB | 7.3 s | 2.2 s | 3.3× |
| 549.7 MB | 15.2 s | 4.6 s | 3.3× |
The 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/.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file laterite-0.12.0.tar.gz.
File metadata
- Download URL: laterite-0.12.0.tar.gz
- Upload date:
- Size: 711.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dfdb0f8873ebf713cbdbb1953d86f09fa36083021d883280251b116c8706b015
|
|
| MD5 |
bc8df5b991b628b2e4892c8ea72cd369
|
|
| BLAKE2b-256 |
d5cca7b673f48ea827f528cfc2bf8de720da00e1bae71cb043e4886b2b21cbc4
|
Provenance
The following attestation bundles were made for laterite-0.12.0.tar.gz:
Publisher:
release.yml on niko86/laterite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
laterite-0.12.0.tar.gz -
Subject digest:
dfdb0f8873ebf713cbdbb1953d86f09fa36083021d883280251b116c8706b015 - Sigstore transparency entry: 2637834657
- Sigstore integration time:
-
Permalink:
niko86/laterite@31d8e548de98b3b70fa3368789107e60dc676c25 -
Branch / Tag:
refs/tags/v0.12.0 - Owner: https://github.com/niko86
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@31d8e548de98b3b70fa3368789107e60dc676c25 -
Trigger Event:
push
-
Statement type:
File details
Details for the file laterite-0.12.0-cp312-abi3-win_amd64.whl.
File metadata
- Download URL: laterite-0.12.0-cp312-abi3-win_amd64.whl
- Upload date:
- Size: 7.1 MB
- Tags: CPython 3.12+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ace8fc3dd2d8806584dd905658c4cce7b40c4de02cb95685f5b17c5fa6d9bc2
|
|
| MD5 |
3f25f6438e42d62a774785928dc9ef07
|
|
| BLAKE2b-256 |
b6c09a750827b1dbf8cd202538bf4efc824945499aa0f4600b8dd38c1c003509
|
Provenance
The following attestation bundles were made for laterite-0.12.0-cp312-abi3-win_amd64.whl:
Publisher:
release.yml on niko86/laterite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
laterite-0.12.0-cp312-abi3-win_amd64.whl -
Subject digest:
3ace8fc3dd2d8806584dd905658c4cce7b40c4de02cb95685f5b17c5fa6d9bc2 - Sigstore transparency entry: 2637834935
- Sigstore integration time:
-
Permalink:
niko86/laterite@31d8e548de98b3b70fa3368789107e60dc676c25 -
Branch / Tag:
refs/tags/v0.12.0 - Owner: https://github.com/niko86
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@31d8e548de98b3b70fa3368789107e60dc676c25 -
Trigger Event:
push
-
Statement type:
File details
Details for the file laterite-0.12.0-cp312-abi3-manylinux2014_x86_64.whl.
File metadata
- Download URL: laterite-0.12.0-cp312-abi3-manylinux2014_x86_64.whl
- Upload date:
- Size: 6.9 MB
- Tags: CPython 3.12+
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e33652820f7588b2b4753ac81c2c0cb7dde767c166c16e0565d9805bce67f389
|
|
| MD5 |
e57cb9e4379eafb60a4cc04dca1fcb83
|
|
| BLAKE2b-256 |
ad9f709dc71f9b86b4a017d7291b9461803b6799f5fd10b0eb4017d83777bcb2
|
Provenance
The following attestation bundles were made for laterite-0.12.0-cp312-abi3-manylinux2014_x86_64.whl:
Publisher:
release.yml on niko86/laterite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
laterite-0.12.0-cp312-abi3-manylinux2014_x86_64.whl -
Subject digest:
e33652820f7588b2b4753ac81c2c0cb7dde767c166c16e0565d9805bce67f389 - Sigstore transparency entry: 2637835779
- Sigstore integration time:
-
Permalink:
niko86/laterite@31d8e548de98b3b70fa3368789107e60dc676c25 -
Branch / Tag:
refs/tags/v0.12.0 - Owner: https://github.com/niko86
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@31d8e548de98b3b70fa3368789107e60dc676c25 -
Trigger Event:
push
-
Statement type:
File details
Details for the file laterite-0.12.0-cp312-abi3-manylinux2014_aarch64.whl.
File metadata
- Download URL: laterite-0.12.0-cp312-abi3-manylinux2014_aarch64.whl
- Upload date:
- Size: 6.5 MB
- Tags: CPython 3.12+
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb6edeca2e16b59107a233490fcf93c447de575e146ec7b92f87659c19914378
|
|
| MD5 |
9c66c00cd95cf48d1cf868153989d2c1
|
|
| BLAKE2b-256 |
df45812cb16e2bb56c97e607a72813f75b18549a864c8ca75f8ba31039616016
|
Provenance
The following attestation bundles were made for laterite-0.12.0-cp312-abi3-manylinux2014_aarch64.whl:
Publisher:
release.yml on niko86/laterite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
laterite-0.12.0-cp312-abi3-manylinux2014_aarch64.whl -
Subject digest:
cb6edeca2e16b59107a233490fcf93c447de575e146ec7b92f87659c19914378 - Sigstore transparency entry: 2637835424
- Sigstore integration time:
-
Permalink:
niko86/laterite@31d8e548de98b3b70fa3368789107e60dc676c25 -
Branch / Tag:
refs/tags/v0.12.0 - Owner: https://github.com/niko86
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@31d8e548de98b3b70fa3368789107e60dc676c25 -
Trigger Event:
push
-
Statement type:
File details
Details for the file laterite-0.12.0-cp312-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: laterite-0.12.0-cp312-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 6.2 MB
- Tags: CPython 3.12+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98db2e988eec9701001dcc3343031b743c5b9e5c7b9407563656ed1e89d008d3
|
|
| MD5 |
226d1fb52a80700b220fdf0076b6684d
|
|
| BLAKE2b-256 |
8d117109d6dd43d3990224ffe170f9e226d685190d66a0349411f64cf138d6f0
|
Provenance
The following attestation bundles were made for laterite-0.12.0-cp312-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on niko86/laterite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
laterite-0.12.0-cp312-abi3-macosx_11_0_arm64.whl -
Subject digest:
98db2e988eec9701001dcc3343031b743c5b9e5c7b9407563656ed1e89d008d3 - Sigstore transparency entry: 2637835176
- Sigstore integration time:
-
Permalink:
niko86/laterite@31d8e548de98b3b70fa3368789107e60dc676c25 -
Branch / Tag:
refs/tags/v0.12.0 - Owner: https://github.com/niko86
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@31d8e548de98b3b70fa3368789107e60dc676c25 -
Trigger Event:
push
-
Statement type: