Metadata-Version: 2.4
Name: geoassert
Version: 0.4.0
Summary: Data contracts for geospatial pipelines
Project-URL: Homepage, https://morphenr.github.io/geoassert
Project-URL: Documentation, https://morphenr.github.io/geoassert
Project-URL: Repository, https://github.com/Morphenr/geoassert
Project-URL: Issues, https://github.com/Morphenr/geoassert/issues
Project-URL: Changelog, https://github.com/Morphenr/geoassert/blob/main/CHANGELOG.md
Author-email: Marten Reichow <marten123@live.de>
License: MIT License
        
        Copyright (c) 2026 Morphenr
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: cli,data-contracts,data-quality,data-validation,etl,geoparquet,geospatial,gis
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: pyarrow>=14.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.12
Provides-Extra: all
Requires-Dist: duckdb>=0.10; extra == 'all'
Requires-Dist: geopandas>=0.14; extra == 'all'
Requires-Dist: google-cloud-bigquery-storage>=2.0; extra == 'all'
Requires-Dist: google-cloud-bigquery>=3.0; extra == 'all'
Requires-Dist: polars>=0.20; extra == 'all'
Requires-Dist: psycopg2-binary>=2.9; extra == 'all'
Requires-Dist: shapely>=2.0; extra == 'all'
Requires-Dist: snowflake-connector-python>=3.0; extra == 'all'
Provides-Extra: bigquery
Requires-Dist: google-cloud-bigquery-storage>=2.0; extra == 'bigquery'
Requires-Dist: google-cloud-bigquery>=3.0; extra == 'bigquery'
Requires-Dist: pyarrow>=14.0; extra == 'bigquery'
Provides-Extra: dbt
Provides-Extra: dev
Requires-Dist: duckdb>=0.10; extra == 'dev'
Requires-Dist: geopandas>=0.14; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pre-commit>=3.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: shapely>=2.0; extra == 'dev'
Requires-Dist: types-pyyaml; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocs>=1.6; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
Provides-Extra: duckdb
Requires-Dist: duckdb>=0.10; extra == 'duckdb'
Provides-Extra: geopandas
Requires-Dist: geopandas>=0.14; extra == 'geopandas'
Requires-Dist: shapely>=2.0; extra == 'geopandas'
Provides-Extra: polars
Requires-Dist: polars>=0.20; extra == 'polars'
Provides-Extra: postgis
Requires-Dist: psycopg2-binary>=2.9; extra == 'postgis'
Provides-Extra: shapely
Requires-Dist: shapely>=2.0; extra == 'shapely'
Provides-Extra: snowflake
Requires-Dist: snowflake-connector-python>=3.0; extra == 'snowflake'
Description-Content-Type: text/markdown

# geoassert

Data contracts for geospatial pipelines.

`geoassert` validates GeoParquet and other geospatial datasets before bad geometry, CRS mistakes, broken metadata, or spatial coverage gaps reach downstream maps, models, and analytics.

![geoassert demo](demo/demo.gif)

---

## Install

```bash
pip install geoassert
```

Optional extras for richer checks:

```bash
pip install "geoassert[shapely]"    # geometry validity, type checks
pip install "geoassert[geopandas]"  # broader file format support
pip install "geoassert[duckdb]"     # scalable local spatial SQL
pip install "geoassert[all]"        # everything
```

---

## Quickstart

```bash
# Profile a dataset
geoassert profile data/buildings.parquet

# Generate a starter contract
geoassert init-contract data/buildings.parquet > contracts/buildings.yml

# Validate against a contract
geoassert validate data/buildings.parquet --contract contracts/buildings.yml

# Check GeoParquet metadata
geoassert geoparquet check data/buildings.parquet
```

---

## Example output

```
─── buildings.parquet ──────────────────────────────────────────
  PASS  geoparquet.geo_metadata
  PASS  geoparquet.primary_column
  PASS  geoparquet.column_in_schema
  PASS  geoparquet.encoding
  PASS  geoparquet.crs_parseable
  PASS  crs.exists
  PASS  crs.match
  PASS  bounds.within
  FAIL  geometry.valid
  WARN  geometry.empty

Failures:
  geometry.valid
    Expected:   all geometries valid
    Observed:   1,284 invalid geometries
    Rows:       1,284
    Suggestion: Inspect invalid geometries or repair them explicitly with a
                make-valid workflow.
```

---

## Contract example

```yaml
geoassert_version: "0.1"

dataset: buildings

source:
  path: data/buildings.parquet
  format: geoparquet

geometry:
  column: geometry
  type:
    - Polygon
    - MultiPolygon
  crs: EPSG:4326
  valid: true
  allow_empty: false

bounds:
  within:
    bbox: [-8.65, 49.86, 1.76, 60.86]   # Great Britain

attributes:
  building_id:
    nullable: false
    unique: true
  height_m:
    nullable: true
    min: 0
    max: 400
```

---

## Failure messages

Every failed check explains what failed, what was expected, what was observed, and how to fix it:

```json
{
  "check": "crs.match",
  "status": "fail",
  "severity": "error",
  "expected": "EPSG:4326",
  "observed": "OGC:CRS84",
  "message": "CRS metadata differs from contract.",
  "why_it_matters": "Coordinate axis order and downstream spatial operations may behave differently.",
  "suggestion": "Normalise CRS metadata before export or set allow_equivalent_crs: true if intentional."
}
```

---

## GitHub Actions

```yaml
- name: Validate geospatial datasets
  run: |
    pip install geoassert
    geoassert validate data/buildings.parquet \
      --contract contracts/buildings.yml \
      --format github
```

Use `--format github` to emit `::error` and `::warning` annotations in the Actions UI.

---

## Python API

```python
import geoassert as ga

result = ga.validate(
    "data/buildings.parquet",
    contract="contracts/buildings.yml",
)

if not result.passed:
    print(result.to_markdown())

assert result.passed
```

---

## Checks (v0.1)

| Category | Check |
|---|---|
| GeoParquet | geo metadata, primary column, schema consistency, encoding, CRS parseable, geometry types |
| CRS | CRS exists, CRS matches contract |
| Bounds | bounds available, dataset within expected bbox |
| Geometry | column exists, validity, empty geometries, type distribution |
| Attributes | column exists, nullable, unique, min/max range |

---

## Exit codes

| Code | Meaning |
|---:|---|
| 0 | all checks passed |
| 1 | validation failures |
| 2 | invalid contract / configuration |
| 3 | unreadable input data |
| 4 | internal error |

---

## Roadmap

**v0.2** — GitHub Actions annotations, pre-commit hook, JUnit XML, sampling, configurable severity  
**v0.3** — DuckDB backend, cloud paths, partition validation, row group checks  
**v0.4** — dbt package, PostGIS, BigQuery GIS, Snowflake  
**v0.5** — spatial drift checks, H3 coverage, HTML reports

---

## Non-goals

`geoassert` is not a GIS analysis library, a GeoPandas replacement, or a spatial database. It sits above them as a validation layer.

---

## Contributing

Bug reports and check requests welcome — use the GitHub issue templates.

```bash
git clone https://github.com/Morphenr/geoassert
cd geoassert
uv sync --extra dev
uv run pytest
```
