feat: add semantic GIS selection metrics
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
from app.models import Dataset
|
||||
from app.schemas.operations import VectorSelectionSummary
|
||||
from app.services.vector_feature_service import VectorFeatureService
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
BBOX = {"min_x": 5.0, "min_y": 51.1, "max_x": 5.2, "max_y": 51.3, "crs": "EPSG:4326"}
|
||||
|
||||
|
||||
class ScalarQuery:
|
||||
def __init__(self, value: float):
|
||||
self.value = value
|
||||
|
||||
def filter(self, *args): # noqa: ANN002, ARG002
|
||||
return self
|
||||
|
||||
def scalar(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class SequenceScalarSession:
|
||||
def __init__(self, values: list[float]):
|
||||
self.values = iter(values)
|
||||
|
||||
def query(self, *args): # noqa: ANN002, ARG002
|
||||
return ScalarQuery(next(self.values))
|
||||
|
||||
|
||||
def themed_dataset(theme: str, *, method: str = "feature_count") -> Dataset:
|
||||
return Dataset(
|
||||
id=uuid4(),
|
||||
project_id=uuid4(),
|
||||
name=f"regional-{theme}.geojson",
|
||||
dataset_type="vector",
|
||||
dataset_role="reference",
|
||||
source_name="grb",
|
||||
reference_layer_name=theme,
|
||||
source_metadata={
|
||||
"theme": theme,
|
||||
"selection_aggregation": {
|
||||
"method": method,
|
||||
"label": theme.title(),
|
||||
"unit": "objecten",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def test_building_selection_promotes_footprint_area_and_retains_object_count() -> None:
|
||||
result = VectorFeatureService.summarize_features_by_bbox(
|
||||
SequenceScalarSession([125_000.0]),
|
||||
dataset=themed_dataset("buildings"),
|
||||
bbox=BBOX,
|
||||
total_feature_count=40,
|
||||
)
|
||||
|
||||
assert result["primary_metric_key"] == "footprint_area"
|
||||
assert result["metric_label"] == "Bebouwde grondoppervlakte"
|
||||
assert result["metric_value"] == 12.5
|
||||
assert result["metric_unit"] == "ha"
|
||||
assert [(item["metric_key"], item["metric_value"]) for item in result["metrics"]] == [
|
||||
("footprint_area", 12.5),
|
||||
("feature_count", 40.0),
|
||||
]
|
||||
assert "niet de totale vloeroppervlakte" in result["warning"]
|
||||
VectorSelectionSummary(**result)
|
||||
|
||||
|
||||
def test_water_selection_reports_surface_length_and_honest_volume_limitation() -> None:
|
||||
result = VectorFeatureService.summarize_features_by_bbox(
|
||||
SequenceScalarSession([52_500.0, 12_750.0]),
|
||||
dataset=themed_dataset("water"),
|
||||
bbox=BBOX,
|
||||
total_feature_count=23,
|
||||
)
|
||||
|
||||
assert result["metric_value"] == 5.25
|
||||
assert result["metric_unit"] == "ha"
|
||||
assert [(item["metric_key"], item["metric_value"], item["metric_unit"]) for item in result["metrics"]] == [
|
||||
("water_area", 5.25, "ha"),
|
||||
("watercourse_length", 12.75, "km"),
|
||||
("feature_count", 23.0, "objecten"),
|
||||
]
|
||||
assert "Watervolume is niet berekenbaar" in result["warning"]
|
||||
|
||||
|
||||
def test_population_keeps_configured_metric_and_adds_sector_count() -> None:
|
||||
dataset = themed_dataset("population", method="sum")
|
||||
dataset.source_metadata["selection_aggregation"].update(
|
||||
{"metric_key": "population", "property": "population_total", "label": "Inwoners", "unit": "inwoners"}
|
||||
)
|
||||
result = VectorFeatureService.summarize_features_by_bbox(
|
||||
SequenceScalarSession([86_458.0]),
|
||||
dataset=dataset,
|
||||
bbox=BBOX,
|
||||
total_feature_count=733,
|
||||
)
|
||||
|
||||
assert result["primary_metric_key"] == "population"
|
||||
assert result["metric_value"] == 86_458.0
|
||||
assert result["metrics"][1] == {
|
||||
"metric_key": "feature_count",
|
||||
"metric_label": "Statistische sectoren",
|
||||
"metric_value": 733.0,
|
||||
"metric_unit": "objecten",
|
||||
"aggregation_method": "feature_count",
|
||||
"is_estimate": False,
|
||||
"warning": None,
|
||||
}
|
||||
|
||||
|
||||
def test_future_regional_imports_persist_semantic_aggregation_configuration() -> None:
|
||||
buildings = (ROOT / "scripts/provision_regional_grb_buildings.py").read_text(encoding="utf-8")
|
||||
context = (ROOT / "scripts/provision_regional_grb_context.py").read_text(encoding="utf-8")
|
||||
frontend = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8")
|
||||
|
||||
assert '"method": "intersection_area"' in buildings
|
||||
assert '"label": "Bebouwde grondoppervlakte"' in buildings
|
||||
assert 'metric_method="intersection_length"' in context
|
||||
assert 'metric_label="Wateroppervlakte"' in context
|
||||
assert 'metric_label="Perceeloppervlakte"' in context
|
||||
assert 'aria-label="Aanvullende gebiedsmetingen"' in frontend
|
||||
assert "activeSelectionResult.summary.warning" in frontend
|
||||
Reference in New Issue
Block a user