fix(map): make area analysis scale-aware
This commit is contained in:
@@ -26,6 +26,7 @@ export interface MapThemeQuery<TThemeId extends string> {
|
||||
dataset?: DatasetCreateResponse
|
||||
partitioned?: boolean
|
||||
acquisition?: MapThemeAcquisition
|
||||
acquisitionBboxes?: VectorSelectionBBox[]
|
||||
}
|
||||
|
||||
export interface MapThemeInsight<TThemeId extends string> {
|
||||
@@ -103,51 +104,63 @@ export function useMapThemeSelectionInsights<TThemeId extends string>(
|
||||
const settled = await settleWithConcurrency(
|
||||
queries,
|
||||
3,
|
||||
async ({ themeId, dataset: existingDataset, partitioned, acquisition }) => {
|
||||
async ({ themeId, dataset: existingDataset, partitioned, acquisition, acquisitionBboxes }) => {
|
||||
let dataset = existingDataset
|
||||
let acquiredDatasets: DatasetCreateResponse[] = []
|
||||
if (acquisition) {
|
||||
const commonPayload = {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
force_refresh: false,
|
||||
}
|
||||
const acquisitionJob = acquisition.kind === 'thematic_raster'
|
||||
? await datasetsApi.acquireThematicRaster(selectedProjectId, {
|
||||
...commonPayload,
|
||||
product_key: acquisition.productKey,
|
||||
})
|
||||
: acquisition.kind === 'dhmv'
|
||||
? await datasetsApi.acquireDhmv(selectedProjectId, {
|
||||
const requestedBboxes = acquisitionBboxes?.length ? acquisitionBboxes : [bbox]
|
||||
const acquisitionResults = await settleWithConcurrency(requestedBboxes, 1, async (acquisitionBbox) => {
|
||||
const commonPayload = {
|
||||
bbox: acquisitionBbox,
|
||||
area_id: areaId,
|
||||
force_refresh: false,
|
||||
}
|
||||
const acquisitionJob = acquisition.kind === 'thematic_raster'
|
||||
? await datasetsApi.acquireThematicRaster(selectedProjectId, {
|
||||
...commonPayload,
|
||||
product_key: acquisition.productKey as 'dtm_1m' | 'dsm_1m',
|
||||
product_key: acquisition.productKey,
|
||||
})
|
||||
: acquisition.kind === 'flood_hazard'
|
||||
? await datasetsApi.acquireFloodHazard(selectedProjectId, {
|
||||
: acquisition.kind === 'dhmv'
|
||||
? await datasetsApi.acquireDhmv(selectedProjectId, {
|
||||
...commonPayload,
|
||||
product_key: acquisition.productKey,
|
||||
product_key: acquisition.productKey as 'dtm_1m' | 'dsm_1m',
|
||||
})
|
||||
: acquisition.kind === 'grb'
|
||||
? await datasetsApi.acquireGrb(selectedProjectId, {
|
||||
: acquisition.kind === 'flood_hazard'
|
||||
? await datasetsApi.acquireFloodHazard(selectedProjectId, {
|
||||
...commonPayload,
|
||||
product_key: acquisition.productKey as 'buildings' | 'roads' | 'water' | 'parcels',
|
||||
product_key: acquisition.productKey,
|
||||
})
|
||||
: acquisition.kind === 'bathymetry_profiles'
|
||||
? await datasetsApi.acquireBathymetryProfiles(selectedProjectId, commonPayload)
|
||||
: await datasetsApi.acquireOfficialVector(selectedProjectId, {
|
||||
: acquisition.kind === 'grb'
|
||||
? await datasetsApi.acquireGrb(selectedProjectId, {
|
||||
...commonPayload,
|
||||
product_key: acquisition.productKey,
|
||||
product_key: acquisition.productKey as 'buildings' | 'roads' | 'water' | 'parcels',
|
||||
})
|
||||
if (acquisitionJob.status !== 'success' || !acquisitionJob.output_dataset_id) {
|
||||
throw new Error(
|
||||
acquisitionJob.error_message
|
||||
|| `De officiële kaartbron ${acquisition.displayName} kon niet worden ingeladen.`,
|
||||
)
|
||||
: acquisition.kind === 'bathymetry_profiles'
|
||||
? await datasetsApi.acquireBathymetryProfiles(selectedProjectId, commonPayload)
|
||||
: await datasetsApi.acquireOfficialVector(selectedProjectId, {
|
||||
...commonPayload,
|
||||
product_key: acquisition.productKey,
|
||||
})
|
||||
if (acquisitionJob.status !== 'success' || !acquisitionJob.output_dataset_id) {
|
||||
throw new Error(
|
||||
acquisitionJob.error_message
|
||||
|| `De officiële kaartbron ${acquisition.displayName} kon niet worden ingeladen.`,
|
||||
)
|
||||
}
|
||||
return datasetsApi.get(selectedProjectId, acquisitionJob.output_dataset_id)
|
||||
})
|
||||
const failedAcquisition = acquisitionResults.find((item) => item.status === 'rejected')
|
||||
if (failedAcquisition?.status === 'rejected') {
|
||||
throw failedAcquisition.reason
|
||||
}
|
||||
dataset = await datasetsApi.get(selectedProjectId, acquisitionJob.output_dataset_id)
|
||||
acquiredDatasets = acquisitionResults.flatMap((item) => item.status === 'fulfilled' ? [item.value] : [])
|
||||
dataset = acquiredDatasets[0]
|
||||
}
|
||||
if (!dataset) {
|
||||
throw new Error(`Geen persistente databron beschikbaar voor thema ${themeId}.`)
|
||||
}
|
||||
const acquiredDatasetIds = acquiredDatasets.map((item) => item.id)
|
||||
const acquiredAsPartitions = acquiredDatasetIds.length > 1
|
||||
return {
|
||||
themeId,
|
||||
dataset,
|
||||
@@ -155,11 +168,12 @@ export function useMapThemeSelectionInsights<TThemeId extends string>(
|
||||
acquisition,
|
||||
result: dataset.dataset_type === 'raster' && dataset.source_name === 'digitaal_vlaanderen_dhmv'
|
||||
? terrainSelectionToMapSelection(
|
||||
partitioned
|
||||
partitioned || acquiredAsPartitions
|
||||
? await datasetsApi.selectTerrainPartitions(selectedProjectId, {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
product_key: String(dataset.source_metadata?.['product_key'] ?? 'dtm_1m'),
|
||||
...(acquiredAsPartitions ? { dataset_ids: acquiredDatasetIds } : {}),
|
||||
})
|
||||
: await datasetsApi.selectTerrain(selectedProjectId, dataset.id, {
|
||||
bbox,
|
||||
@@ -168,11 +182,12 @@ export function useMapThemeSelectionInsights<TThemeId extends string>(
|
||||
)
|
||||
: dataset.dataset_type === 'raster' && dataset.source_name === 'vmm_flood_hazard'
|
||||
? floodHazardSelectionToMapSelection(
|
||||
partitioned
|
||||
partitioned || acquiredAsPartitions
|
||||
? await datasetsApi.selectFloodHazardPartitions(selectedProjectId, {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
product_key: String(dataset.source_metadata?.['product_key'] ?? 'pluviaal_current_t100'),
|
||||
...(acquiredAsPartitions ? { dataset_ids: acquiredDatasetIds } : {}),
|
||||
})
|
||||
: await datasetsApi.selectFloodHazard(selectedProjectId, dataset.id, {
|
||||
bbox,
|
||||
@@ -189,6 +204,13 @@ export function useMapThemeSelectionInsights<TThemeId extends string>(
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
}))
|
||||
: acquiredAsPartitions && dataset.dataset_type !== 'raster'
|
||||
? await datasetsApi.selectVectorFeaturePartitions(selectedProjectId, {
|
||||
dataset_ids: acquiredDatasetIds,
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
limit: 1000,
|
||||
})
|
||||
: dataset.source_name === 'vmm_vha_bathymetry_profiles' && partitioned
|
||||
? await datasetsApi.selectBathymetryProfilePartitions(selectedProjectId, {
|
||||
bbox,
|
||||
|
||||
@@ -3,11 +3,13 @@ import { datasetsApi, externalApi } from '../services/api'
|
||||
import { formatError } from '../lib/formatError'
|
||||
import type {
|
||||
BathymetrySourceRead,
|
||||
CoverageResolveResponse,
|
||||
DhmvProductRead,
|
||||
FloodHazardProductRead,
|
||||
GrbProductRead,
|
||||
OfficialVectorProductRead,
|
||||
ThematicRasterProductRead,
|
||||
VectorSelectionBBox,
|
||||
} from '../types'
|
||||
|
||||
export interface OfficialMapProducts {
|
||||
@@ -104,5 +106,47 @@ export function useOfficialMapProducts(selectedProjectId: string | null) {
|
||||
[selectedProjectId],
|
||||
)
|
||||
|
||||
return { products, loading, error, resolveCoverage }
|
||||
const resolveCoveragePartitions = useCallback(
|
||||
async (bboxes: VectorSelectionBBox[]): Promise<Array<{
|
||||
bbox: VectorSelectionBBox
|
||||
coverage: CoverageResolveResponse
|
||||
}> | null> => {
|
||||
if (!selectedProjectId) {
|
||||
setError('Selecteer eerst een werkruimte.')
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const resolved: Array<{
|
||||
bbox: VectorSelectionBBox
|
||||
coverage: CoverageResolveResponse
|
||||
}> = []
|
||||
for (let offset = 0; offset < bboxes.length; offset += 4) {
|
||||
const batch = bboxes.slice(offset, offset + 4)
|
||||
resolved.push(...await Promise.all(batch.map(async (bbox) => ({
|
||||
bbox,
|
||||
coverage: await externalApi.resolveCoverage({
|
||||
projectId: selectedProjectId,
|
||||
bbox: {
|
||||
minx: bbox.min_x,
|
||||
miny: bbox.min_y,
|
||||
maxx: bbox.max_x,
|
||||
maxy: bbox.max_y,
|
||||
},
|
||||
}),
|
||||
}))))
|
||||
}
|
||||
setError(null)
|
||||
return resolved
|
||||
} catch (requestError) {
|
||||
setError(formatError(
|
||||
requestError,
|
||||
'De regionale dekking kon niet voor alle bronpartities worden bepaald.',
|
||||
))
|
||||
return null
|
||||
}
|
||||
},
|
||||
[selectedProjectId],
|
||||
)
|
||||
|
||||
return { products, loading, error, resolveCoverage, resolveCoveragePartitions }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user