feat: map the complete municipality of Mol
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from shapely.geometry import Polygon, shape
|
||||
|
||||
from app.models import VectorFeature
|
||||
from app.services.vector_feature_service import VectorFeatureService
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def load_provisioner():
|
||||
script_path = ROOT / "scripts" / "provision_mol_municipality_workspace.py"
|
||||
spec = importlib.util.spec_from_file_location("mol_municipality_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 feature(feature_id: str, coordinates: list[list[list[float]]]):
|
||||
return {
|
||||
"type": "Feature",
|
||||
"id": feature_id,
|
||||
"geometry": {"type": "Polygon", "coordinates": coordinates},
|
||||
"properties": {"UIDN": feature_id},
|
||||
}
|
||||
|
||||
|
||||
def test_mol_provisioner_uses_official_identity_and_exact_boundary_clipping() -> None:
|
||||
module = load_provisioner()
|
||||
boundary = Polygon([(5.0, 51.0), (5.2, 51.0), (5.2, 51.2), (5.0, 51.2), (5.0, 51.0)])
|
||||
inside = feature("GBG.inside", [[[5.05, 51.05], [5.1, 51.05], [5.1, 51.1], [5.05, 51.1], [5.05, 51.05]]])
|
||||
crossing = feature("GBG.crossing", [[[5.18, 51.08], [5.22, 51.08], [5.22, 51.12], [5.18, 51.12], [5.18, 51.08]]])
|
||||
outside = feature("GBG.outside", [[[5.3, 51.3], [5.31, 51.3], [5.31, 51.31], [5.3, 51.31], [5.3, 51.3]]])
|
||||
pages = [
|
||||
(
|
||||
{"type": "FeatureCollection", "features": [inside, crossing, outside, inside]},
|
||||
"https://geo.api.vlaanderen.be/GRB/page-1",
|
||||
)
|
||||
]
|
||||
|
||||
buildings, summary = module.build_municipality_buildings(pages, boundary, max_features=10)
|
||||
|
||||
assert module.MUNICIPALITY_NIS_CODE == "13025"
|
||||
assert module.PROJECT_NAME == "Mol Municipality Workbench"
|
||||
assert len(buildings) == 2
|
||||
assert summary["bbox_feature_count"] == 3
|
||||
assert summary["outside_boundary_count"] == 1
|
||||
assert summary["clipped_at_boundary_count"] == 1
|
||||
assert summary["reference_truncated"] is False
|
||||
assert all(shape(item["geometry"]).within(boundary) for item in buildings)
|
||||
assert buildings[0]["properties"]["coverage_scope"] == "municipality"
|
||||
assert buildings[0]["properties"]["source_name"] == "grb"
|
||||
assert buildings[0]["properties"]["reference_layer_name"] == "buildings"
|
||||
assert buildings[1]["properties"]["clipped_to_municipality"] is True
|
||||
|
||||
|
||||
def test_mol_provisioner_refuses_a_truncated_municipality_dataset() -> None:
|
||||
module = load_provisioner()
|
||||
boundary = Polygon([(5.0, 51.0), (5.2, 51.0), (5.2, 51.2), (5.0, 51.2), (5.0, 51.0)])
|
||||
pages = [
|
||||
(
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
feature("GBG.1", [[[5.01, 51.01], [5.02, 51.01], [5.02, 51.02], [5.01, 51.02], [5.01, 51.01]]]),
|
||||
feature("GBG.2", [[[5.03, 51.03], [5.04, 51.03], [5.04, 51.04], [5.03, 51.04], [5.03, 51.03]]]),
|
||||
],
|
||||
},
|
||||
"https://geo.api.vlaanderen.be/GRB/page-1",
|
||||
)
|
||||
]
|
||||
|
||||
with pytest.raises(RuntimeError, match="refusing a truncated municipality dataset"):
|
||||
module.build_municipality_buildings(pages, boundary, max_features=1)
|
||||
|
||||
|
||||
def test_large_vector_persistence_flushes_once_without_per_feature_refresh() -> None:
|
||||
class FakeSession:
|
||||
def __init__(self) -> None:
|
||||
self.added = []
|
||||
self.flushes = 0
|
||||
self.commits = 0
|
||||
self.refreshes = 0
|
||||
|
||||
def add(self, item) -> None:
|
||||
self.added.append(item)
|
||||
|
||||
def flush(self) -> None:
|
||||
self.flushes += 1
|
||||
|
||||
def commit(self) -> None:
|
||||
self.commits += 1
|
||||
|
||||
def refresh(self, _item) -> None:
|
||||
self.refreshes += 1
|
||||
|
||||
payload = {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"id": f"GBG.{index}",
|
||||
"properties": {"layer_type": "building"},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[[5.0, 51.0], [5.001, 51.0], [5.001, 51.001], [5.0, 51.001], [5.0, 51.0]]],
|
||||
},
|
||||
}
|
||||
for index in range(250)
|
||||
],
|
||||
}
|
||||
db = FakeSession()
|
||||
|
||||
persisted = VectorFeatureService.persist_geojson_features(db, uuid4(), payload, feature_class="buildings")
|
||||
|
||||
assert len(persisted) == 250
|
||||
assert all(isinstance(item, VectorFeature) for item in persisted)
|
||||
assert db.flushes == 1
|
||||
assert db.commits == 1
|
||||
assert db.refreshes == 0
|
||||
|
||||
|
||||
def test_municipality_workspace_is_wired_into_runtime_and_frontend_priority() -> 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")
|
||||
focus = (ROOT / "frontend" / "src" / "config" / "primaryFocus.ts").read_text(encoding="utf-8")
|
||||
project_hook = (ROOT / "frontend" / "src" / "hooks" / "useProjectWorkspace.ts").read_text(encoding="utf-8")
|
||||
dataset_hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8")
|
||||
map_source = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8")
|
||||
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8")
|
||||
|
||||
assert "py_compile scripts/provision_mol_municipality_workspace.py" in readiness
|
||||
assert "COPY scripts/provision_mol_municipality_workspace.py" in dockerfile
|
||||
assert "PRIMARY_FOCUS_MUNICIPALITY_PROJECT_NAME = 'Mol Municipality Workbench'" in focus
|
||||
assert "items.find(isPrimaryFocusMunicipalityProject)" in project_hook
|
||||
assert "datasets.find(isPrimaryFocusMunicipalityBoundaryDataset)" in dataset_hook
|
||||
assert "featureCollectionBounds(featureCollection)" in map_source
|
||||
assert "useMemo(() => getFeatureCollectionBBox(mapFeatureCollection)" in map_workspace
|
||||
assert "Math.min(...xs)" not in map_source
|
||||
|
||||
@@ -79,7 +79,9 @@ def test_vector_feature_service_persists_geojson_features_with_properties() -> N
|
||||
assert persisted[0].source_feature_id == "building-1"
|
||||
assert persisted[0].properties_json == {"class": "building", "height": 7}
|
||||
assert db.added == persisted
|
||||
assert db.flushes == 1
|
||||
assert db.commits == 1
|
||||
assert db.refreshes == []
|
||||
|
||||
|
||||
def test_dataset_upload_persists_vector_features(monkeypatch, tmp_path) -> None:
|
||||
|
||||
Reference in New Issue
Block a user