from __future__ import annotations import importlib.util from pathlib import Path from types import SimpleNamespace import sys import numpy as np from pyproj import Transformer import rasterio from rasterio.transform import from_origin from shapely.geometry import Polygon, shape from shapely.ops import transform from tests.frontend_contract import read_map_workspace ROOT = Path(__file__).resolve().parents[2] def load_provisioner(): script_path = ROOT / "scripts" / "provision_official_landuse_timeseries.py" spec = importlib.util.spec_from_file_location("official_landuse_provisioner", 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 test_official_landuse_wcs_contract_is_categorical_and_deterministic() -> None: module = load_provisioner() params = module.build_wcs_params(2025, (196594.1, 205064.2, 210910.7, 223974.9)) assert module.SUPPORTED_YEARS == (2013, 2016, 2019, 2022, 2025) assert module.LAND_USE_CLASSES[12] == "Bos" assert params == { "SERVICE": "WCS", "VERSION": "1.0.0", "REQUEST": "GetCoverage", "COVERAGE": "lu:lu_landgebruik_vlaa_2025_v3", "CRS": "EPSG:31370", "BBOX": "196590.000,205060.000,210920.000,223980.000", "RESX": "10", "RESY": "10", "FORMAT": "image/tiff", "RESPONSE_CRS": "EPSG:31370", } assert module.series_key(module.THEMES[0], "Mol") == "department-omgeving:land-use:forest:mol" def test_official_landuse_polygonization_clips_and_preserves_provenance(tmp_path: Path) -> None: module = load_provisioner() raster_path = tmp_path / "landuse.tif" values = np.array( [ [1, 1, 1, 1, 1, 1], [1, 12, 12, 1, 1, 1], [1, 12, 12, 1, 12, 1], [1, 1, 1, 1, 12, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], ], dtype="int32", ) with rasterio.open( raster_path, "w", driver="GTiff", width=6, height=6, count=1, dtype="int32", crs="EPSG:31370", transform=from_origin(200000, 210000, 10, 10), nodata=-9999, ) as destination: destination.write(values, 1) to_wgs84 = Transformer.from_crs(31370, 4326, always_xy=True) boundary_metric = Polygon( [(200005, 209945), (200055, 209945), (200055, 209995), (200005, 209995), (200005, 209945)] ) boundary = transform(to_wgs84.transform, boundary_metric) payload, stats = module.polygonize_snapshot( raster_path=raster_path, boundary=boundary, year=2025, theme=module.THEMES[0], municipality_name="Mol", nis_code="13025", scope_key="mol", max_features=100, ) assert payload["type"] == "FeatureCollection" assert payload["crs"]["properties"]["name"] == "EPSG:4326" assert payload["source_coverage_id"] == "lu:lu_landgebruik_vlaa_2025_v3" assert stats["source_pixel_count"] == 6 assert stats["source_pixel_area_m2"] == 600 assert stats["feature_count"] == 2 assert 0 < stats["polygon_area_m2"] <= 600 assert stats["class_histogram"] == {"1": 30, "12": 6} for feature in payload["features"]: geometry = shape(feature["geometry"]) properties = feature["properties"] assert geometry.is_valid assert geometry.within(boundary.buffer(1e-9)) assert properties["source_name"] == "department_omgeving_land_use" assert properties["land_use_class_ids"] == [12] assert properties["source_resolution_m"] == 10.0 assert properties["source_raster_sha256"] == stats["raster_sha256"] def test_official_landuse_metadata_keeps_modern_series_separate(tmp_path: Path) -> None: module = load_provisioner() theme = module.THEMES[0] snapshot = module.PreparedSnapshot( year=2022, theme=theme, raster_path=tmp_path / "source.tif", vector_path=tmp_path / "forest.geojson", manifest_path=tmp_path / "forest.manifest.json", feature_count=42, raster_sha256="a" * 64, vector_sha256="b" * 64, ) args = SimpleNamespace(scope_key="mol", municipality_name="Mol", nis_code="13025") source = module.build_source_metadata(args, snapshot) provenance = module.build_provenance_metadata(args, snapshot) assert source["temporal_series_label"] == "Moderne landgebruikskaart (10 m)" assert source["selection_aggregation"]["method"] == "intersection_area" assert source["identity_stable"] is False assert source["land_use_class_names"] == ["Bos"] assert "10 m" in source["selection_aggregation"]["warning"] assert provenance["operator_explicit_fetch"] is True assert provenance["coverage_id"] == "lu:lu_landgebruik_vlaa_2022_v3" assert "historical-landuse" not in module.series_key(theme, "mol") def test_official_landuse_operator_paginates_within_api_limit() -> None: module = load_provisioner() class Response: ok = True status_code = 200 text = "" def __init__(self, payload): self.payload = payload def json(self): return {"data": self.payload} class Session: def __init__(self) -> None: self.calls = [] def get(self, url, *, params, timeout): self.calls.append((url, params, timeout)) offset = params["offset"] page_items = [{"id": index} for index in range(offset, min(offset + 200, 405))] return Response({"items": page_items, "total": 405, "limit": 200, "offset": offset}) session = Session() items = module.list_paginated_items(session, "http://backend/api/v1/projects", timeout=30) assert len(items) == 405 assert [call[1] for call in session.calls] == [ {"limit": 200, "offset": 0}, {"limit": 200, "offset": 200}, {"limit": 200, "offset": 400}, ] def test_official_landuse_operator_is_packaged_and_readiness_checked() -> None: 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") workspace = read_map_workspace() geo_map = (ROOT / "frontend/src/components/GeoMap.tsx").read_text(encoding="utf-8") premium_css = (ROOT / "frontend/src/styles/premium.css").read_text(encoding="utf-8") assert "py_compile scripts/provision_official_landuse_timeseries.py" in readiness assert "COPY scripts/provision_official_landuse_timeseries.py" in dockerfile assert "department_omgeving_land_use' ? 90_000" in workspace assert "activeTemporalSeriesGroups.length > 1" in workspace assert "dataFillColor={activeThemeMapStyle.fill}" in workspace assert "forest: { fill: '#347950', line: '#225f3b' }" in workspace assert "datasetFillColor(dataFillColor)" in geo_map assert ".workbench-main > .geo-explorer" in premium_css assert "Moderne landgebruikskaart (10 m)" in (ROOT / "scripts/provision_official_landuse_timeseries.py").read_text( encoding="utf-8" )