Complete regional raster exploration
This commit is contained in:
@@ -16,11 +16,13 @@ from app.core.errors import AppError
|
||||
from app.models import Area, Dataset
|
||||
from app.schemas.flood_hazard import (
|
||||
FloodHazardMetric,
|
||||
FloodHazardPartitionSelectionRequest,
|
||||
FloodHazardSelectionRequest,
|
||||
FloodHazardSelectionResponse,
|
||||
FloodHazardSelectionSummary,
|
||||
)
|
||||
from app.services.flood_hazard_acquisition_service import FloodHazardAcquisitionService
|
||||
from app.services.raster_partition_analysis_service import RasterPartitionAnalysisService
|
||||
|
||||
|
||||
class FloodHazardAnalysisService:
|
||||
@@ -170,6 +172,8 @@ class FloodHazardAnalysisService:
|
||||
primary = metrics[0]
|
||||
response = FloodHazardSelectionResponse(
|
||||
dataset_id=dataset.id,
|
||||
dataset_ids=[dataset.id],
|
||||
partition_count=1,
|
||||
product_key=product.key,
|
||||
mechanism=product.mechanism,
|
||||
climate_context=product.climate_context,
|
||||
@@ -195,6 +199,129 @@ class FloodHazardAnalysisService:
|
||||
)
|
||||
return response.model_dump(mode="json")
|
||||
|
||||
@staticmethod
|
||||
def analyze_partitions(
|
||||
db,
|
||||
project_id: UUID,
|
||||
payload: FloodHazardPartitionSelectionRequest,
|
||||
*,
|
||||
settings: Settings | None = None,
|
||||
) -> dict:
|
||||
resolved_settings = settings or get_settings()
|
||||
product = FloodHazardAcquisitionService._products().get(payload.product_key.strip().lower())
|
||||
if product is None:
|
||||
raise AppError(
|
||||
code="FLOOD_HAZARD_PRODUCT_NOT_SUPPORTED",
|
||||
message="Select a governed VMM fluvial or pluvial flood-depth scenario",
|
||||
details={"product_key": payload.product_key},
|
||||
status_code=422,
|
||||
)
|
||||
selection_4326 = FloodHazardAnalysisService._selection_geometry(db, project_id, payload)
|
||||
partition = RasterPartitionAnalysisService.select(
|
||||
db,
|
||||
project_id,
|
||||
source_name=FloodHazardAcquisitionService.PROVIDER,
|
||||
product_key=product.key,
|
||||
selection_geometry_4326=selection_4326,
|
||||
nodata=FloodHazardAcquisitionService.NODATA,
|
||||
max_pixels=resolved_settings.flood_hazard_max_pixels,
|
||||
)
|
||||
try:
|
||||
import numpy as np
|
||||
except ImportError as exc:
|
||||
raise AppError(
|
||||
code="RASTER_PROCESSING_UNAVAILABLE",
|
||||
message="Numpy is required for partitioned flood-hazard analysis",
|
||||
status_code=503,
|
||||
) from exc
|
||||
|
||||
raw = partition.values
|
||||
valid = (
|
||||
partition.selected_cells
|
||||
& np.isfinite(raw)
|
||||
& (raw != FloodHazardAcquisitionService.NODATA)
|
||||
& (raw > 0.0)
|
||||
)
|
||||
values = raw[valid]
|
||||
selected_cell_count = int(partition.selected_cells.sum())
|
||||
inundated_cell_count = int(values.size)
|
||||
cell_area_m2 = partition.resolution_x * partition.resolution_y
|
||||
|
||||
def metric(key: str, label: str, value: float, unit: str, method: str) -> FloodHazardMetric:
|
||||
return FloodHazardMetric(
|
||||
metric_key=key,
|
||||
metric_label=label,
|
||||
metric_value=round(float(value), 4),
|
||||
metric_unit=unit,
|
||||
aggregation_method=method,
|
||||
)
|
||||
|
||||
inundated_area_ha = inundated_cell_count * cell_area_m2 / 10_000.0
|
||||
metrics = [
|
||||
metric(
|
||||
"modelled_inundated_area_ha",
|
||||
"Gemodelleerd overstroomd oppervlak",
|
||||
inundated_area_ha,
|
||||
"ha",
|
||||
"positive_depth_cells_times_cell_area",
|
||||
),
|
||||
metric(
|
||||
"modelled_inundated_share_pct",
|
||||
"Aandeel selectie met gemodelleerde diepte",
|
||||
inundated_cell_count / max(1, selected_cell_count) * 100.0,
|
||||
"%",
|
||||
"positive_depth_cells_divided_by_selected_cells",
|
||||
),
|
||||
]
|
||||
if inundated_cell_count:
|
||||
metrics.extend(
|
||||
[
|
||||
metric("modelled_depth_mean_m", "Gemiddelde gemodelleerde maximumdiepte", values.mean(), "m", "mean_positive_depth_cells"),
|
||||
metric("modelled_depth_p90_m", "90e percentiel gemodelleerde maximumdiepte", np.percentile(values, 90), "m", "percentile_90_positive_depth_cells"),
|
||||
metric("modelled_depth_max_m", "Hoogste gemodelleerde maximumdiepte", values.max(), "m", "maximum_positive_depth_cells"),
|
||||
metric(
|
||||
"modelled_max_depth_area_integral_m3",
|
||||
"Diepte-oppervlakte-integraal (geen gelijktijdig volume)",
|
||||
values.sum() * cell_area_m2,
|
||||
"m3",
|
||||
"sum_local_max_depth_times_cell_area",
|
||||
),
|
||||
]
|
||||
)
|
||||
primary = metrics[0]
|
||||
first_dataset = partition.datasets[0]
|
||||
response = FloodHazardSelectionResponse(
|
||||
dataset_id=first_dataset.id,
|
||||
dataset_ids=[dataset.id for dataset in partition.datasets],
|
||||
partition_count=len(partition.datasets),
|
||||
product_key=product.key,
|
||||
mechanism=product.mechanism,
|
||||
climate_context=product.climate_context,
|
||||
probability_class=product.probability_class,
|
||||
return_period_years=product.return_period_years,
|
||||
selection_bbox=payload.bbox,
|
||||
selection_area_id=payload.area_id,
|
||||
selected_cell_count=selected_cell_count,
|
||||
inundated_cell_count=inundated_cell_count,
|
||||
inundated_fraction=round(inundated_cell_count / max(1, selected_cell_count), 6),
|
||||
resolution_m=round(max(partition.resolution_x, partition.resolution_y), 4),
|
||||
summary=FloodHazardSelectionSummary(
|
||||
metric_label=primary.metric_label,
|
||||
metric_value=primary.metric_value,
|
||||
metric_unit=primary.metric_unit,
|
||||
aggregation_method=primary.aggregation_method,
|
||||
primary_metric_key=primary.metric_key,
|
||||
metrics=metrics,
|
||||
),
|
||||
unsupported_metrics=FloodHazardAnalysisService.UNSUPPORTED_METRICS,
|
||||
limitation_message=(
|
||||
f"{FloodHazardAnalysisService.LIMITATION} De selectie werd exact berekend over "
|
||||
f"{len(partition.datasets)} persistente gemeentelijke rasterpartities."
|
||||
),
|
||||
generated_at=datetime.now(UTC).isoformat(),
|
||||
)
|
||||
return response.model_dump(mode="json")
|
||||
|
||||
@staticmethod
|
||||
def render_png(db, project_id: UUID, dataset_id: UUID, *, max_dimension: int = 1800) -> bytes:
|
||||
dataset = FloodHazardAnalysisService._load_dataset(db, project_id, dataset_id)
|
||||
|
||||
Reference in New Issue
Block a user