feat: add cross-domain Mol data profile
This commit is contained in:
@@ -555,6 +555,14 @@ provider technology: space/buildings, nature/agriculture, soil/relief,
|
||||
mobility/accessibility, population/services and climate/living environment.
|
||||
The central definitions live in `src/lib/sourcePortfolio.ts`.
|
||||
|
||||
The current Map explorer also recognizes the governed `Ruimtebeslag`, `Open
|
||||
ruimte`, `Bevolking`, `Bereikbaarheid`, `Voorzieningen` and `Bodem` Datasets.
|
||||
The first five render persisted raster PNGs with product-specific legends and
|
||||
return cell-based semantic metrics; `Bodem` renders the persisted DOV polygons
|
||||
and exposes soil attributes on feature selection. A single official snapshot
|
||||
never activates Evolution by itself. Evolution is enabled only when at least
|
||||
two comparable observations exist.
|
||||
|
||||
The domain cards count only matching `ready` Datasets as operational. Audited
|
||||
official sources that have not passed acquisition, persistence and metric
|
||||
validation remain inside the collapsed follow-up list with an explicit
|
||||
|
||||
@@ -191,7 +191,10 @@ function App(): JSX.Element {
|
||||
const floodHazards = datasets.filter(
|
||||
(dataset) => dataset.dataset_type === 'raster' && dataset.source_name === 'vmm_flood_hazard' && dataset.status === 'ready',
|
||||
)
|
||||
return [...vectors, ...terrain, ...floodHazards]
|
||||
const thematicRasters = datasets.filter(
|
||||
(dataset) => dataset.dataset_type === 'raster' && dataset.source_name === 'department_omgeving_thematic_raster' && dataset.status === 'ready',
|
||||
)
|
||||
return [...vectors, ...terrain, ...floodHazards, ...thematicRasters]
|
||||
},
|
||||
[datasets],
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import { getDatasetDisplayName, getDatasetSourceDisplayName } from '../../lib/da
|
||||
import { TemporalTrendChart } from './TemporalTrendChart'
|
||||
import { terrainImageUrl } from '../../lib/terrainImage'
|
||||
import { floodHazardImageUrl } from '../../lib/floodHazardImage'
|
||||
import { thematicRasterImageUrl } from '../../lib/thematicRaster'
|
||||
|
||||
const DEFAULT_SELECTED_FEATURE_FILENAME = 'selected-feature.geojson'
|
||||
const DEFAULT_AREA_SELECTION_FILENAME = 'area-selection.geojson'
|
||||
@@ -15,7 +16,7 @@ const EMPTY_TEMPORAL_SERIES: DatasetCreateResponse[] = []
|
||||
const MOL_PROJECT_NAME = 'Mol Municipality Workbench'
|
||||
const KEMPEN_PROJECT_NAME = 'Kempen Regional Workbench'
|
||||
|
||||
type DataThemeId = 'buildings' | 'population' | 'forest' | 'nature_value' | 'agriculture' | 'water' | 'flood_hazard' | 'elevation' | 'roads' | 'parcels'
|
||||
type DataThemeId = 'buildings' | 'space_occupation' | 'open_space' | 'population' | 'forest' | 'nature_value' | 'agriculture' | 'soil' | 'water' | 'flood_hazard' | 'elevation' | 'accessibility' | 'services' | 'roads' | 'parcels'
|
||||
|
||||
interface DataTheme {
|
||||
id: DataThemeId
|
||||
@@ -39,6 +40,20 @@ const DATA_THEMES: DataTheme[] = [
|
||||
description: 'Gebouwen en gebouwcontouren uit GRB of een andere persistente bron.',
|
||||
tokens: ['buildings', 'building', 'gebouwen', 'gebouw', 'bebouwing', 'gbg'],
|
||||
},
|
||||
{
|
||||
id: 'space_occupation',
|
||||
label: 'Ruimtebeslag',
|
||||
shortLabel: 'Ruimtebeslag',
|
||||
description: 'Officiële 10 m-beleidskaart van ruimte ingenomen door wonen, economie, infrastructuur en recreatie.',
|
||||
tokens: ['space_occupation', 'ruimtebeslag', 'ruibes'],
|
||||
},
|
||||
{
|
||||
id: 'open_space',
|
||||
label: 'Open ruimte',
|
||||
shortLabel: 'Open ruimte',
|
||||
description: 'Officiële 10 m-beleidskaart van open ruimte buiten kernen en ruimtebeslag.',
|
||||
tokens: ['open_space', 'open ruimte', 'openruimte'],
|
||||
},
|
||||
{
|
||||
id: 'population',
|
||||
label: 'Bevolking',
|
||||
@@ -67,6 +82,13 @@ const DATA_THEMES: DataTheme[] = [
|
||||
description: 'Jaarlijkse officiële landbouwgebruikspercelen en hoofdteeltgroepen.',
|
||||
tokens: ['agriculture', 'agricultural', 'landbouw', 'landbouwgebruik', 'agpa'],
|
||||
},
|
||||
{
|
||||
id: 'soil',
|
||||
label: 'Bodem',
|
||||
shortLabel: 'Bodemkaart',
|
||||
description: 'Historische DOV-bodemkartering met bodemtype, textuur en drainageklasse voor Mol.',
|
||||
tokens: ['soil', 'bodem', 'bodemkaart', 'bodemtype', 'dov_soil_map'],
|
||||
},
|
||||
{
|
||||
id: 'water',
|
||||
label: 'Water',
|
||||
@@ -88,6 +110,20 @@ const DATA_THEMES: DataTheme[] = [
|
||||
description: 'Maaiveld- of oppervlaktehoogte, reliëf en helling uit DHMV II.',
|
||||
tokens: ['dhmv', 'elevation', 'height', 'hoogte', 'terrain', 'surface', 'dtm', 'dsm', 'reliëf'],
|
||||
},
|
||||
{
|
||||
id: 'accessibility',
|
||||
label: 'Bereikbaarheid',
|
||||
shortLabel: 'Knooppuntwaarde',
|
||||
description: 'Knooppuntwaarde van collectief vervoer per hectare voor referentiejaar 2022.',
|
||||
tokens: ['accessibility', 'bereikbaarheid', 'knooppuntwaarde', 'knptw'],
|
||||
},
|
||||
{
|
||||
id: 'services',
|
||||
label: 'Voorzieningen',
|
||||
shortLabel: 'Voorzieningenniveau',
|
||||
description: 'Genormaliseerde nabijheid van basis-, regionale en metropolitane voorzieningen in 2022.',
|
||||
tokens: ['services', 'voorzieningen', 'voorzieningenniveau', 'totvznv'],
|
||||
},
|
||||
{
|
||||
id: 'roads',
|
||||
label: 'Wegen',
|
||||
@@ -106,13 +142,18 @@ const DATA_THEMES: DataTheme[] = [
|
||||
|
||||
const DATA_THEME_MAP_STYLES: Record<DataThemeId, { fill: string; line: string }> = {
|
||||
buildings: { fill: '#d45f3d', line: '#9f3e24' },
|
||||
space_occupation: { fill: '#be3e33', line: '#8f2c24' },
|
||||
open_space: { fill: '#267a46', line: '#175c32' },
|
||||
population: { fill: '#7559a6', line: '#5b3f88' },
|
||||
forest: { fill: '#347950', line: '#225f3b' },
|
||||
nature_value: { fill: '#9a4f64', line: '#74364a' },
|
||||
agriculture: { fill: '#7b8f32', line: '#53671d' },
|
||||
soil: { fill: '#9a7040', line: '#6f4c27' },
|
||||
water: { fill: '#2676a8', line: '#155b85' },
|
||||
flood_hazard: { fill: '#1597c2', line: '#075985' },
|
||||
elevation: { fill: '#a57a4b', line: '#315f59' },
|
||||
accessibility: { fill: '#0f766e', line: '#115e59' },
|
||||
services: { fill: '#b66d16', line: '#854d0e' },
|
||||
roads: { fill: '#6b7280', line: '#4b5563' },
|
||||
parcels: { fill: '#a7792f', line: '#7d571f' },
|
||||
}
|
||||
@@ -126,6 +167,12 @@ function datasetAvailabilityLabel(dataset: DatasetCreateResponse): string {
|
||||
const resolution = Number(dataset.source_metadata?.['analysis_resolution_m'])
|
||||
return `${Number.isFinite(resolution) ? `${resolution.toLocaleString('nl-BE')} m` : 'Raster'} overstromingsscenario`
|
||||
}
|
||||
if (dataset.dataset_type === 'raster' && dataset.source_name === 'department_omgeving_thematic_raster') {
|
||||
const resolution = Number(dataset.source_metadata?.['analysis_resolution_m'])
|
||||
const year = Number(dataset.source_metadata?.['observation_year'])
|
||||
const resolutionLabel = Number.isFinite(resolution) ? `${resolution.toLocaleString('nl-BE')} m` : 'Raster'
|
||||
return `${resolutionLabel} officiële bron${Number.isFinite(year) ? ` · ${year}` : ''}`
|
||||
}
|
||||
return `${(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0).toLocaleString('nl-BE')} objecten beschikbaar`
|
||||
}
|
||||
|
||||
@@ -156,6 +203,12 @@ function datasetMatchesTheme(dataset: DatasetCreateResponse, theme: DataTheme):
|
||||
if (dataset.source_name === 'digitaal_vlaanderen_dhmv') {
|
||||
return theme.id === 'elevation'
|
||||
}
|
||||
if (dataset.source_name === 'department_omgeving_thematic_raster') {
|
||||
return dataset.source_metadata?.['theme'] === theme.id
|
||||
}
|
||||
if (dataset.source_name === 'dov_soil_map') {
|
||||
return theme.id === 'soil'
|
||||
}
|
||||
const searchText = datasetSearchText(dataset)
|
||||
return theme.tokens.some((token) => searchText.includes(token))
|
||||
}
|
||||
@@ -184,6 +237,7 @@ function pickThemeDataset(
|
||||
(dataset.source_name === 'department_omgeving_land_use' ? 90_000 : 0) +
|
||||
(dataset.source_name === 'inbo_bwk_natura2000' ? 95_000 : 0) +
|
||||
(dataset.source_name === 'agentschap_landbouw_zeevisserij_agricultural_parcels' ? 98_000 : 0) +
|
||||
(dataset.source_name === 'department_omgeving_thematic_raster' ? 5_000_000 : 0) +
|
||||
(dataset.source_name === 'digitaal_vlaanderen_buildings_addresses_register' ? 5_000_000 : 0) +
|
||||
(dataset.source_name === 'digitaal_vlaanderen_dhmv' ? 5_000_000 : 0) +
|
||||
(dataset.source_name === 'vmm_flood_hazard' ? 5_000_000 : 0) +
|
||||
@@ -763,7 +817,20 @@ export function MapWorkspace({
|
||||
opacity: 0.82,
|
||||
}
|
||||
: null
|
||||
const activeImageOverlay = floodHazardImageOverlay ?? terrainImageOverlay ?? orthophotoImageOverlay
|
||||
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 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 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
|
||||
@@ -775,7 +842,9 @@ export function MapWorkspace({
|
||||
[availableMapDatasets],
|
||||
)
|
||||
const activeTemporalSeriesGroups = themeTemporalSeriesMap[activeTheme.id]
|
||||
const availableEvolutionThemes = DATA_THEMES.filter((theme) => themeTemporalSeriesMap[theme.id].length > 0)
|
||||
const availableEvolutionThemes = DATA_THEMES.filter((theme) =>
|
||||
themeTemporalSeriesMap[theme.id].some((group) => group.items.length >= 2),
|
||||
)
|
||||
const activeTemporalSeriesGroup = activeTemporalSeriesGroups.find((group) => group.key === selectedTemporalSeriesKey)
|
||||
?? activeTemporalSeriesGroups[0]
|
||||
const activeTemporalSeries = activeTemporalSeriesGroup?.items ?? EMPTY_TEMPORAL_SERIES
|
||||
@@ -815,13 +884,28 @@ export function MapWorkspace({
|
||||
(metric) => metric.metric_key !== activeSelectionResult?.summary?.primary_metric_key,
|
||||
)
|
||||
const terrainReliefMetric = activeSupportingMetrics.find((metric) => metric.metric_key === 'relief_m')
|
||||
const populationDensityMetric = activeSupportingMetrics.find((metric) => metric.metric_key === 'population_density_mean_per_ha')
|
||||
const scoreMedianMetric = activeSupportingMetrics.find((metric) => metric.metric_key.endsWith('_median'))
|
||||
const activeSecondaryMetric = activeMetricUnit === 'm TAW'
|
||||
? terrainReliefMetric ? `${terrainReliefMetric.metric_value.toLocaleString('nl-BE', { maximumFractionDigits: 2 })} m reliëf` : null
|
||||
: selectedAreaSquareMetres && selectedAreaSquareMetres > 0
|
||||
? activeMetricUnit === 'ha'
|
||||
: activeMetricUnit === 'inwoners'
|
||||
? populationDensityMetric ? selectionMetricLabel(populationDensityMetric) : null
|
||||
: activeMetricUnit.startsWith('score')
|
||||
? scoreMedianMetric ? selectionMetricLabel(scoreMedianMetric) : null
|
||||
: selectedAreaSquareMetres && selectedAreaSquareMetres > 0
|
||||
? activeMetricUnit === 'ha'
|
||||
? `${((activeMetricValue * 10_000) / selectedAreaSquareMetres * 100).toLocaleString('nl-BE', { maximumFractionDigits: 1 })}% dekking`
|
||||
: `${(activeMetricValue / (selectedAreaSquareMetres / 1_000_000)).toLocaleString('nl-BE', { maximumFractionDigits: 1 })} ${activeMetricUnit} / km2`
|
||||
: null
|
||||
const activeSecondaryLabel = activeMetricUnit === 'ha'
|
||||
? 'Aandeel selectie'
|
||||
: activeMetricUnit === 'm TAW'
|
||||
? 'Reliëf'
|
||||
: activeMetricUnit === 'inwoners'
|
||||
? 'Gemiddelde dichtheid'
|
||||
: activeMetricUnit.startsWith('score')
|
||||
? 'Mediaan'
|
||||
: 'Dichtheid'
|
||||
const selectedResultProperties = useMemo(() => {
|
||||
const keys = new Map<string, Set<string>>()
|
||||
for (const feature of activeSelectionResult?.geojson.features ?? []) {
|
||||
@@ -1185,7 +1269,7 @@ export function MapWorkspace({
|
||||
const dataset = themeDatasetMap[theme.id]
|
||||
const temporalGroups = themeTemporalSeriesMap[theme.id]
|
||||
const temporalGroup = temporalGroups[0]
|
||||
const evolutionAvailable = temporalGroups.length > 0
|
||||
const evolutionAvailable = temporalGroups.some((group) => group.items.length >= 2)
|
||||
const available = Boolean(dataset) && (analysisMode === 'current' || evolutionAvailable)
|
||||
const active = activeThemeId === theme.id
|
||||
const firstObservation = temporalGroup?.items[0]?.observed_at
|
||||
@@ -1382,7 +1466,12 @@ export function MapWorkspace({
|
||||
/>
|
||||
<div className="geo-map-legend" aria-label="Kaartlegende">
|
||||
<span><i className="geo-legend-area" /> Werkgebied</span>
|
||||
{activeImageOverlay ? <span><i className="geo-legend-imagery" /> {activeImageOverlay.label}</span> : null}
|
||||
{thematicRasterImageOverlay ? (
|
||||
<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}
|
||||
{analysisOverlayActive ? (
|
||||
<>
|
||||
<span><i className="geo-legend-layer geo-legend-layer-buildings" /> AI-kandidaten</span>
|
||||
@@ -1585,7 +1674,7 @@ export function MapWorkspace({
|
||||
<strong>{activeSelectionResult ? resultMetricLabel(activeSelectionResult) : 'Geen resultaat'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{activeMetricUnit === 'ha' ? 'Aandeel selectie' : activeMetricUnit === 'm TAW' ? 'Reliëf' : 'Dichtheid'}</span>
|
||||
<span>{activeSecondaryLabel}</span>
|
||||
<strong>{activeSecondaryMetric ?? (selectedDensity === null ? 'n.v.t.' : `${selectedDensity.toLocaleString('nl-BE', { maximumFractionDigits: 1 })} / km2`)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1658,7 +1747,7 @@ export function MapWorkspace({
|
||||
|
||||
{analysisMode === 'current' ? (
|
||||
<div className="geo-result-actions">
|
||||
<button className="secondary-action" disabled={!activeSelectionResult || activeTheme.id === 'elevation' || activeTheme.id === 'flood_hazard'} type="button" onClick={downloadActiveThemeSelection}>Download GeoJSON</button>
|
||||
<button className="secondary-action" disabled={!activeSelectionResult || activeThemeDataset?.dataset_type === 'raster'} type="button" onClick={downloadActiveThemeSelection}>Download GeoJSON</button>
|
||||
<button className="secondary-action" disabled={!activeSelectionResult} type="button" onClick={copyActiveThemeSelection}>Kopieer gegevens</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { formatError } from '../lib/formatError'
|
||||
import type { DatasetCreateResponse, VectorSelectionBBox, VectorSelectionResponse } from '../types'
|
||||
import { terrainSelectionToMapSelection } from '../lib/terrainSelection'
|
||||
import { floodHazardSelectionToMapSelection } from '../lib/floodHazardSelection'
|
||||
import { thematicRasterSelectionToMapSelection } from '../lib/thematicRaster'
|
||||
|
||||
interface MapSelectionExtractOptions {
|
||||
selectedProjectId: string | null
|
||||
@@ -42,8 +43,9 @@ export function useMapSelectionExtract({
|
||||
}
|
||||
const terrainDataset = selectedDataset.dataset_type === 'raster' && selectedDataset.source_name === 'digitaal_vlaanderen_dhmv'
|
||||
const floodHazardDataset = selectedDataset.dataset_type === 'raster' && selectedDataset.source_name === 'vmm_flood_hazard'
|
||||
if (!isVectorDatasetType(selectedDataset.dataset_type) && !terrainDataset && !floodHazardDataset) {
|
||||
setMapSelectionError('Gebiedsanalyse ondersteunt een vectorlaag, DHMV-hoogtemodel of beheerd VMM-overstromingsscenario.')
|
||||
const thematicRasterDataset = selectedDataset.dataset_type === 'raster' && selectedDataset.source_name === 'department_omgeving_thematic_raster'
|
||||
if (!isVectorDatasetType(selectedDataset.dataset_type) && !terrainDataset && !floodHazardDataset && !thematicRasterDataset) {
|
||||
setMapSelectionError('Gebiedsanalyse ondersteunt een vectorlaag of een beheerd thematisch raster.')
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -63,6 +65,11 @@ export function useMapSelectionExtract({
|
||||
bbox: { ...bbox, crs: 'EPSG:4326' },
|
||||
area_id: areaId,
|
||||
}))
|
||||
: thematicRasterDataset
|
||||
? thematicRasterSelectionToMapSelection(await datasetsApi.selectThematicRaster(selectedProjectId, selectedDataset.id, {
|
||||
bbox: { ...bbox, crs: 'EPSG:4326' },
|
||||
area_id: areaId,
|
||||
}))
|
||||
: await datasetsApi.selectVectorFeatures(selectedProjectId, selectedDataset.id, {
|
||||
bbox: { ...bbox, crs: 'EPSG:4326' },
|
||||
area_id: areaId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { datasetsApi } from '../services/api/datasets'
|
||||
import type { DatasetCreateResponse, VectorSelectionBBox, VectorSelectionResponse } from '../types'
|
||||
import { terrainSelectionToMapSelection } from '../lib/terrainSelection'
|
||||
import { floodHazardSelectionToMapSelection } from '../lib/floodHazardSelection'
|
||||
import { thematicRasterSelectionToMapSelection } from '../lib/thematicRaster'
|
||||
|
||||
export interface MapThemeQuery<TThemeId extends string> {
|
||||
themeId: TThemeId
|
||||
@@ -66,6 +67,11 @@ export function useMapThemeSelectionInsights<TThemeId extends string>(
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
}))
|
||||
: dataset.dataset_type === 'raster' && dataset.source_name === 'department_omgeving_thematic_raster'
|
||||
? thematicRasterSelectionToMapSelection(await datasetsApi.selectThematicRaster(selectedProjectId, dataset.id, {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
}))
|
||||
: await datasetsApi.selectVectorFeatures(selectedProjectId, dataset.id, {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
|
||||
@@ -9,8 +9,13 @@ const DATASET_LABEL_BY_LAYER: Record<string, string> = {
|
||||
forest: 'Bos en groen',
|
||||
nature_value: 'Natuurwaarde',
|
||||
agriculture: 'Landbouwgebruikspercelen',
|
||||
soil: 'Digitale bodemkaart',
|
||||
flood_hazard: 'Overstromingsgevaar',
|
||||
elevation: 'Hoogte en reliëf',
|
||||
space_occupation: 'Ruimtebeslag',
|
||||
open_space: 'Open ruimte',
|
||||
accessibility: 'Knooppuntwaarde',
|
||||
services: 'Voorzieningenniveau',
|
||||
building_registry: 'Gebouwenregister',
|
||||
regional_boundary: 'Grens vervoerregio Kempen',
|
||||
municipality_boundaries: 'Gemeentegrenzen Kempen',
|
||||
@@ -29,6 +34,8 @@ const DATASET_SOURCE_LABELS: Record<string, string> = {
|
||||
vrbg: 'Digitaal Vlaanderen',
|
||||
waterinfo: 'Waterinfo Vlaanderen',
|
||||
vmm_flood_hazard: 'Vlaamse Milieumaatschappij',
|
||||
department_omgeving_thematic_raster: 'Departement Omgeving',
|
||||
dov_soil_map: 'Databank Ondergrond Vlaanderen',
|
||||
}
|
||||
|
||||
export function getDatasetSourceDisplayName(dataset: DatasetCreateResponse): string {
|
||||
@@ -45,6 +52,10 @@ export function getDatasetDisplayName(dataset: DatasetCreateResponse): string {
|
||||
const productName = dataset.source_metadata?.['product_display_name']
|
||||
return typeof productName === 'string' && productName.trim() ? productName : 'VMM-overstromingsscenario'
|
||||
}
|
||||
if (dataset.source_name === 'department_omgeving_thematic_raster') {
|
||||
const productName = dataset.source_metadata?.['product_display_name']
|
||||
return typeof productName === 'string' && productName.trim() ? productName : 'Officieel Vlaams themaraster'
|
||||
}
|
||||
const layer = (dataset.reference_layer_name ?? dataset.source_metadata?.layer_name ?? dataset.source_metadata?.layer_type ?? '')
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
|
||||
@@ -35,6 +35,9 @@ const sourceNameIs = (dataset: DatasetCreateResponse, sourceName: string): boole
|
||||
const referenceLayerIs = (dataset: DatasetCreateResponse, layerName: string): boolean =>
|
||||
String(dataset.reference_layer_name ?? dataset.source_metadata?.['theme'] ?? '').toLowerCase() === layerName
|
||||
|
||||
const thematicProductIs = (dataset: DatasetCreateResponse, productKey: string): boolean =>
|
||||
sourceNameIs(dataset, 'department_omgeving_thematic_raster') && dataset.source_metadata?.['product_key'] === productKey
|
||||
|
||||
export const SOURCE_DOMAINS: SourceDomainDefinition[] = [
|
||||
{
|
||||
key: 'space',
|
||||
@@ -127,7 +130,7 @@ export const OFFICIAL_SOURCE_PORTFOLIO: OfficialSourceDefinition[] = [
|
||||
metricExamples: 'hectare ruimtebeslag, aandeel, vergelijking met open ruimte',
|
||||
priority: 'next',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/ruimtebeslag-vlaanderen-toestand-2025',
|
||||
matches: (dataset) => sourceNameIs(dataset, 'department_omgeving_space_occupation'),
|
||||
matches: (dataset) => thematicProductIs(dataset, 'space_occupation_2025'),
|
||||
},
|
||||
{
|
||||
key: 'settlement_typology',
|
||||
@@ -137,7 +140,7 @@ export const OFFICIAL_SOURCE_PORTFOLIO: OfficialSourceDefinition[] = [
|
||||
coverage: 'Toestand 2022',
|
||||
value: 'Begrijpelijke morfologie voor kern, lint en verspreide bebouwing.',
|
||||
metricExamples: 'oppervlakte en aandeel per morfologisch type',
|
||||
priority: 'planned',
|
||||
priority: 'next',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/kernen-linten-verspreide-bebouwing-in-vlaanderen-kernen-toestand-2022',
|
||||
matches: (dataset) => sourceNameIs(dataset, 'department_omgeving_settlement_typology'),
|
||||
},
|
||||
@@ -187,7 +190,7 @@ export const OFFICIAL_SOURCE_PORTFOLIO: OfficialSourceDefinition[] = [
|
||||
metricExamples: 'hectare open ruimte, aandeel en fragmentatie',
|
||||
priority: 'planned',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/open-ruimte-vlaanderen-toestand-2022',
|
||||
matches: (dataset) => sourceNameIs(dataset, 'department_omgeving_open_space'),
|
||||
matches: (dataset) => thematicProductIs(dataset, 'open_space_2022'),
|
||||
},
|
||||
{
|
||||
key: 'dhmv',
|
||||
@@ -244,10 +247,10 @@ export const OFFICIAL_SOURCE_PORTFOLIO: OfficialSourceDefinition[] = [
|
||||
owner: 'Departement Omgeving',
|
||||
coverage: '1 ha raster, toestand 2022',
|
||||
value: 'Modelmatige bereikbaarheid via collectief vervoer.',
|
||||
metricExamples: 'gemiddelde score en aandeel per bereikbaarheidsklasse',
|
||||
metricExamples: 'gemiddelde, mediaan en percentielen van de bronindex',
|
||||
priority: 'next',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/knooppuntwaarde-per-ha-toestand-2022',
|
||||
matches: (dataset) => sourceNameIs(dataset, 'department_omgeving_node_value'),
|
||||
matches: (dataset) => thematicProductIs(dataset, 'node_value_2022'),
|
||||
},
|
||||
{
|
||||
key: 'hoppin',
|
||||
@@ -295,7 +298,7 @@ export const OFFICIAL_SOURCE_PORTFOLIO: OfficialSourceDefinition[] = [
|
||||
metricExamples: 'geschat aantal inwoners en inwoners per hectare',
|
||||
priority: 'next',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/inwonersdichtheid-per-ha-vlaanderen-toestand-2019',
|
||||
matches: (dataset) => sourceNameIs(dataset, 'department_omgeving_population_density'),
|
||||
matches: (dataset) => thematicProductIs(dataset, 'population_density_2019'),
|
||||
},
|
||||
{
|
||||
key: 'service_level',
|
||||
@@ -304,10 +307,10 @@ export const OFFICIAL_SOURCE_PORTFOLIO: OfficialSourceDefinition[] = [
|
||||
owner: 'Departement Omgeving',
|
||||
coverage: '1 ha raster, toestand 2022',
|
||||
value: 'Nabijheid van dagelijkse voorzieningen in één brongetrouwe score.',
|
||||
metricExamples: 'gemiddelde score en aandeel per voorzieningsklasse',
|
||||
metricExamples: 'gemiddelde, mediaan en percentielen van de 0-1-score',
|
||||
priority: 'next',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/totaal-voorzieningenniveau-toestand-2022',
|
||||
matches: (dataset) => sourceNameIs(dataset, 'department_omgeving_service_level'),
|
||||
matches: (dataset) => thematicProductIs(dataset, 'service_level_2022'),
|
||||
},
|
||||
{
|
||||
key: 'municipality_indicators',
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { ThematicRasterSelectionResponse, VectorSelectionResponse } from '../types'
|
||||
|
||||
export function thematicRasterImageUrl(projectId: string, datasetId: string): string {
|
||||
return `/api/v1/projects/${projectId}/datasets/${datasetId}/raster/thematic/image`
|
||||
}
|
||||
|
||||
export function thematicRasterSelectionToMapSelection(result: ThematicRasterSelectionResponse): VectorSelectionResponse {
|
||||
return {
|
||||
selection_bbox: result.selection_bbox,
|
||||
selection_area_id: result.selection_area_id,
|
||||
feature_count: result.valid_cell_count,
|
||||
total_feature_count: result.valid_cell_count,
|
||||
limit: 0,
|
||||
truncated: false,
|
||||
geojson: { type: 'FeatureCollection', features: [] },
|
||||
summary: {
|
||||
...result.summary,
|
||||
feature_count: result.valid_cell_count,
|
||||
is_estimate: true,
|
||||
warning: result.limitation_message,
|
||||
metrics: result.summary.metrics.map((metric) => ({
|
||||
...metric,
|
||||
is_estimate: metric.is_estimate ?? true,
|
||||
})),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,9 @@ import type {
|
||||
FloodHazardSelectionResponse,
|
||||
DhmvProductRead,
|
||||
TerrainSelectionResponse,
|
||||
ThematicRasterAcquireRequest,
|
||||
ThematicRasterProductRead,
|
||||
ThematicRasterSelectionResponse,
|
||||
} from '../../types'
|
||||
|
||||
const DATASET_PAGE_SIZE = 200
|
||||
@@ -141,6 +144,16 @@ 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),
|
||||
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 }> =>
|
||||
apiGet<{ items: ThematicRasterProductRead[]; total: number }>(`/api/v1/projects/${projectId}/datasets/thematic-raster/products`),
|
||||
selectThematicRaster: (
|
||||
projectId: string,
|
||||
datasetId: string,
|
||||
payload: { bbox: VectorSelectionRequest['bbox']; area_id?: string },
|
||||
): Promise<ThematicRasterSelectionResponse> =>
|
||||
apiPost<ThematicRasterSelectionResponse>(`/api/v1/projects/${projectId}/datasets/${datasetId}/raster/thematic/select`, payload),
|
||||
refreshMetadata: (projectId: string, datasetId: string): Promise<DatasetCreateResponse> =>
|
||||
apiPost<DatasetCreateResponse>(`/api/v1/projects/${projectId}/datasets/${datasetId}/metadata/refresh`, {}),
|
||||
inspectRaster: (projectId: string, datasetId: string): Promise<RasterInspectResponse> =>
|
||||
|
||||
@@ -5751,13 +5751,18 @@ section {
|
||||
}
|
||||
|
||||
.geo-theme-symbol-buildings { background: #d45f3d; }
|
||||
.geo-theme-symbol-space_occupation { background: #be3e33; }
|
||||
.geo-theme-symbol-open_space { background: #267a46; }
|
||||
.geo-theme-symbol-population { background: #7559a6; }
|
||||
.geo-theme-symbol-forest { background: #347950; }
|
||||
.geo-theme-symbol-nature_value { background: #9a4f64; }
|
||||
.geo-theme-symbol-agriculture { background: #7b8f32; }
|
||||
.geo-theme-symbol-soil { background: #9a7040; }
|
||||
.geo-theme-symbol-water { background: #2676a8; }
|
||||
.geo-theme-symbol-flood_hazard { background: #1597c2; }
|
||||
.geo-theme-symbol-elevation { background: #a57a4b; }
|
||||
.geo-theme-symbol-accessibility { background: #0f766e; }
|
||||
.geo-theme-symbol-services { background: #b66d16; }
|
||||
.geo-theme-symbol-roads { background: #6b7280; }
|
||||
.geo-theme-symbol-parcels { background: #a7792f; }
|
||||
|
||||
@@ -5932,6 +5937,16 @@ section {
|
||||
background: rgba(117, 89, 166, 0.24);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-space_occupation {
|
||||
border-color: #8f2c24;
|
||||
background: rgba(190, 62, 51, 0.28);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-open_space {
|
||||
border-color: #175c32;
|
||||
background: rgba(38, 122, 70, 0.26);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-forest {
|
||||
border-color: #225f3b;
|
||||
background: rgba(52, 121, 80, 0.24);
|
||||
@@ -5942,6 +5957,11 @@ section {
|
||||
background: rgba(38, 118, 168, 0.24);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-soil {
|
||||
border-color: #6f4c27;
|
||||
background: rgba(154, 112, 64, 0.24);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-flood_hazard {
|
||||
border-color: #075985;
|
||||
background: rgba(21, 151, 194, 0.28);
|
||||
@@ -5952,6 +5972,16 @@ section {
|
||||
background: rgba(165, 122, 75, 0.26);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-accessibility {
|
||||
border-color: #115e59;
|
||||
background: rgba(15, 118, 110, 0.26);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-services {
|
||||
border-color: #854d0e;
|
||||
background: rgba(182, 109, 22, 0.26);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-roads {
|
||||
border-color: #4b5563;
|
||||
background: rgba(107, 114, 128, 0.24);
|
||||
@@ -5972,6 +6002,32 @@ section {
|
||||
background: linear-gradient(135deg, #7a9b68 0 33%, #d1b37a 33% 66%, #8eb5cb 66%);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-thematic {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.42rem;
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-thematic small {
|
||||
color: #42504c;
|
||||
font-size: 0.66rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-ramp {
|
||||
display: block;
|
||||
width: 3.4rem;
|
||||
height: 0.62rem;
|
||||
border: 1px solid rgba(23, 39, 34, 0.25);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.geo-legend-ramp-space_occupation { background: linear-gradient(90deg, #fbe7d3, #be3e33); }
|
||||
.geo-legend-ramp-open_space { background: linear-gradient(90deg, #ddeedb, #267a46); }
|
||||
.geo-legend-ramp-population { background: linear-gradient(90deg, #eee7f6, #673a97); }
|
||||
.geo-legend-ramp-accessibility { background: linear-gradient(90deg, #e9f1f4, #0f766e); }
|
||||
.geo-legend-ramp-services { background: linear-gradient(90deg, #fff4bf, #b66d16); }
|
||||
|
||||
.geo-map-legend .geo-legend-added {
|
||||
border-color: #15803d;
|
||||
background: rgba(22, 163, 74, 0.2);
|
||||
|
||||
@@ -434,6 +434,57 @@ export interface FloodHazardSelectionResponse {
|
||||
generated_at: string
|
||||
}
|
||||
|
||||
export interface ThematicRasterAcquireRequest {
|
||||
bbox: VectorSelectionBBox
|
||||
area_id?: string | null
|
||||
product_key: string
|
||||
force_refresh?: boolean
|
||||
}
|
||||
|
||||
export interface ThematicRasterProductRead {
|
||||
key: string
|
||||
display_name: string
|
||||
theme: 'space_occupation' | 'open_space' | 'population' | 'accessibility' | 'services'
|
||||
metric_kind: 'binary_area' | 'population_density' | 'index_score' | 'normalized_score'
|
||||
coverage_id: string
|
||||
native_resolution_m: number
|
||||
source_crs: 'EPSG:31370'
|
||||
source_value_unit: string
|
||||
observation_year: number
|
||||
source_version: string
|
||||
catalog_url: string
|
||||
attribution: string
|
||||
license_note: string
|
||||
legend_min_label: string
|
||||
legend_max_label: string
|
||||
limitation_message: string
|
||||
}
|
||||
|
||||
export interface ThematicRasterSelectionResponse {
|
||||
dataset_id: string
|
||||
product_key: string
|
||||
theme: ThematicRasterProductRead['theme']
|
||||
metric_kind: ThematicRasterProductRead['metric_kind']
|
||||
selection_bbox: VectorSelectionBBox
|
||||
selection_area_id?: string | null
|
||||
selected_cell_count: number
|
||||
valid_cell_count: number
|
||||
coverage_ratio: number
|
||||
resolution_m: number
|
||||
observation_year: number
|
||||
summary: {
|
||||
metric_label: string
|
||||
metric_value: number
|
||||
metric_unit: string
|
||||
aggregation_method: string
|
||||
primary_metric_key: string
|
||||
metrics: Array<VectorSelectionMetric & { is_estimate: boolean }>
|
||||
}
|
||||
unsupported_metrics: string[]
|
||||
limitation_message: string
|
||||
generated_at: string
|
||||
}
|
||||
|
||||
export interface MapImageOverlay {
|
||||
url: string
|
||||
bbox: [number, number, number, number]
|
||||
|
||||
Reference in New Issue
Block a user