feat: add cross-domain Mol data profile
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-16 03:06:36 +02:00
parent 4aa0d6da24
commit 035ec3b233
39 changed files with 3049 additions and 22 deletions
@@ -22,8 +22,11 @@ from app.schemas.assistant import (
AssistantTemporalSeries,
)
from app.schemas.flood_hazard import FloodHazardSelectionRequest
from app.schemas.thematic_raster import ThematicRasterSelectionRequest
from app.services.flood_hazard_acquisition_service import FloodHazardAcquisitionService
from app.services.flood_hazard_analysis_service import FloodHazardAnalysisService
from app.services.thematic_raster_acquisition_service import ThematicRasterAcquisitionService
from app.services.thematic_raster_analysis_service import ThematicRasterAnalysisService
from app.services.vector_feature_service import VectorFeatureService
@@ -42,9 +45,17 @@ class GeoAssistantService:
)
ESTIMATE_TOPIC_TERMS = {
"population": ("bevolk", "inwoner"),
"space_occupation": ("ruimtebeslag",),
"open_space": ("open ruimte",),
"accessibility": ("bereikbaar", "knooppunt"),
"services": ("voorziening",),
}
ESTIMATE_TOPIC_LABELS = {
"population": "bevolkingswaarden",
"space_occupation": "ruimtebeslagoppervlakten",
"open_space": "openruimte-oppervlakten",
"accessibility": "bereikbaarheidsscores",
"services": "voorzieningenscores",
}
@classmethod
@@ -264,6 +275,19 @@ class GeoAssistantService:
if dataset.dataset_type == "raster" and dataset.source_name == FloodHazardAcquisitionService.PROVIDER
and (area is None or dataset.area_id is None or dataset.area_id == area.id)
]
thematic_candidates = [
dataset
for dataset in datasets
if dataset.dataset_type == "raster" and dataset.source_name == ThematicRasterAcquisitionService.PROVIDER
and (area is None or dataset.area_id is None or dataset.area_id == area.id)
]
thematic_by_product: dict[str, Dataset] = {}
for dataset in thematic_candidates:
product_key = str((dataset.source_metadata or {}).get("product_key") or "")
current = thematic_by_product.get(product_key)
if product_key and (current is None or (dataset.imported_at or datetime.min.replace(tzinfo=timezone.utc)) > (current.imported_at or datetime.min.replace(tzinfo=timezone.utc))):
thematic_by_product[product_key] = dataset
thematic_datasets = list(thematic_by_product.values())
warnings: list[str] = []
context_metrics: list[AssistantContextMetric] = []
source_dataset_ids: list[UUID] = []
@@ -323,6 +347,47 @@ class GeoAssistantService:
}
)
for dataset in sorted(thematic_datasets, key=lambda item: str((item.source_metadata or {}).get("product_key") or item.name)):
try:
result = ThematicRasterAnalysisService.analyze(
db,
project_id,
dataset.id,
ThematicRasterSelectionRequest(bbox=bbox, area_id=area.id if area is not None else None),
settings=self.settings,
)
except AppError as exc:
warnings.append(f"{dataset.name}: {exc.message}")
continue
serialized_metrics: list[dict[str, Any]] = []
for metric in result["summary"]["metrics"]:
item = AssistantContextMetric(
theme=result["theme"],
label=str(metric["metric_label"]),
value=float(metric["metric_value"]),
unit=str(metric["metric_unit"]),
source=ThematicRasterAcquisitionService.ATTRIBUTION,
dataset_id=dataset.id,
observed_at=dataset.observed_at,
is_estimate=bool(metric.get("is_estimate", True)),
)
context_metrics.append(item)
serialized_metrics.append(item.model_dump(mode="json"))
serialized_metrics[-1]["measurement_quality"] = "resolutiegebonden_bronmeting"
source_dataset_ids.append(dataset.id)
current_context.append(
{
"dataset_name": dataset.name,
"dataset_id": str(dataset.id),
"theme": result["theme"],
"source": ThematicRasterAcquisitionService.ATTRIBUTION,
"observed_at": dataset.observed_at.isoformat() if dataset.observed_at else None,
"metrics": serialized_metrics,
"unsupported_metrics": result["unsupported_metrics"],
"warning": result["limitation_message"],
}
)
for dataset in sorted(
flood_hazard_datasets,
key=lambda item: str((item.source_metadata or {}).get("product_key") or item.name),
@@ -425,6 +490,7 @@ class GeoAssistantService:
"water_volume_available": False,
"water_volume_reason": "Geen bathymetrie gekoppeld voor de permanente inhoud van waterlichamen.",
"flood_hazard_scenarios_available": bool(flood_hazard_datasets),
"thematic_policy_rasters_available": bool(thematic_datasets),
"flood_depth_area_integral_is_concurrent_volume": False,
"object_counts_are_supporting_metrics": True,
"causal_explanations_available": False,