Complete regional raster exploration
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 14:21:32 +02:00
parent 153cff06c0
commit 45dc730e76
24 changed files with 1162 additions and 123 deletions
+8 -6
View File
@@ -519,12 +519,14 @@ GeoJSON export stays disabled. The UI never labels the maximum-depth area
integral as current, permanent or concurrent water volume.
Regional VMM provisioning creates one scenario raster per municipality Area.
The explorer therefore shows only the flood scenarios whose `area_id` matches
the active work area. This avoids presenting a Mol scenario while the map is
focused on another municipality. The region-wide Area remains the navigation
context; municipality Areas are the analysis scope for flood rasters because
the public WCS and raster cell limits make one monolithic Kempen raster
operationally unsafe.
For a municipality the explorer still uses only that exact Area-linked file.
For the complete Kempen Area it presents the 28 VMM and DHMV partitions as one
logical map layer, deduplicates VMM into twelve scenario choices and renders
every matching MapLibre image partition. A drawn rectangle is sent to the
partition endpoint, which opens only intersecting files and calculates exact
combined cell statistics. A monolithic full-region 5 m calculation remains
disabled because it exceeds the governed raster-cell limit; users draw a
bounded rectangle without first choosing a municipality.
## Useful repository scripts
+31 -29
View File
@@ -13,7 +13,7 @@ interface GeoMapProps {
selectedFeature?: GeoJSON.Feature | null
selectionData?: GeoJSON.FeatureCollection | null
qaEvidenceData?: GeoJSON.FeatureCollection | null
imageOverlay?: MapImageOverlay | null
imageOverlays?: MapImageOverlay[]
selectionBbox?: { min_x: number; min_y: number; max_x: number; max_y: number } | null
bboxSelectionMode?: boolean
visible?: boolean
@@ -154,7 +154,7 @@ function GeoMap({
selectedFeature = null,
selectionData = null,
qaEvidenceData = null,
imageOverlay = null,
imageOverlays = [],
selectionBbox = null,
bboxSelectionMode = false,
visible = true,
@@ -180,6 +180,7 @@ function GeoMap({
const dataRef = useRef<GeoJSON.FeatureCollection | null>(data)
const fitDataOnChangeRef = useRef(fitDataOnChange)
const lastFittedAreaRef = useRef<GeoJSON.FeatureCollection | null>(null)
const imageOverlayIdsRef = useRef<string[]>([])
const [mapStyleReady, setMapStyleReady] = useState(false)
areaDataRef.current = areaData
@@ -353,37 +354,38 @@ function GeoMap({
if (!map || !mapStyleReady || !map.isStyleLoaded()) {
return
}
if (map.getLayer('bounded-orthophoto')) {
map.removeLayer('bounded-orthophoto')
for (const overlayId of [...imageOverlayIdsRef.current].reverse()) {
if (map.getLayer(overlayId)) {
map.removeLayer(overlayId)
}
if (map.getSource(overlayId)) {
map.removeSource(overlayId)
}
}
if (map.getSource('bounded-orthophoto')) {
map.removeSource('bounded-orthophoto')
}
if (!imageOverlay) {
return
}
const [minX, minY, maxX, maxY] = imageOverlay.bbox
map.addSource('bounded-orthophoto', {
type: 'image',
url: imageOverlay.url,
coordinates: [
[minX, maxY],
[maxX, maxY],
[maxX, minY],
[minX, minY],
],
})
imageOverlayIdsRef.current = []
const beforeLayer = ['area-fill', 'dataset-fill', 'selection-bbox-fill'].find((layerId) => map.getLayer(layerId))
map.addLayer(
{
id: 'bounded-orthophoto',
imageOverlays.forEach((imageOverlay, index) => {
const overlayId = `bounded-raster-${index}`
const [minX, minY, maxX, maxY] = imageOverlay.bbox
map.addSource(overlayId, {
type: 'image',
url: imageOverlay.url,
coordinates: [
[minX, maxY],
[maxX, maxY],
[maxX, minY],
[minX, minY],
],
})
map.addLayer({
id: overlayId,
type: 'raster',
source: 'bounded-orthophoto',
source: overlayId,
paint: { 'raster-opacity': imageOverlay.opacity ?? 0.88 },
},
beforeLayer,
)
}, [imageOverlay, mapStyleReady])
}, beforeLayer)
imageOverlayIdsRef.current.push(overlayId)
})
}, [imageOverlays, mapStyleReady])
useEffect(() => {
const map = mapRef.current
+200 -60
View File
@@ -158,14 +158,15 @@ const DATA_THEME_MAP_STYLES: Record<DataThemeId, { fill: string; line: string }>
parcels: { fill: '#a7792f', line: '#7d571f' },
}
function datasetAvailabilityLabel(dataset: DatasetCreateResponse): string {
function datasetAvailabilityLabel(dataset: DatasetCreateResponse, partitionCount = 1): string {
const regionalSuffix = partitionCount > 1 ? ` · ${partitionCount} gemeenten` : ''
if (dataset.dataset_type === 'raster' && dataset.source_name === 'digitaal_vlaanderen_dhmv') {
const resolution = Number(dataset.source_metadata?.['analysis_resolution_m'])
return `${Number.isFinite(resolution) ? `${resolution.toLocaleString('nl-BE')} m` : 'Raster'} hoogtegrid beschikbaar`
return `${Number.isFinite(resolution) ? `${resolution.toLocaleString('nl-BE')} m` : 'Raster'} hoogtegrid${regionalSuffix}`
}
if (dataset.dataset_type === 'raster' && dataset.source_name === 'vmm_flood_hazard') {
const resolution = Number(dataset.source_metadata?.['analysis_resolution_m'])
return `${Number.isFinite(resolution) ? `${resolution.toLocaleString('nl-BE')} m` : 'Raster'} overstromingsscenario`
return `${Number.isFinite(resolution) ? `${resolution.toLocaleString('nl-BE')} m` : 'Raster'} overstromingsscenario${regionalSuffix}`
}
if (dataset.dataset_type === 'raster' && dataset.source_name === 'department_omgeving_thematic_raster') {
const resolution = Number(dataset.source_metadata?.['analysis_resolution_m'])
@@ -213,21 +214,69 @@ function datasetMatchesTheme(dataset: DatasetCreateResponse, theme: DataTheme):
return theme.tokens.some((token) => searchText.includes(token))
}
function datasetCoversSelectedArea(dataset: DatasetCreateResponse, selectedAreaId: string | null): boolean {
function isMunicipalityAreaName(name: string | null | undefined): boolean {
return /^Gemeente\s/i.test(name ?? '')
}
function isPartitionedRaster(dataset: DatasetCreateResponse | null | undefined): boolean {
return Boolean(
dataset?.dataset_type === 'raster'
&& ['digitaal_vlaanderen_dhmv', 'vmm_flood_hazard'].includes(dataset.source_name ?? ''),
)
}
function datasetProductKey(dataset: DatasetCreateResponse): string {
return String(dataset.source_metadata?.['product_key'] ?? '')
}
function datasetCoversSelectedArea(
dataset: DatasetCreateResponse,
selectedAreaId: string | null,
regionalScope = false,
): boolean {
const coverageScope = String(dataset.source_metadata?.['coverage_scope'] ?? '')
if (coverageScope !== 'municipality' || !dataset.area_id) {
return true
}
if (regionalScope) {
return true
}
return Boolean(selectedAreaId) && dataset.area_id === selectedAreaId
}
function rasterPartitionsForDataset(
datasets: DatasetCreateResponse[],
representative: DatasetCreateResponse | null,
selectedAreaId: string | null,
regionalScope: boolean,
): DatasetCreateResponse[] {
if (!representative) {
return []
}
if (!regionalScope || !isPartitionedRaster(representative)) {
return [representative]
}
const productKey = datasetProductKey(representative)
return datasets
.filter(
(dataset) =>
dataset.source_name === representative.source_name
&& datasetProductKey(dataset) === productKey
&& datasetCoversSelectedArea(dataset, selectedAreaId, true),
)
.sort((left, right) => String(left.area_id ?? '').localeCompare(String(right.area_id ?? '')))
}
function pickThemeDataset(
datasets: DatasetCreateResponse[],
theme: DataTheme,
selectedAreaId: string | null,
regionalScope = false,
): DatasetCreateResponse | null {
const candidates = datasets.filter(
(dataset) => datasetMatchesTheme(dataset, theme) && datasetCoversSelectedArea(dataset, selectedAreaId),
(dataset) =>
datasetMatchesTheme(dataset, theme)
&& datasetCoversSelectedArea(dataset, selectedAreaId, regionalScope),
)
candidates.sort((left, right) => {
const score = (dataset: DatasetCreateResponse) =>
@@ -745,6 +794,7 @@ export function MapWorkspace({
const [fullWorkflowError, setFullWorkflowError] = useState<string | null>(null)
const [fullWorkflowMode, setFullWorkflowMode] = useState<'new' | 'reuse'>('new')
const selectedMapArea = areas.find((area) => area.id === selectedMapAreaId)
const regionalScopeSelected = Boolean(selectedMapArea && !isMunicipalityAreaName(selectedMapArea.name))
const featureProperties = selectedMapFeature?.properties ?? null
const featureSummaryEntries = featureProperties
? Object.entries(featureProperties)
@@ -767,70 +817,138 @@ export function MapWorkspace({
const selectedMapDataset = availableMapDatasets.find((dataset) => dataset.id === selectedMapDatasetId) ?? null
const usesDefaultOsmBasemap = !import.meta.env.VITE_MAP_STYLE_URL
const floodHazardDatasets = useMemo(
() => availableMapDatasets
.filter((dataset) => dataset.source_name === 'vmm_flood_hazard' && datasetCoversSelectedArea(dataset, selectedMapAreaId))
.sort((left, right) => floodScenarioLabel(left).localeCompare(floodScenarioLabel(right), 'nl')),
[availableMapDatasets, selectedMapAreaId],
() => {
const scoped = availableMapDatasets
.filter(
(dataset) =>
dataset.source_name === 'vmm_flood_hazard'
&& datasetCoversSelectedArea(dataset, selectedMapAreaId, regionalScopeSelected),
)
.sort((left, right) => floodScenarioLabel(left).localeCompare(floodScenarioLabel(right), 'nl'))
if (!regionalScopeSelected) {
return scoped
}
const products = new Map<string, DatasetCreateResponse>()
for (const dataset of scoped) {
const key = datasetProductKey(dataset)
if (key && !products.has(key)) {
products.set(key, dataset)
}
}
return Array.from(products.values())
},
[availableMapDatasets, regionalScopeSelected, selectedMapAreaId],
)
const themeDatasetMap = useMemo(() => {
const result = Object.fromEntries(
DATA_THEMES.map((theme) => [theme.id, pickThemeDataset(availableMapDatasets, theme, selectedMapAreaId)]),
DATA_THEMES.map((theme) => [
theme.id,
pickThemeDataset(availableMapDatasets, theme, selectedMapAreaId, regionalScopeSelected),
]),
) as Record<DataThemeId, DatasetCreateResponse | null>
const selectedFloodHazard = floodHazardDatasets.find((dataset) => dataset.id === selectedFloodHazardDatasetId)
if (selectedFloodHazard) {
result.flood_hazard = selectedFloodHazard
}
return result
}, [availableMapDatasets, floodHazardDatasets, selectedFloodHazardDatasetId, selectedMapAreaId])
}, [availableMapDatasets, floodHazardDatasets, regionalScopeSelected, selectedFloodHazardDatasetId, selectedMapAreaId])
const themePartitionMap = useMemo(
() =>
Object.fromEntries(
DATA_THEMES.map((theme) => [
theme.id,
rasterPartitionsForDataset(
availableMapDatasets,
themeDatasetMap[theme.id],
selectedMapAreaId,
regionalScopeSelected,
),
]),
) as Record<DataThemeId, DatasetCreateResponse[]>,
[availableMapDatasets, regionalScopeSelected, selectedMapAreaId, themeDatasetMap],
)
const activeTheme = DATA_THEMES.find((theme) => theme.id === activeThemeId) ?? DATA_THEMES[0]
const activeThemeMapStyle = DATA_THEME_MAP_STYLES[activeTheme.id]
const analysisOverlayActive = mapContentMode === 'analysis' && analysisLayerAvailable && Boolean(mapFeatureCollection)
const selectedOrthophotoProduct = orthophotoProducts.find((item) => item.key === selectedOrthophotoProductKey) ?? null
const orthophotoImageOverlay = orthophotoResult && orthophotoImageUrl && orthophotoResult.bbox_epsg4326.length === 4
? {
url: orthophotoImageUrl,
bbox: orthophotoResult.bbox_epsg4326 as [number, number, number, number],
label: orthophotoResult.display_name,
opacity: 0.9,
}
: null
const orthophotoImageOverlay = useMemo(
() => orthophotoResult && orthophotoImageUrl && orthophotoResult.bbox_epsg4326.length === 4
? {
url: orthophotoImageUrl,
bbox: orthophotoResult.bbox_epsg4326 as [number, number, number, number],
label: orthophotoResult.display_name,
opacity: 0.9,
}
: null,
[orthophotoImageUrl, orthophotoResult],
)
const activeThemeDataset = themeDatasetMap[activeTheme.id]
const terrainBounds = activeThemeDataset?.source_name === 'digitaal_vlaanderen_dhmv'
? activeThemeDataset.source_metadata?.['bbox_epsg4326']
: null
const terrainImageOverlay = activeTheme.id === 'elevation' && activeThemeDataset && selectedProjectId && Array.isArray(terrainBounds) && terrainBounds.length === 4
? {
url: terrainImageUrl(selectedProjectId, activeThemeDataset.id),
bbox: terrainBounds.map(Number) as [number, number, number, number],
label: getDatasetDisplayName(activeThemeDataset),
opacity: 0.82,
}
: null
const floodHazardBounds = activeThemeDataset?.source_name === 'vmm_flood_hazard'
? activeThemeDataset.source_metadata?.['bbox_epsg4326']
: null
const floodHazardImageOverlay = activeTheme.id === 'flood_hazard' && activeThemeDataset && selectedProjectId && Array.isArray(floodHazardBounds) && floodHazardBounds.length === 4
? {
url: floodHazardImageUrl(selectedProjectId, activeThemeDataset.id),
bbox: floodHazardBounds.map(Number) as [number, number, number, number],
label: floodScenarioLabel(activeThemeDataset),
opacity: 0.82,
}
: null
const activeThemePartitions = themePartitionMap[activeTheme.id]
const regionalRasterThemeActive = regionalScopeSelected && isPartitionedRaster(activeThemeDataset)
const terrainImageOverlays = useMemo(
() =>
activeTheme.id === 'elevation' && selectedProjectId
? activeThemePartitions.flatMap((dataset) => {
const bounds = dataset.source_metadata?.['bbox_epsg4326']
return dataset.source_name === 'digitaal_vlaanderen_dhmv'
&& Array.isArray(bounds)
&& bounds.length === 4
? [{
url: terrainImageUrl(selectedProjectId, dataset.id),
bbox: bounds.map(Number) as [number, number, number, number],
label: getDatasetDisplayName(dataset),
opacity: 0.82,
}]
: []
})
: [],
[activeTheme.id, activeThemePartitions, selectedProjectId],
)
const floodHazardImageOverlays = useMemo(
() =>
activeTheme.id === 'flood_hazard' && selectedProjectId
? activeThemePartitions.flatMap((dataset) => {
const bounds = dataset.source_metadata?.['bbox_epsg4326']
return dataset.source_name === 'vmm_flood_hazard'
&& Array.isArray(bounds)
&& bounds.length === 4
? [{
url: floodHazardImageUrl(selectedProjectId, dataset.id),
bbox: bounds.map(Number) as [number, number, number, number],
label: floodScenarioLabel(dataset),
opacity: 0.82,
}]
: []
})
: [],
[activeTheme.id, activeThemePartitions, selectedProjectId],
)
const thematicRasterBounds = activeThemeDataset?.source_name === 'department_omgeving_thematic_raster'
? activeThemeDataset.source_metadata?.['bbox_epsg4326']
: null
const thematicRasterImageOverlay = activeThemeDataset?.source_name === 'department_omgeving_thematic_raster' && selectedProjectId && Array.isArray(thematicRasterBounds) && thematicRasterBounds.length === 4
? {
url: thematicRasterImageUrl(selectedProjectId, activeThemeDataset.id),
bbox: thematicRasterBounds.map(Number) as [number, number, number, number],
label: getDatasetDisplayName(activeThemeDataset),
opacity: 0.78,
}
: null
const thematicRasterImageOverlays = useMemo(
() => activeThemeDataset?.source_name === 'department_omgeving_thematic_raster' && selectedProjectId && Array.isArray(thematicRasterBounds) && thematicRasterBounds.length === 4
? [{
url: thematicRasterImageUrl(selectedProjectId, activeThemeDataset.id),
bbox: thematicRasterBounds.map(Number) as [number, number, number, number],
label: getDatasetDisplayName(activeThemeDataset),
opacity: 0.78,
}]
: [],
[activeThemeDataset, selectedProjectId, thematicRasterBounds],
)
const thematicLegendMin = String(activeThemeDataset?.source_metadata?.['legend_min_label'] ?? 'Lagere waarde')
const thematicLegendMax = String(activeThemeDataset?.source_metadata?.['legend_max_label'] ?? 'Hogere waarde')
const activeImageOverlay = thematicRasterImageOverlay ?? floodHazardImageOverlay ?? terrainImageOverlay ?? orthophotoImageOverlay
const activeImageOverlays = useMemo(
() => thematicRasterImageOverlays.length > 0
? thematicRasterImageOverlays
: floodHazardImageOverlays.length > 0
? floodHazardImageOverlays
: terrainImageOverlays.length > 0
? terrainImageOverlays
: orthophotoImageOverlay ? [orthophotoImageOverlay] : [],
[floodHazardImageOverlays, orthophotoImageOverlay, terrainImageOverlays, thematicRasterImageOverlays],
)
const activeScopeProject = projects.find((project) => project.id === selectedProjectId) ?? null
const activeScopeLabel = activeScopeProject ? operationalScopeProjectLabel(activeScopeProject) : 'Werkgebied'
const municipalityAreaCount = areas.filter((area) => /^Gemeente\s/i.test(area.name)).length
@@ -865,7 +983,7 @@ export function MapWorkspace({
[themeInsights],
)
const activeSelectionResult = themeResults.find((item) => item.theme.id === activeThemeId)?.result
?? (selectedMapDataset?.id === activeThemeDataset?.id ? mapSelectionResult : null)
?? (!regionalRasterThemeActive && selectedMapDataset?.id === activeThemeDataset?.id ? mapSelectionResult : null)
const selectedAreaSquareMetres = useMemo(
() =>
bboxesEqual(mapSelectionBbox, selectedAreaBbox) && selectedMapArea?.area_m2
@@ -1106,14 +1224,23 @@ export function MapWorkspace({
const loadAllThemeResults = async (bbox: VectorSelectionBBox, areaId?: string) => {
const availableThemes = DATA_THEMES.flatMap((theme) => {
const dataset = themeDatasetMap[theme.id]
return dataset ? [{ themeId: theme.id, dataset }] : []
return dataset
? [{
themeId: theme.id,
dataset,
partitioned: regionalScopeSelected && isPartitionedRaster(dataset),
}]
: []
})
await loadThemeInsights(bbox, availableThemes, areaId)
}
const analyzeSelection = async (bbox: VectorSelectionBBox, areaId?: string) => {
setSelectionBbox(bbox)
const tasks: Array<Promise<unknown>> = [onRunMapSelectionExtract(bbox, areaId), loadAllThemeResults(bbox, areaId)]
const tasks: Array<Promise<unknown>> = [loadAllThemeResults(bbox, areaId)]
if (!regionalRasterThemeActive) {
tasks.push(onRunMapSelectionExtract(bbox, areaId))
}
if (analysisMode === 'evolution' && earlierDatasetId && laterDatasetId) {
tasks.push(compareTemporalSnapshots(earlierDatasetId, laterDatasetId, bbox, areaId))
}
@@ -1267,6 +1394,7 @@ export function MapWorkspace({
<div className="geo-theme-list">
{DATA_THEMES.map((theme) => {
const dataset = themeDatasetMap[theme.id]
const partitionCount = themePartitionMap[theme.id].length
const temporalGroups = themeTemporalSeriesMap[theme.id]
const temporalGroup = temporalGroups[0]
const evolutionAvailable = temporalGroups.some((group) => group.items.length >= 2)
@@ -1296,7 +1424,7 @@ export function MapWorkspace({
? 'Alleen huidige toestand'
: 'Bron nog niet ingeladen'
: dataset
? datasetAvailabilityLabel(dataset)
? datasetAvailabilityLabel(dataset, partitionCount)
: 'Bron nog niet ingeladen'}
</small>
</span>
@@ -1416,7 +1544,13 @@ export function MapWorkspace({
<span>2</span>
<div>
<h3>Selecteer een gebied</h3>
<p>{bboxSelectionMode ? 'Sleep nu een rechthoek op de kaart.' : 'Sleep een rechthoek of analyseer het volledige werkgebied.'}</p>
<p>
{bboxSelectionMode
? 'Sleep nu een rechthoek op de kaart.'
: regionalRasterThemeActive
? 'Teken een rechthoek; de juiste gemeentelijke rasters worden automatisch gecombineerd.'
: 'Sleep een rechthoek of analyseer het volledige werkgebied.'}
</p>
</div>
</div>
<div className="geo-map-actions">
@@ -1430,11 +1564,12 @@ export function MapWorkspace({
</button>
<button
className="secondary-action"
disabled={!activeThemeDataset || (analysisMode === 'evolution' && activeTemporalSeries.length < 2) || !selectedAreaBbox || mapSelectionLoading || themeResultsLoading}
disabled={!activeThemeDataset || regionalRasterThemeActive || (analysisMode === 'evolution' && activeTemporalSeries.length < 2) || !selectedAreaBbox || mapSelectionLoading || themeResultsLoading}
type="button"
title={regionalRasterThemeActive ? 'Teken een begrensde rechthoek voor een regionale rasteranalyse.' : undefined}
onClick={() => selectedAreaBbox && void analyzeSelection(selectedAreaBbox, selectedMapArea?.id)}
>
Volledig werkgebied
{regionalRasterThemeActive ? 'Selecteer een deelgebied' : 'Volledig werkgebied'}
</button>
<button className="secondary-action" disabled={!mapSelectionBbox} type="button" onClick={clearAreaSelection}>
Wis selectie
@@ -1450,7 +1585,7 @@ export function MapWorkspace({
areaData={areaFeatureCollection}
selectedFeature={selectedFeature}
selectionData={analysisMode === 'current' ? mapSelectionResult?.geojson ?? null : null}
imageOverlay={activeImageOverlay}
imageOverlays={activeImageOverlays}
selectionBbox={mapSelectionBbox}
bboxSelectionMode={bboxSelectionMode}
visible={mapLayerVisible}
@@ -1466,12 +1601,17 @@ export function MapWorkspace({
/>
<div className="geo-map-legend" aria-label="Kaartlegende">
<span><i className="geo-legend-area" /> Werkgebied</span>
{thematicRasterImageOverlay ? (
{thematicRasterImageOverlays.length > 0 ? (
<span className="geo-legend-thematic">
<i className={`geo-legend-ramp geo-legend-ramp-${activeTheme.id}`} />
<small>{thematicLegendMin} {thematicLegendMax}</small>
</span>
) : activeImageOverlay ? <span><i className="geo-legend-imagery" /> {activeImageOverlay.label}</span> : null}
) : activeImageOverlays.length > 0 ? (
<span>
<i className="geo-legend-imagery" /> {activeImageOverlays[0].label}
{activeImageOverlays.length > 1 ? ` · ${activeImageOverlays.length} gemeenten` : ''}
</span>
) : null}
{analysisOverlayActive ? (
<>
<span><i className="geo-legend-layer geo-legend-layer-buildings" /> AI-kandidaten</span>
@@ -9,6 +9,7 @@ import { thematicRasterSelectionToMapSelection } from '../lib/thematicRaster'
export interface MapThemeQuery<TThemeId extends string> {
themeId: TThemeId
dataset: DatasetCreateResponse
partitioned?: boolean
}
export interface MapThemeInsight<TThemeId extends string> extends MapThemeQuery<TThemeId> {
@@ -54,19 +55,35 @@ export function useMapThemeSelectionInsights<TThemeId extends string>(
setThemeInsightsError(null)
try {
const settled = await Promise.allSettled(
queries.map(async ({ themeId, dataset }) => ({
queries.map(async ({ themeId, dataset, partitioned }) => ({
themeId,
dataset,
result: dataset.dataset_type === 'raster' && dataset.source_name === 'digitaal_vlaanderen_dhmv'
? terrainSelectionToMapSelection(await datasetsApi.selectTerrain(selectedProjectId, dataset.id, {
bbox,
area_id: areaId,
}))
? terrainSelectionToMapSelection(
partitioned
? await datasetsApi.selectTerrainPartitions(selectedProjectId, {
bbox,
area_id: areaId,
product_key: String(dataset.source_metadata?.['product_key'] ?? 'dtm_1m'),
})
: await datasetsApi.selectTerrain(selectedProjectId, dataset.id, {
bbox,
area_id: areaId,
}),
)
: dataset.dataset_type === 'raster' && dataset.source_name === 'vmm_flood_hazard'
? floodHazardSelectionToMapSelection(await datasetsApi.selectFloodHazard(selectedProjectId, dataset.id, {
bbox,
area_id: areaId,
}))
? floodHazardSelectionToMapSelection(
partitioned
? await datasetsApi.selectFloodHazardPartitions(selectedProjectId, {
bbox,
area_id: areaId,
product_key: String(dataset.source_metadata?.['product_key'] ?? 'pluviaal_current_t100'),
})
: await datasetsApi.selectFloodHazard(selectedProjectId, dataset.id, {
bbox,
area_id: areaId,
}),
)
: dataset.dataset_type === 'raster' && dataset.source_name === 'department_omgeving_thematic_raster'
? thematicRasterSelectionToMapSelection(await datasetsApi.selectThematicRaster(selectedProjectId, dataset.id, {
bbox,
+10
View File
@@ -134,6 +134,11 @@ export const datasetsApi = {
payload: { bbox: VectorSelectionRequest['bbox']; area_id?: string },
): Promise<TerrainSelectionResponse> =>
apiPost<TerrainSelectionResponse>(`/api/v1/projects/${projectId}/datasets/${datasetId}/raster/terrain/select`, payload),
selectTerrainPartitions: (
projectId: string,
payload: { bbox: VectorSelectionRequest['bbox']; area_id?: string; product_key: string },
): Promise<TerrainSelectionResponse> =>
apiPost<TerrainSelectionResponse>(`/api/v1/projects/${projectId}/datasets/raster/terrain/select`, payload),
acquireFloodHazard: (projectId: string, payload: FloodHazardAcquireRequest): Promise<JobRead> =>
apiPost<JobRead>(`/api/v1/projects/${projectId}/datasets/flood-hazard/acquire`, payload),
listFloodHazardProducts: (projectId: string): Promise<{ items: FloodHazardProductRead[]; total: number }> =>
@@ -144,6 +149,11 @@ export const datasetsApi = {
payload: { bbox: VectorSelectionRequest['bbox']; area_id?: string },
): Promise<FloodHazardSelectionResponse> =>
apiPost<FloodHazardSelectionResponse>(`/api/v1/projects/${projectId}/datasets/${datasetId}/raster/flood-hazard/select`, payload),
selectFloodHazardPartitions: (
projectId: string,
payload: { bbox: VectorSelectionRequest['bbox']; area_id?: string; product_key: string },
): Promise<FloodHazardSelectionResponse> =>
apiPost<FloodHazardSelectionResponse>(`/api/v1/projects/${projectId}/datasets/raster/flood-hazard/select`, payload),
acquireThematicRaster: (projectId: string, payload: ThematicRasterAcquireRequest): Promise<JobRead> =>
apiPost<JobRead>(`/api/v1/projects/${projectId}/datasets/thematic-raster/acquire`, payload),
listThematicRasterProducts: (projectId: string): Promise<{ items: ThematicRasterProductRead[]; total: number }> =>
+4
View File
@@ -360,6 +360,8 @@ export interface DhmvProductRead {
export interface TerrainSelectionResponse {
dataset_id: string
dataset_ids: string[]
partition_count: number
product_key: string
surface_model: 'terrain' | 'surface'
selection_bbox: VectorSelectionBBox
@@ -410,6 +412,8 @@ export interface FloodHazardProductRead {
export interface FloodHazardSelectionResponse {
dataset_id: string
dataset_ids: string[]
partition_count: number
product_key: string
mechanism: 'pluviaal' | 'fluviaal'
climate_context: string