Add Mol multi-zone operational validation

This commit is contained in:
Codex
2026-07-13 18:12:48 +02:00
parent 4539e92bcf
commit 2c16ff2bc0
16 changed files with 605 additions and 12 deletions
+13
View File
@@ -460,6 +460,13 @@ manifests generated for AI handoff include source CRS metadata so pixel-space
model outputs can be transformed to WGS84 GeoJSON coordinates. Current V1 upload
support is limited to GeoTIFF-style rasters and GeoJSON/JSON reference vectors.
When `REAL_AREA_BBOX=minx,miny,maxx,maxy` is supplied, the same workflow also
persists an EPSG:4326 project Area before uploading data. `REAL_AREA_NAME` and
`REAL_PROJECT_REGION` retain operator context. The multi-sample runner fills
these values from manifest `wgs84_bbox` and municipality metadata, so generated
projects are immediately usable in the map without an alternate persistence
path or API contract.
To prepare the documented operator sample corpus inside the all-in-one runtime
container, run:
@@ -477,6 +484,12 @@ empty GRB FeatureCollections for negative-tile training; normal reference AOIs
still fail when GRB returns no buildings. These are runtime artifacts only and
are not committed to Git.
Mol additionally has operational holdouts for Achterbos, Gompel, Donk and
Postel, with Mol center as the historical baseline and Postel-bos as a separate
background control. Prepare and execute that pack with the documented
`prepare_operator_real_data_samples.py` and
`run_mol_operational_validation.sh` commands in `scripts/README.md`.
The current recommended local building model is
`geointel-building-yolov8s-smallbld-minpx3-img640-ft30-pt` with tile size
`512`, overlap `64` and confidence threshold `0.15`. Its SHA256 is
@@ -75,6 +75,7 @@ def test_all_in_one_dockerfile_copies_operator_scripts_for_runtime_use() -> None
"verify_real_data_detection_qa_workflow.sh",
"run_detection_quality_matrix.sh",
"run_multi_sample_detection_quality_matrix.sh",
"run_mol_operational_validation.sh",
"export_detection_calibration_evidence.sh",
"assemble_detection_calibration_evidence_portfolio.sh",
"build_fixed_threshold_evidence_portfolio_inputs.py",
@@ -0,0 +1,114 @@
from __future__ import annotations
import importlib.util
import math
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[2]
def load_sample_preparer():
script_path = ROOT / "scripts" / "prepare_operator_real_data_samples.py"
spec = importlib.util.spec_from_file_location("mol_operational_sample_preparer", script_path)
assert spec is not None
assert spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def distance_m(left, right) -> float:
radius_m = 6_371_008.8
left_lat = math.radians(left.center_lat)
right_lat = math.radians(right.center_lat)
delta_lat = right_lat - left_lat
delta_lon = math.radians(right.center_lon - left.center_lon)
haversine = (
math.sin(delta_lat / 2) ** 2
+ math.cos(left_lat) * math.cos(right_lat) * math.sin(delta_lon / 2) ** 2
)
return 2 * radius_m * math.asin(math.sqrt(haversine))
def test_mol_operational_registry_has_distinct_real_world_zones_and_holdouts() -> None:
module = load_sample_preparer()
expected = {
"mol": "center",
"mol_achterbos": "residential",
"mol_gompel": "mixed_settlement",
"mol_donk": "canal_industrial",
"mol_postel": "rural_village",
}
assert tuple(expected) == module.MOL_OPERATIONAL_SAMPLE_SLUGS
assert module.MOL_BACKGROUND_CONTROL_SAMPLE_SLUGS == ("postel_bos",)
assert module.MOL_OPERATIONAL_VALIDATION_SAMPLE_SLUGS == frozenset(expected) - {"mol"}
samples = [module.SAMPLES[slug] for slug in expected]
assert all(sample.municipality == "Mol" for sample in samples)
assert {sample.operational_zone for sample in samples} == set(expected.values())
assert all(5.09 < sample.center_lon < 5.20 for sample in samples)
assert all(51.18 < sample.center_lat < 51.30 for sample in samples)
assert all(
module.recommended_split_for_sample(module.SAMPLES[slug]) == "val"
for slug in module.MOL_OPERATIONAL_VALIDATION_SAMPLE_SLUGS
)
new_holdouts = [module.SAMPLES[slug] for slug in module.MOL_OPERATIONAL_VALIDATION_SAMPLE_SLUGS]
assert min(
distance_m(left, right)
for index, left in enumerate(new_holdouts)
for right in new_holdouts[index + 1 :]
) >= 1_500
def test_mol_sample_metadata_is_persisted_in_operator_manifest_records(tmp_path: Path, monkeypatch) -> None:
module = load_sample_preparer()
sample = module.SAMPLES["mol_achterbos"]
monkeypatch.setattr(module, "sample_bounds", lambda _sample: ((1.0, 2.0, 3.0, 4.0), [5.0, 51.0, 5.1, 51.1]))
monkeypatch.setattr(module, "sample_artifact_paths", lambda _sample, _output: (tmp_path / "ortho.tif", tmp_path / "reference.geojson"))
monkeypatch.setattr(module, "fetch_orthophoto", lambda *_args: "https://example.test/ortho")
monkeypatch.setattr(module, "fetch_reference", lambda *_args, **_kwargs: ("https://example.test/grb", 42))
monkeypatch.setattr(module, "raster_summary", lambda _path: {"crs": "EPSG:31370"})
prepared = module.prepare_sample(sample, tmp_path, force=True)
assert prepared["municipality"] == "Mol"
assert prepared["operational_zone"] == "residential"
assert prepared["recommended_split"] == "val"
assert prepared["wgs84_bbox"] == [5.0, 51.0, 5.1, 51.1]
def test_real_data_matrix_propagates_project_region_and_persisted_area() -> None:
workflow = (ROOT / "scripts" / "verify_real_data_detection_qa_workflow.sh").read_text(encoding="utf-8")
matrix = (ROOT / "scripts" / "run_detection_quality_matrix.sh").read_text(encoding="utf-8")
multi = (ROOT / "scripts" / "run_multi_sample_detection_quality_matrix.sh").read_text(encoding="utf-8")
assert 'REAL_PROJECT_REGION="${REAL_PROJECT_REGION:-Kempen}"' in workflow
assert 'REAL_AREA_BBOX="${REAL_AREA_BBOX:-}"' in workflow
assert '/api/v1/projects/${project_id}/areas' in workflow
assert 'echo "Area: ${area_id}"' in workflow
assert 'REAL_PROJECT_REGION="${REAL_PROJECT_REGION}"' in matrix
assert 'REAL_AREA_BBOX="${REAL_AREA_BBOX}"' in matrix
assert 'REAL_AREA_BBOX="${wgs84_bbox}"' in multi
assert 'REAL_AREA_NAME="${sample_slug} AOI"' in multi
assert 'project_region="Mol, Kempen"' in multi
assert 'REAL_PROJECT_REGION="${project_region}"' in multi
def test_mol_operational_runner_uses_real_positive_qa_and_background_paths() -> None:
runner = (ROOT / "scripts" / "run_mol_operational_validation.sh").read_text(encoding="utf-8")
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8")
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
assert "mol_achterbos mol_gompel mol_donk mol_postel" in runner
assert "run_multi_sample_detection_quality_matrix.sh" in runner
assert "run_operator_hard_negative_detection_matrix.sh" in runner
assert "mol_operational_validation_summary.json" in runner
assert "fixture_mode" not in runner
assert "manual-fixture-detector" not in runner
assert "bash -n scripts/run_mol_operational_validation.sh" in readiness
assert "COPY scripts/run_mol_operational_validation.sh" in dockerfile