Add governed bathymetry profile workflow
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-17 15:25:00 +02:00
parent c0cab9e3c5
commit 5b4e18059a
31 changed files with 1990 additions and 4 deletions
+59 -1
View File
@@ -36,7 +36,7 @@ const DEFAULT_SELECTED_FEATURE_FILENAME = 'selected-feature.geojson'
const DEFAULT_AREA_SELECTION_FILENAME = 'area-selection.geojson'
const EMPTY_TEMPORAL_SERIES: DatasetCreateResponse[] = []
type DataThemeId = 'buildings' | 'space_occupation' | 'open_space' | 'population' | 'forest' | 'nature_value' | 'agriculture' | 'soil' | 'water' | 'flood_hazard' | 'elevation' | 'accessibility' | 'services' | 'roads' | 'parcels'
type DataThemeId = 'buildings' | 'space_occupation' | 'open_space' | 'population' | 'forest' | 'nature_value' | 'agriculture' | 'soil' | 'water' | 'bathymetry' | 'flood_hazard' | 'elevation' | 'accessibility' | 'services' | 'roads' | 'parcels'
interface DataTheme {
id: DataThemeId
@@ -116,6 +116,13 @@ const DATA_THEMES: DataTheme[] = [
description: 'Waterlopen, grachten, kanalen en wateroppervlakken.',
tokens: ['waterways', 'waterway', 'water', 'hydro', 'river', 'stream', 'canal', 'waterloop'],
},
{
id: 'bathymetry',
label: 'Waterbodem',
shortLabel: 'Dwarsprofielen',
description: 'Officiële historische VHA-dwarsprofielen met meetvelden en brondocumenten.',
tokens: ['bathymetry', 'bathymetry_profiles', 'dwarsprofielen', 'waterbodem'],
},
{
id: 'flood_hazard',
label: 'Overstroming',
@@ -170,6 +177,7 @@ const DATA_THEME_MAP_STYLES: Record<DataThemeId, { fill: string; line: string }>
agriculture: { fill: '#7b8f32', line: '#53671d' },
soil: { fill: '#9a7040', line: '#6f4c27' },
water: { fill: '#2676a8', line: '#155b85' },
bathymetry: { fill: '#0e7490', line: '#164e63' },
flood_hazard: { fill: '#1597c2', line: '#075985' },
elevation: { fill: '#a57a4b', line: '#315f59' },
accessibility: { fill: '#0f766e', line: '#115e59' },
@@ -188,6 +196,11 @@ function datasetAvailabilityLabel(dataset: DatasetCreateResponse, partitionCount
const resolution = Number(dataset.source_metadata?.['analysis_resolution_m'])
return `${Number.isFinite(resolution) ? `${resolution.toLocaleString('nl-BE')} m` : 'Raster'} overstromingsscenario${regionalSuffix}`
}
if (dataset.source_name === 'vmm_vha_bathymetry_profiles') {
const profiles = dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0
const documents = Number(dataset.source_metadata?.['document_count'] ?? 0)
return `${profiles.toLocaleString('nl-BE')} profielen · ${documents.toLocaleString('nl-BE')} bronbladen`
}
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'])
@@ -221,6 +234,9 @@ function datasetMatchesTheme(dataset: DatasetCreateResponse, theme: DataTheme):
if (dataset.source_name === 'vmm_flood_hazard') {
return theme.id === 'flood_hazard'
}
if (dataset.source_name === 'vmm_vha_bathymetry_profiles') {
return theme.id === 'bathymetry'
}
if (dataset.source_name === 'digitaal_vlaanderen_dhmv') {
return theme.id === 'elevation'
}
@@ -310,6 +326,7 @@ function pickThemeDataset(
(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) +
(dataset.source_name === 'vmm_vha_bathymetry_profiles' ? 5_000_000 : 0) +
(dataset.source_metadata?.['product_key'] === 'dtm_1m' ? 1_000_000 : 0) +
(dataset.source_metadata?.['product_key'] === 'pluviaal_current_t100' ? 1_000_000 : 0) +
(dataset.dataset_role === 'reference' ? 10_000 : 0)
@@ -618,6 +635,12 @@ export function MapWorkspace({
const selectedMapArea = areas.find((area) => area.id === selectedMapAreaId)
const regionalScopeSelected = Boolean(selectedMapArea && !isMunicipalityAreaName(selectedMapArea.name))
const featureProperties = selectedMapFeature?.properties ?? null
const isBathymetryProfile = featureProperties?.['provider'] === 'vmm_vha_bathymetry_profiles'
|| featureProperties?.['measurement_semantics'] === 'historical_cross_section_profile_point'
const bathymetryDocumentUrl = typeof featureProperties?.['source_document_url'] === 'string'
&& featureProperties['source_document_url'].startsWith('https://vha.waterinfo.be/')
? featureProperties['source_document_url']
: null
const featureSummaryEntries = featureProperties
? Object.entries(featureProperties)
.filter(([, value]) => value !== null && value !== undefined && typeof value !== 'object')
@@ -2555,6 +2578,41 @@ export function MapWorkspace({
</div>
{selectedMapFeature ? (
<>
{isBathymetryProfile ? (
<div className="bathymetry-profile-summary" aria-label="Samenvatting VHA-dwarsprofiel">
<div>
<span>Waterloop</span>
<strong>{String(featureProperties?.['watercourse_name'] ?? 'Onbekende waterloop')}</strong>
</div>
<div>
<span>Profiel</span>
<strong>{String(featureProperties?.['profile_number'] ?? 'n.v.t.')}</strong>
</div>
<div>
<span>Meetdatum</span>
<strong>{String(featureProperties?.['measurement_date'] ?? 'Niet geregistreerd')}</strong>
</div>
<div>
<span>Geregistreerde diepte</span>
<strong>
{typeof featureProperties?.['recorded_depth_m'] === 'number'
? `${featureProperties['recorded_depth_m'].toLocaleString('nl-BE')} m`
: 'Niet als veld beschikbaar'}
</strong>
</div>
{bathymetryDocumentUrl ? (
<a href={bathymetryDocumentUrl} target="_blank" rel="noreferrer">
Officieel profielblad openen
</a>
) : (
<small>Voor dit meetpunt is geen digitaal profielblad gekoppeld.</small>
)}
<p>
Historisch dwarsprofiel. Dit punt is geen continue actuele bodemkaart en levert zonder
gelijktijdig waterpeil geen actueel watervolume.
</p>
</div>
) : null}
<div className="feature-extract-grid" aria-label="Geometrie van het geselecteerde object">
<div>
<span>Geometrie</span>