feat: add governed hydrology and historical imagery
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-15 12:17:45 +02:00
parent 5b1156e989
commit fb38eb3e91
32 changed files with 1646 additions and 55 deletions
@@ -86,7 +86,13 @@ class FakeImageResponse:
return self.content[:limit]
def _selection_payload(*, side_m: float = 512.0, force_refresh: bool = True, area_id=None) -> OrthophotoAcquireRequest:
def _selection_payload(
*,
side_m: float = 512.0,
force_refresh: bool = True,
area_id=None,
product_key: str = "most_recent",
) -> OrthophotoAcquireRequest:
west, south = 199_000.0, 210_000.0
transformer = Transformer.from_crs("EPSG:31370", "EPSG:4326", always_xy=True)
min_lon, min_lat = transformer.transform(west, south)
@@ -100,6 +106,7 @@ def _selection_payload(*, side_m: float = 512.0, force_refresh: bool = True, are
"crs": "EPSG:4326",
},
area_id=area_id,
product_key=product_key,
force_refresh=force_refresh,
)
@@ -136,6 +143,25 @@ def test_orthophoto_request_is_bounded_and_uses_official_wms_contract() -> None:
assert len(prepared["request_hash"]) == 64
def test_orthophoto_product_registry_exposes_only_governed_official_layers() -> None:
settings = Settings(_env_file=None)
products = OrthophotoAcquisitionService.list_products(settings)
keys = [item["key"] for item in products]
assert keys[0] == "most_recent"
assert {"2025", "2012", "2008_2011", "2000_2003", "1979_1990", "1971"}.issubset(keys)
assert next(item for item in products if item["key"] == "most_recent")["supports_detection"] is True
assert all(item["supports_detection"] is False for item in products if item["key"] != "most_recent")
prepared = OrthophotoAcquisitionService._prepared_request(_selection_payload(product_key="1971"), settings)
assert prepared["params"]["LAYERS"] == "OKZPAN71VL"
assert prepared["wms_url"] == "https://geo.api.vlaanderen.be/OKZ/wms"
with pytest.raises(AppError) as exc_info:
OrthophotoAcquisitionService._prepared_request(_selection_payload(product_key="arbitrary-layer"), settings)
assert exc_info.value.code == "ORTHOPHOTO_PRODUCT_NOT_SUPPORTED"
@pytest.mark.parametrize(
("side_m", "expected_code"),
[(64.0, "ORTHOPHOTO_SELECTION_TOO_SMALL"), (1_200.0, "ORTHOPHOTO_SELECTION_TOO_LARGE")],
@@ -240,7 +266,7 @@ def test_orthophoto_acquisition_reuses_fresh_exact_request_without_provider_call
cached = Dataset(
id=uuid4(),
project_id=project_id,
name=f"orthofoto_selectie_{prepared['request_hash'][:12]}.tif",
name=f"orthofoto_most_recent_{prepared['request_hash'][:12]}.tif",
dataset_type="raster",
source="Digitaal Vlaanderen",
source_name="digitaal_vlaanderen_orthophoto",
@@ -277,6 +303,54 @@ def test_orthophoto_provider_rejects_non_image_response() -> None:
assert exc_info.value.code == "ORTHOPHOTO_PROVIDER_INVALID_RESPONSE"
def test_historical_orthophoto_persists_temporal_product_provenance(tmp_path) -> None:
project_id = uuid4()
payload = _selection_payload(product_key="2020")
db = FakeSession({(Project, project_id): Project(id=project_id, name="Mol")})
settings = Settings(_env_file=None, storage_root=str(tmp_path), orthophoto_resolution_m=1.0)
prepared = OrthophotoAcquisitionService._prepared_request(payload, settings)
response = FakeImageResponse(_source_tiff(prepared["width"], prepared["height"]))
result = OrthophotoAcquisitionService.acquire(
db,
project_id,
payload,
settings=settings,
opener=lambda *_args, **_kwargs: response,
)
dataset = next(row for row in db.added if isinstance(row, Dataset))
assert result["product_key"] == "2020"
assert result["supports_detection"] is False
assert dataset.observed_at.year == 2020
assert dataset.temporal_granularity == "year"
assert dataset.source_metadata["layer"] == "OMWRGB20VL"
assert dataset.source_metadata["product_key"] == "2020"
assert dataset.provenance_metadata["spatial_hash"] == prepared["spatial_hash"]
def test_persisted_orthophoto_renders_browser_png(tmp_path) -> None:
project_id = uuid4()
dataset_id = uuid4()
path = tmp_path / "ortho.tif"
path.write_bytes(_source_tiff(32, 24))
dataset = Dataset(
id=dataset_id,
project_id=project_id,
name="ortho.tif",
dataset_type="raster",
source="Digitaal Vlaanderen",
source_name="digitaal_vlaanderen_orthophoto",
status="ready",
storage_path=str(path),
)
db = FakeSession({(Dataset, dataset_id): dataset})
png = OrthophotoAcquisitionService.render_png(db, project_id, dataset_id)
assert png.startswith(b"\x89PNG\r\n\x1a\n")
def test_orthophoto_endpoint_returns_canonical_job_envelope(monkeypatch) -> None:
project_id = uuid4()
output_dataset_id = uuid4()
@@ -307,6 +381,22 @@ def test_orthophoto_endpoint_returns_canonical_job_envelope(monkeypatch) -> None
assert any(isinstance(row, Job) for row in db.added)
def test_orthophoto_product_endpoint_returns_canonical_envelope() -> None:
project_id = uuid4()
db = FakeSession({(Project, project_id): Project(id=project_id, name="Mol")})
app.dependency_overrides[get_db] = lambda: db
try:
response = TestClient(app).get(f"/api/v1/projects/{project_id}/datasets/orthophoto/products")
finally:
app.dependency_overrides.clear()
assert response.status_code == 200
body = response.json()
assert set(body) == {"data"}
assert body["data"]["total"] == len(body["data"]["items"])
assert body["data"]["items"][0]["key"] == "most_recent"
def test_frontend_connects_map_selection_to_existing_detection_and_qa_flows() -> None:
app_source = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
hook_source = (ROOT / "frontend" / "src" / "hooks" / "useMapOrthophotoAnalysis.ts").read_text(encoding="utf-8")