Build governed Belgian training corpus pipeline
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-26 22:14:28 +02:00
parent 6323885a8d
commit 68d3fa34e3
5 changed files with 447 additions and 2 deletions
@@ -0,0 +1,71 @@
from __future__ import annotations
import importlib.util
import json
from pathlib import Path
import numpy as np
import rasterio
from rasterio.transform import from_origin
ROOT = Path(__file__).resolve().parents[2]
SCRIPT = ROOT / "scripts" / "normalize_belgium_building_labels.py"
SPEC = importlib.util.spec_from_file_location("normalize_buildings", SCRIPT)
assert SPEC and SPEC.loader
module = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(module)
def test_normalizer_retains_native_identity_and_records_rejections(tmp_path: Path) -> None:
raster_path = tmp_path / "image.tif"
with rasterio.open(
raster_path,
"w",
driver="GTiff",
width=100,
height=100,
count=3,
dtype="uint8",
crs="EPSG:4326",
transform=from_origin(4.0, 51.0, 0.001, 0.001),
) as dataset:
dataset.write(np.zeros((3, 100, 100), dtype="uint8"))
valid = {
"type": "Feature",
"id": "native-1",
"properties": {"TYPE": "main building"},
"geometry": {"type": "Polygon", "coordinates": [[[4.01, 50.99], [4.02, 50.99], [4.02, 50.98], [4.01, 50.98], [4.01, 50.99]]]},
}
duplicate = json.loads(json.dumps(valid))
duplicate["id"] = "native-2"
canopy = json.loads(json.dumps(valid))
canopy["id"] = "native-3"
canopy["properties"]["TYPE"] = "canopy"
tiny = json.loads(json.dumps(valid))
tiny["id"] = "native-4"
tiny["geometry"] = {"type": "Polygon", "coordinates": [[[4.03, 50.97], [4.031, 50.97], [4.031, 50.969], [4.03, 50.969], [4.03, 50.97]]]}
reference_path = tmp_path / "reference.geojson"
reference_path.write_text(json.dumps({"type": "FeatureCollection", "features": [valid, duplicate, canopy, tiny]}), encoding="utf-8")
normalized, audit = module.normalize(
reference_path=reference_path,
raster_path=raster_path,
source_name="urbis",
min_label_px=3,
imagery_observed_at="2026-01-01T00:00:00Z",
reference_observed_at="2025-12-01T00:00:00Z",
)
assert len(normalized["features"]) == 1
properties = normalized["features"][0]["properties"]
assert properties["canonical_class"] == "building"
assert properties["source_name"] == "urbis"
assert properties["source_feature_id"] == "native-1"
assert properties["source_class"] == "main building"
assert audit["decision_counts"] == {
"accepted": 1,
"below_resolvable_pixel_size": 1,
"duplicate_geometry": 1,
"excluded_canopy": 1,
}
assert audit["temporal_mismatch_days"] == 31