GeoDebug
Find the geographic bug, not just the code bug.
Geospatial correctness checks for data and workflows.
Architecture · Rule catalog · Failure cases · Contributing · Security · Changelog · Releases
Your file opens. Your geometry is valid. Your tests pass.
Your result can still be geographically wrong.
✓ file readable
✓ geometry valid
✓ pipeline completed
✓ tests passed
? geographically correct
GeoDebug checks the last question. It catches spatial-semantic failures that ordinary software tests and file validators can miss: CRS misuse, impossible coordinates, raster-grid misalignment, cross-dataset incompatibility, NoData/mask conflicts, and operations whose units do not mean what the code assumes.
See the gap
This workflow can run without raising a Python exception:
roads = gpd.read_file("roads.geojson")
buffered = roads.buffer(500)
buffered.to_file("roads_buffer.geojson")
But if the data uses a geographic CRS, 500 is interpreted in angular coordinate units rather
than meters.
GeoDebug makes that failure explicit before the operation becomes a result:
$ geodebug preflight roads.geojson --operation buffer --distance 500
roads.geojson
ERROR GEO501 Buffer distance is interpreted in angular coordinate units.
crs.kind: geographic
crs.axis_units: degree, degree
operation.distance: 500
1 error(s) · 0 warning(s) · 0 note(s) · 1 unknown
The code can be valid while the geography is not.
Install and see one failure immediately:
pip install geodebug
geodebug demo
Then check a dataset or preflight an operation:
geodebug check roads.geojson
geodebug preflight roads.geojson --operation buffer --distance 500
Where GeoDebug fits
| Layer | Question |
|---|---|
| Parser / schema | Can the data be read and interpreted structurally? |
| Geometry validity | Is the geometry structurally valid? |
| Software tests | Does the program behave as specified? |
| GeoDebug | Does the data or operation make geographic sense? |
GeoDebug does not replace GDAL, GeoPandas, Shapely, Rasterio, or PyProj. It turns spatial facts exposed by the geospatial stack into a systematic, deterministic geospatial correctness layer with stable diagnostics that can run locally or in CI.
No LLM in the core. No silent CRS guessing. No automatic "fix everything."
What it catches
GeoDebug is built for bugs that ordinary syntax checks and file validators often miss.
| Domain | Examples |
|---|---|
| CRS | missing CRS, impossible angular coordinates, data outside a projected CRS area of use |
| Vector | invalid geometry |
| Raster | singular affine transforms, NoData/mask conflicts |
| Relations | non-overlapping datasets, half-pixel raster grid shifts |
| Operations | metric buffer, planar area, or planar distance on a geographic CRS |
A file can be valid in isolation and still be wrong for the operation you are about to run. That distinction is the point.
Failure cases
Want the bugs, not the architecture?
Run five small failures that still produce valid-looking pipeline output.
Each case builds its own local fixture, runs the bad workflow, and is exercised in CI.
Mental model
source
│
▼
adapter ──► facts ──► rules ──► diagnostics ──► report
▲
│
dataset / relation / operation
Three questions drive the engine:
- Dataset — is this dataset internally spatially plausible?
- Relation — are these datasets compatible with each other?
- Operation — is this operation semantically valid for these coordinates and units?
Adapters observe. Rules diagnose. Policy decides what fails the build.
CLI
| Command | Purpose |
|---|---|
geodebug demo |
Run a built-in 500 m buffer failure |
geodebug inspect DATA |
Show normalized spatial facts without diagnosing |
geodebug check DATA |
Run dataset diagnostics |
geodebug compare A B |
Run dataset + relational diagnostics |
geodebug preflight DATA --operation ... |
Check operation semantics before execution |
geodebug rules list |
Inspect the built-in rule corpus |
geodebug schema |
Print the canonical report schema |
Full geometry or raster scans are opt-in:
geodebug check landcover.tif --deep
Machine-readable output is first-class:
geodebug check roads.geojson --format json
Install
pip install geodebug
geodebug demo
Need every adapter:
pip install "geodebug[all]"
Optional extras keep the core small:
| Extra | Support |
|---|---|
| core | GeoJSON |
vector |
GeoPackage, Shapefile, FlatGeobuf |
raster |
GeoTIFF, COG, VRT |
parquet |
GeoParquet |
geopandas |
in-memory GeoDataFrame |
all |
all optional adapters |
Requires Python 3.12+.
Diagnostic corpus
GeoDebug ships 11 built-in rules with stable IDs.
| Family | Rules |
|---|---|
| CRS | GEO101 missing CRS · GEO103 coordinate range · GEO105 area of use |
| Vector | GEO201 invalid geometry |
| Raster | GEO301 affine transform · GEO304 NoData/mask conflict |
| Relations | GEO402 spatial overlap · GEO404 grid alignment |
| Operations | GEO501 buffer · GEO502 area · GEO503 distance/length |
Every released rule has an explicit contract for PASS, FAIL, UNKNOWN, and
NOT_APPLICABLE.
geodebug rules show GEO404
Project policy
Projects can tune severity, disable rules, and suppress known exceptions without changing rule truth values.
schema_version = "1"
profile = "default"
fail_on = "error"
[rules]
disable = []
[rules.severity]
GEO101 = "error"
[[suppress]]
rule = "GEO103"
path = "legacy/*.geojson"
reason = "Known upstream coordinate convention"
expires = 2027-01-01
GeoDebug searches the current directory and its parents for .geodebug.toml. Use --config
to select one explicitly.
Profiles are intentionally simple:
default— rule defaultsstrict— warnings become errorsexploratory— warnings become notes
Explicit overrides win. Suppressions require a reason and may expire.
Contracts over vibes
A few things GeoDebug refuses to blur:
- Adapters do not diagnose. They normalize observations into facts.
- Rules do not perform I/O. They evaluate spatial invariants.
UNKNOWNis notPASS. Missing evidence stays missing.- Policy does not rewrite truth. It changes severity or visibility, not the rule result.
- Expensive scans are explicit.
--deepmeans--deep. - Automatic repair is conservative. A geometrically valid output is not necessarily a scientifically valid fix.
The canonical report schema is versioned independently at 1.0.0; project config uses schema
version 1.
geodebug schema
geodebug schema --kind config
Development
git clone https://github.com/GeoGeekLab/geodebug.git
cd geodebug
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev,all]'
ruff check .
mypy src/geodebug
pytest
The release gate checks more than unit tests: four-state rule contracts, golden
must_not_report cases, metamorphic corrections, clean-corpus silence, cross-platform smoke
tests, and installation from a freshly built wheel.
See Architecture, Diagnostic rules, and Releasing for the internals.
Contributing
Contributions should start from a concrete geospatial failure mode or a concrete engineering improvement. New diagnostics must preserve the four-state rule contract and include false-positive coverage where adjacent rules can cascade.
See CONTRIBUTING.md.
Security
Please report vulnerabilities privately through GitHub's security reporting features when available. Do not publish exploit details in a public issue before a fix or mitigation is available.
See SECURITY.md.
License
Geo to see. Geek to build.
GeoGeekLab
Release files for geodebug 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| geodebug-0.1.2.tar.gz | 54.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| geodebug-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 105.7 kB
Release files / geodebug-0.1.2.tar.gz
| Download URL | geodebug-0.1.2.tar.gz |
|---|---|
| Size | 54.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3a644d5d717fa752171fc757274eff717d54e07adcc2f9f5924cda300620f643
|
|
BLAKE2b-256 checksum How to use checksums |
19bf116d5f5aba2334abe385299473e7ea5a0af1573f1f2312b6460b5e2bc9e8
|
| 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 20, 2026.
Transparency logRelease files / geodebug-0.1.2-py3-none-any.whl
| Download URL | geodebug-0.1.2-py3-none-any.whl |
|---|---|
| Size | 51.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bca62b15637550d2f4f9c61a806b443e8fcfcbeabc13fbb757c334ea74a83d1b
|
|
BLAKE2b-256 checksum How to use checksums |
42c15cbc4b8b5d9fe0f4bfde91a594979c0f14aefe5aa6f0dd139258bbf23910
|
| 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 20, 2026.
Transparency log