Add governed DHMV terrain analysis
This commit is contained in:
@@ -492,6 +492,14 @@ another municipality or the complete Kempen scope falls back to the complete
|
||||
regional GRB building layer. This avoids presenting a Mol-only snapshot as
|
||||
regional coverage.
|
||||
|
||||
When a governed DHMV Dataset is loaded, the Map explorer adds `Hoogte &
|
||||
reliëf`. The active 5 m analysis raster is rendered as a colour-relief
|
||||
MapLibre image over the ordinary OpenStreetMap context. A drawn rectangle or
|
||||
the exact selected Area returns measured DTM/DSM height, relief and slope with
|
||||
TAW and the 2013-2015 source period visible. Raster cells are not presented as
|
||||
objects. GeoJSON export is disabled for this raster-only result, and the UI
|
||||
explicitly states that water depth and volume cannot be derived from DHMV.
|
||||
|
||||
## Useful repository scripts
|
||||
|
||||
- `bash scripts/frontend_install.sh`
|
||||
|
||||
@@ -182,7 +182,13 @@ function App(): JSX.Element {
|
||||
)
|
||||
const availableVectorDatasets = useMemo(() => datasets.filter((item) => isVectorDatasetType(item.dataset_type)), [datasets])
|
||||
const availableMapDatasets = useMemo(
|
||||
() => datasets.filter((dataset) => isVectorDatasetType(dataset.dataset_type) && dataset.status === 'ready'),
|
||||
() => {
|
||||
const vectors = datasets.filter((dataset) => isVectorDatasetType(dataset.dataset_type) && dataset.status === 'ready')
|
||||
const terrain = datasets.filter(
|
||||
(dataset) => dataset.dataset_type === 'raster' && dataset.source_name === 'digitaal_vlaanderen_dhmv' && dataset.status === 'ready',
|
||||
)
|
||||
return [...vectors, ...terrain]
|
||||
},
|
||||
[datasets],
|
||||
)
|
||||
const referenceDatasets = useMemo(
|
||||
@@ -1023,7 +1029,7 @@ function App(): JSX.Element {
|
||||
orthophotoResult={mapOrthophotoAnalysis.lastResult}
|
||||
orthophotoImageUrl={mapOrthophotoAnalysis.imageUrl}
|
||||
availableMapDatasets={availableMapDatasets}
|
||||
selectedMapDatasetId={selectedDataset && isVectorDatasetType(selectedDataset.dataset_type) ? selectedDataset.id : ''}
|
||||
selectedMapDatasetId={selectedDataset && availableMapDatasets.some((dataset) => dataset.id === selectedDataset.id) ? selectedDataset.id : ''}
|
||||
selectedFeature={selectedMapFeature}
|
||||
onSelectMapArea={setSelectedMapAreaId}
|
||||
onOpenDatasetInMap={openDatasetInMap}
|
||||
|
||||
@@ -12,6 +12,7 @@ const THEME_LABELS: Record<string, string> = {
|
||||
nature_value: 'Natuurwaarde',
|
||||
agriculture: 'Landbouw',
|
||||
water: 'Water',
|
||||
elevation: 'Hoogte en reliëf',
|
||||
roads: 'Wegen en transport',
|
||||
parcels: 'Percelen',
|
||||
}
|
||||
@@ -54,7 +55,7 @@ const AVAILABLE_SOURCES = [
|
||||
name: 'Digitaal Hoogtemodel Vlaanderen II',
|
||||
owner: 'Digitaal Vlaanderen',
|
||||
coverage: 'LiDAR-opname 2013-2015, DTM/DSM 1 m en 5 m',
|
||||
value: 'Hoogte, reliëf, helling en afstroming; geen waterdiepte',
|
||||
value: 'Hoogte, reliëf en helling; afstroming is afgeleid, geen waterdiepte',
|
||||
url: 'https://www.vlaanderen.be/digitaal-vlaanderen/onze-diensten-en-platformen/earth-observation-data-science-eodas/het-digitaal-hoogtemodel/digitaal-hoogtemodel-vlaanderen-ii',
|
||||
},
|
||||
{
|
||||
@@ -111,6 +112,7 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
|
||||
const buildingsRegisterDatasets = ready.filter(
|
||||
(dataset) => dataset.source_name === 'digitaal_vlaanderen_buildings_addresses_register',
|
||||
)
|
||||
const dhmvDatasets = ready.filter((dataset) => dataset.source_name === 'digitaal_vlaanderen_dhmv')
|
||||
const latestBuildingsRegister = [...buildingsRegisterDatasets].sort(
|
||||
(left, right) => new Date(right.observed_at ?? 0).getTime() - new Date(left.observed_at ?? 0).getTime(),
|
||||
)[0]
|
||||
@@ -122,6 +124,7 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
|
||||
if (source.key === 'bwk') return bwkDatasets.length === 0
|
||||
if (source.key === 'agriculture') return agricultureDatasets.length === 0
|
||||
if (source.key === 'buildings_register') return buildingsRegisterDatasets.length === 0
|
||||
if (source.key === 'elevation') return dhmvDatasets.length === 0
|
||||
return true
|
||||
})
|
||||
const themes = Object.keys(THEME_LABELS).map((theme) => {
|
||||
@@ -181,7 +184,7 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
|
||||
))}
|
||||
</div>
|
||||
|
||||
{waterinfoDatasets.length > 0 || historicalOrthophotos.length > 0 || bwkDatasets.length > 0 || agricultureDatasets.length > 0 || buildingsRegisterDatasets.length > 0 ? (
|
||||
{waterinfoDatasets.length > 0 || historicalOrthophotos.length > 0 || bwkDatasets.length > 0 || agricultureDatasets.length > 0 || buildingsRegisterDatasets.length > 0 || dhmvDatasets.length > 0 ? (
|
||||
<div className="source-catalog-loaded" aria-label="Aanvullende ingeladen bronnen">
|
||||
{waterinfoDatasets.length > 0 ? (
|
||||
<article>
|
||||
@@ -222,6 +225,13 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
|
||||
<p>Registerstatus en geaggregeerde koppelingen voor Mol; adreslabels en persoonsgegevens worden niet in de kaartlaag getoond.</p>
|
||||
</article>
|
||||
) : null}
|
||||
{dhmvDatasets.length > 0 ? (
|
||||
<article>
|
||||
<strong>Digitaal Hoogtemodel Vlaanderen II</strong>
|
||||
<span>{dhmvDatasets.length} rasterproducten · DTM/DSM · analyse op 5 m</span>
|
||||
<p>Hoogte in TAW, reliëf en helling uit opnameperiode 2013-2015. Geen waterdiepte of watervolume.</p>
|
||||
</article>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useMapThemeSelectionInsights } from '../../hooks/useMapThemeSelectionIn
|
||||
import { useTemporalComparison } from '../../hooks/useTemporalComparison'
|
||||
import { getDatasetDisplayName, getDatasetSourceDisplayName } from '../../lib/datasetDisplay'
|
||||
import { TemporalTrendChart } from './TemporalTrendChart'
|
||||
import { terrainImageUrl } from '../../lib/terrainImage'
|
||||
|
||||
const DEFAULT_SELECTED_FEATURE_FILENAME = 'selected-feature.geojson'
|
||||
const DEFAULT_AREA_SELECTION_FILENAME = 'area-selection.geojson'
|
||||
@@ -13,7 +14,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' | 'roads' | 'parcels'
|
||||
type DataThemeId = 'buildings' | 'population' | 'forest' | 'nature_value' | 'agriculture' | 'water' | 'elevation' | 'roads' | 'parcels'
|
||||
|
||||
interface DataTheme {
|
||||
id: DataThemeId
|
||||
@@ -72,6 +73,13 @@ const DATA_THEMES: DataTheme[] = [
|
||||
description: 'Waterlopen, grachten, kanalen en wateroppervlakken.',
|
||||
tokens: ['waterways', 'waterway', 'water', 'hydro', 'river', 'stream', 'canal', 'waterloop'],
|
||||
},
|
||||
{
|
||||
id: 'elevation',
|
||||
label: 'Hoogte & reliëf',
|
||||
shortLabel: 'Hoogte',
|
||||
description: 'Maaiveld- of oppervlaktehoogte, reliëf en helling uit DHMV II.',
|
||||
tokens: ['dhmv', 'elevation', 'height', 'hoogte', 'terrain', 'surface', 'dtm', 'dsm', 'reliëf'],
|
||||
},
|
||||
{
|
||||
id: 'roads',
|
||||
label: 'Wegen',
|
||||
@@ -95,10 +103,19 @@ const DATA_THEME_MAP_STYLES: Record<DataThemeId, { fill: string; line: string }>
|
||||
nature_value: { fill: '#9a4f64', line: '#74364a' },
|
||||
agriculture: { fill: '#7b8f32', line: '#53671d' },
|
||||
water: { fill: '#2676a8', line: '#155b85' },
|
||||
elevation: { fill: '#a57a4b', line: '#315f59' },
|
||||
roads: { fill: '#6b7280', line: '#4b5563' },
|
||||
parcels: { fill: '#a7792f', line: '#7d571f' },
|
||||
}
|
||||
|
||||
function datasetAvailabilityLabel(dataset: DatasetCreateResponse): string {
|
||||
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 `${(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0).toLocaleString('nl-BE')} objecten beschikbaar`
|
||||
}
|
||||
|
||||
function datasetSearchText(dataset: DatasetCreateResponse): string {
|
||||
return [
|
||||
dataset.name,
|
||||
@@ -144,6 +161,8 @@ function pickThemeDataset(
|
||||
(dataset.source_name === 'inbo_bwk_natura2000' ? 95_000 : 0) +
|
||||
(dataset.source_name === 'agentschap_landbouw_zeevisserij_agricultural_parcels' ? 98_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_metadata?.['product_key'] === 'dtm_1m' ? 1_000_000 : 0) +
|
||||
(dataset.dataset_role === 'reference' ? 10_000 : 0) +
|
||||
(dataset.observed_at ? new Date(dataset.observed_at).getTime() / 100_000_000 : 0) +
|
||||
(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0)
|
||||
@@ -200,6 +219,14 @@ function formatObservationDate(value: string | null | undefined): string {
|
||||
return new Intl.DateTimeFormat('nl-BE', { year: 'numeric', month: 'short', day: 'numeric' }).format(new Date(value))
|
||||
}
|
||||
|
||||
function formatDatasetObservation(dataset: DatasetCreateResponse): string {
|
||||
const period = dataset.source_metadata?.['acquisition_period']
|
||||
if (typeof period === 'string' && period.trim()) {
|
||||
return `opnameperiode ${period}`
|
||||
}
|
||||
return formatObservationDate(dataset.observed_at)
|
||||
}
|
||||
|
||||
function operationalScopeProjectLabel(project: ProjectRead): string {
|
||||
if (project.name === MOL_PROJECT_NAME) {
|
||||
return 'Mol'
|
||||
@@ -668,6 +695,18 @@ export function MapWorkspace({
|
||||
}
|
||||
: null
|
||||
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 activeImageOverlay = 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
|
||||
@@ -709,11 +748,14 @@ export function MapWorkspace({
|
||||
const activeSupportingMetrics = (activeSelectionResult?.summary?.metrics ?? []).filter(
|
||||
(metric) => metric.metric_key !== activeSelectionResult?.summary?.primary_metric_key,
|
||||
)
|
||||
const activeSecondaryMetric = selectedAreaSquareMetres && selectedAreaSquareMetres > 0
|
||||
? activeMetricUnit === 'ha'
|
||||
const terrainReliefMetric = activeSupportingMetrics.find((metric) => metric.metric_key === 'relief_m')
|
||||
const activeSecondaryMetric = activeMetricUnit === 'm TAW'
|
||||
? terrainReliefMetric ? `${terrainReliefMetric.metric_value.toLocaleString('nl-BE', { maximumFractionDigits: 2 })} m reliëf` : 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
|
||||
: null
|
||||
const selectedResultProperties = useMemo(() => {
|
||||
const keys = new Map<string, Set<string>>()
|
||||
for (const feature of activeSelectionResult?.geojson.features ?? []) {
|
||||
@@ -1094,7 +1136,7 @@ export function MapWorkspace({
|
||||
? 'Alleen huidige toestand'
|
||||
: 'Bron nog niet ingeladen'
|
||||
: dataset
|
||||
? `${(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0).toLocaleString('nl-BE')} objecten beschikbaar`
|
||||
? datasetAvailabilityLabel(dataset)
|
||||
: 'Bron nog niet ingeladen'}
|
||||
</small>
|
||||
</span>
|
||||
@@ -1125,7 +1167,7 @@ export function MapWorkspace({
|
||||
? `${activeTemporalSeries.length} officiële meetmomenten · ${formatObservationDate(activeTemporalSeries[0].observed_at)} tot ${formatObservationDate(activeTemporalSeries[activeTemporalSeries.length - 1].observed_at)}`
|
||||
: 'Voor dit thema is nog geen tweede officieel meetmoment beschikbaar.'
|
||||
: activeThemeDataset
|
||||
? `${getDatasetSourceDisplayName(activeThemeDataset)} · ${formatObservationDate(activeThemeDataset.observed_at)}`
|
||||
? `${getDatasetSourceDisplayName(activeThemeDataset)} · ${formatDatasetObservation(activeThemeDataset)}`
|
||||
: activeTheme.description}
|
||||
</small>
|
||||
</div>
|
||||
@@ -1225,7 +1267,7 @@ export function MapWorkspace({
|
||||
areaData={areaFeatureCollection}
|
||||
selectedFeature={selectedFeature}
|
||||
selectionData={analysisMode === 'current' ? mapSelectionResult?.geojson ?? null : null}
|
||||
imageOverlay={orthophotoImageOverlay}
|
||||
imageOverlay={activeImageOverlay}
|
||||
selectionBbox={mapSelectionBbox}
|
||||
bboxSelectionMode={bboxSelectionMode}
|
||||
visible={mapLayerVisible}
|
||||
@@ -1241,7 +1283,7 @@ export function MapWorkspace({
|
||||
/>
|
||||
<div className="geo-map-legend" aria-label="Kaartlegende">
|
||||
<span><i className="geo-legend-area" /> Werkgebied</span>
|
||||
{orthophotoImageOverlay ? <span><i className="geo-legend-imagery" /> {orthophotoImageOverlay.label}</span> : null}
|
||||
{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>
|
||||
@@ -1444,7 +1486,7 @@ export function MapWorkspace({
|
||||
<strong>{activeSelectionResult ? resultMetricLabel(activeSelectionResult) : 'Geen resultaat'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{activeMetricUnit === 'ha' ? 'Aandeel selectie' : 'Dichtheid'}</span>
|
||||
<span>{activeMetricUnit === 'ha' ? 'Aandeel selectie' : activeMetricUnit === 'm TAW' ? 'Reliëf' : 'Dichtheid'}</span>
|
||||
<strong>{activeSecondaryMetric ?? (selectedDensity === null ? 'n.v.t.' : `${selectedDensity.toLocaleString('nl-BE', { maximumFractionDigits: 1 })} / km2`)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1517,7 +1559,7 @@ export function MapWorkspace({
|
||||
|
||||
{analysisMode === 'current' ? (
|
||||
<div className="geo-result-actions">
|
||||
<button className="secondary-action" disabled={!activeSelectionResult} type="button" onClick={downloadActiveThemeSelection}>Download GeoJSON</button>
|
||||
<button className="secondary-action" disabled={!activeSelectionResult || activeTheme.id === 'elevation'} type="button" onClick={downloadActiveThemeSelection}>Download GeoJSON</button>
|
||||
<button className="secondary-action" disabled={!activeSelectionResult} type="button" onClick={copyActiveThemeSelection}>Kopieer gegevens</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { datasetsApi } from '../services/api'
|
||||
import { formatError } from '../lib/formatError'
|
||||
import type { DatasetCreateResponse, VectorSelectionBBox, VectorSelectionResponse } from '../types'
|
||||
import { terrainSelectionToMapSelection } from '../lib/terrainSelection'
|
||||
|
||||
interface MapSelectionExtractOptions {
|
||||
selectedProjectId: string | null
|
||||
@@ -38,8 +39,9 @@ export function useMapSelectionExtract({
|
||||
setMapSelectionError('Open a vector dataset before extracting a map area.')
|
||||
return null
|
||||
}
|
||||
if (!isVectorDatasetType(selectedDataset.dataset_type)) {
|
||||
setMapSelectionError('Area extraction requires an active vector dataset.')
|
||||
const terrainDataset = selectedDataset.dataset_type === 'raster' && selectedDataset.source_name === 'digitaal_vlaanderen_dhmv'
|
||||
if (!isVectorDatasetType(selectedDataset.dataset_type) && !terrainDataset) {
|
||||
setMapSelectionError('Gebiedsanalyse ondersteunt een vectorlaag of een beheerd DHMV-hoogtemodel.')
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -49,11 +51,16 @@ export function useMapSelectionExtract({
|
||||
setMapSelectionError(null)
|
||||
setMapSelectionBbox(bbox)
|
||||
try {
|
||||
const response = await datasetsApi.selectVectorFeatures(selectedProjectId, selectedDataset.id, {
|
||||
bbox: { ...bbox, crs: 'EPSG:4326' },
|
||||
area_id: areaId,
|
||||
limit: 1000,
|
||||
})
|
||||
const response = terrainDataset
|
||||
? terrainSelectionToMapSelection(await datasetsApi.selectTerrain(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,
|
||||
limit: 1000,
|
||||
})
|
||||
if (requestSequence.current !== sequence) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { formatError } from '../lib/formatError'
|
||||
import { datasetsApi } from '../services/api/datasets'
|
||||
import type { DatasetCreateResponse, VectorSelectionBBox, VectorSelectionResponse } from '../types'
|
||||
import { terrainSelectionToMapSelection } from '../lib/terrainSelection'
|
||||
|
||||
export interface MapThemeQuery<TThemeId extends string> {
|
||||
themeId: TThemeId
|
||||
@@ -54,11 +55,16 @@ export function useMapThemeSelectionInsights<TThemeId extends string>(
|
||||
queries.map(async ({ themeId, dataset }) => ({
|
||||
themeId,
|
||||
dataset,
|
||||
result: await datasetsApi.selectVectorFeatures(selectedProjectId, dataset.id, {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
limit: 1000,
|
||||
}),
|
||||
result: dataset.dataset_type === 'raster' && dataset.source_name === 'digitaal_vlaanderen_dhmv'
|
||||
? terrainSelectionToMapSelection(await datasetsApi.selectTerrain(selectedProjectId, dataset.id, {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
}))
|
||||
: await datasetsApi.selectVectorFeatures(selectedProjectId, dataset.id, {
|
||||
bbox,
|
||||
area_id: areaId,
|
||||
limit: 1000,
|
||||
}),
|
||||
})),
|
||||
)
|
||||
const successful = settled.flatMap((item) => (item.status === 'fulfilled' ? [item.value] : []))
|
||||
|
||||
@@ -9,6 +9,7 @@ const DATASET_LABEL_BY_LAYER: Record<string, string> = {
|
||||
forest: 'Bos en groen',
|
||||
nature_value: 'Natuurwaarde',
|
||||
agriculture: 'Landbouwgebruikspercelen',
|
||||
elevation: 'Hoogte en reliëf',
|
||||
building_registry: 'Gebouwenregister',
|
||||
regional_boundary: 'Grens vervoerregio Kempen',
|
||||
municipality_boundaries: 'Gemeentegrenzen Kempen',
|
||||
@@ -19,6 +20,7 @@ const DATASET_SOURCE_LABELS: Record<string, string> = {
|
||||
agentschap_landbouw_zeevisserij_agricultural_parcels: 'Agentschap Landbouw en Zeevisserij',
|
||||
digitaal_vlaanderen_orthophoto: 'Digitaal Vlaanderen',
|
||||
digitaal_vlaanderen_buildings_addresses_register: 'Digitaal Vlaanderen',
|
||||
digitaal_vlaanderen_dhmv: 'Digitaal Vlaanderen',
|
||||
grb: 'GRB',
|
||||
historical_landuse: 'Digitaal Vlaanderen',
|
||||
inbo_bwk_natura2000: 'INBO',
|
||||
@@ -33,6 +35,10 @@ export function getDatasetSourceDisplayName(dataset: DatasetCreateResponse): str
|
||||
}
|
||||
|
||||
export function getDatasetDisplayName(dataset: DatasetCreateResponse): string {
|
||||
if (dataset.source_name === 'digitaal_vlaanderen_dhmv') {
|
||||
const productName = dataset.source_metadata?.['product_display_name']
|
||||
return typeof productName === 'string' && productName.trim() ? productName : 'DHMV II hoogtemodel'
|
||||
}
|
||||
const layer = (dataset.reference_layer_name ?? dataset.source_metadata?.layer_name ?? dataset.source_metadata?.layer_type ?? '')
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export function terrainImageUrl(projectId: string, datasetId: string): string {
|
||||
return `/api/v1/projects/${projectId}/datasets/${datasetId}/raster/terrain/image`
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { TerrainSelectionResponse, VectorSelectionResponse } from '../types'
|
||||
|
||||
export function terrainSelectionToMapSelection(result: TerrainSelectionResponse): VectorSelectionResponse {
|
||||
return {
|
||||
selection_bbox: result.selection_bbox,
|
||||
selection_area_id: result.selection_area_id,
|
||||
feature_count: 0,
|
||||
total_feature_count: 0,
|
||||
limit: 0,
|
||||
truncated: false,
|
||||
geojson: { type: 'FeatureCollection', features: [] },
|
||||
summary: {
|
||||
...result.summary,
|
||||
feature_count: result.sample_count,
|
||||
is_estimate: false,
|
||||
warning: result.limitation_message,
|
||||
metrics: result.summary.metrics.map((metric) => ({
|
||||
...metric,
|
||||
is_estimate: false,
|
||||
})),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,9 @@ import type {
|
||||
RasterNdbiRequest,
|
||||
OrthophotoAcquireRequest,
|
||||
OrthophotoProductRead,
|
||||
DhmvAcquireRequest,
|
||||
DhmvProductRead,
|
||||
TerrainSelectionResponse,
|
||||
} from '../../types'
|
||||
|
||||
const DATASET_PAGE_SIZE = 200
|
||||
@@ -115,6 +118,16 @@ export const datasetsApi = {
|
||||
apiGet<{ items: OrthophotoProductRead[]; total: number }>(`/api/v1/projects/${projectId}/datasets/orthophoto/products`),
|
||||
orthophotoImageUrl: (projectId: string, datasetId: string): string =>
|
||||
`/api/v1/projects/${projectId}/datasets/${datasetId}/raster/image`,
|
||||
acquireDhmv: (projectId: string, payload: DhmvAcquireRequest): Promise<JobRead> =>
|
||||
apiPost<JobRead>(`/api/v1/projects/${projectId}/datasets/dhmv/acquire`, payload),
|
||||
listDhmvProducts: (projectId: string): Promise<{ items: DhmvProductRead[]; total: number }> =>
|
||||
apiGet<{ items: DhmvProductRead[]; total: number }>(`/api/v1/projects/${projectId}/datasets/dhmv/products`),
|
||||
selectTerrain: (
|
||||
projectId: string,
|
||||
datasetId: string,
|
||||
payload: { bbox: VectorSelectionRequest['bbox']; area_id?: string },
|
||||
): Promise<TerrainSelectionResponse> =>
|
||||
apiPost<TerrainSelectionResponse>(`/api/v1/projects/${projectId}/datasets/${datasetId}/raster/terrain/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> =>
|
||||
|
||||
@@ -5756,6 +5756,7 @@ section {
|
||||
.geo-theme-symbol-nature_value { background: #9a4f64; }
|
||||
.geo-theme-symbol-agriculture { background: #7b8f32; }
|
||||
.geo-theme-symbol-water { background: #2676a8; }
|
||||
.geo-theme-symbol-elevation { background: #a57a4b; }
|
||||
.geo-theme-symbol-roads { background: #6b7280; }
|
||||
.geo-theme-symbol-parcels { background: #a7792f; }
|
||||
|
||||
@@ -5940,6 +5941,11 @@ section {
|
||||
background: rgba(38, 118, 168, 0.24);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-elevation {
|
||||
border-color: #315f59;
|
||||
background: rgba(165, 122, 75, 0.26);
|
||||
}
|
||||
|
||||
.geo-map-legend .geo-legend-layer-roads {
|
||||
border-color: #4b5563;
|
||||
background: rgba(107, 114, 128, 0.24);
|
||||
|
||||
@@ -336,6 +336,52 @@ export interface OrthophotoAcquisitionResult {
|
||||
limitation_message: string
|
||||
}
|
||||
|
||||
export interface DhmvAcquireRequest {
|
||||
bbox: VectorSelectionBBox
|
||||
area_id?: string | null
|
||||
product_key?: 'dtm_1m' | 'dsm_1m'
|
||||
resolution_m?: number | null
|
||||
force_refresh?: boolean
|
||||
}
|
||||
|
||||
export interface DhmvProductRead {
|
||||
key: 'dtm_1m' | 'dsm_1m'
|
||||
display_name: string
|
||||
surface_model: 'terrain' | 'surface'
|
||||
coverage_id: string
|
||||
native_resolution_m: number
|
||||
source_crs: string
|
||||
vertical_reference: string
|
||||
acquisition_period: string
|
||||
catalog_url: string
|
||||
attribution: string
|
||||
limitation_message: string
|
||||
}
|
||||
|
||||
export interface TerrainSelectionResponse {
|
||||
dataset_id: string
|
||||
product_key: string
|
||||
surface_model: 'terrain' | 'surface'
|
||||
selection_bbox: VectorSelectionBBox
|
||||
selection_area_id?: string | null
|
||||
sample_count: number
|
||||
slope_sample_count: number
|
||||
coverage_ratio: number
|
||||
resolution_m: number
|
||||
vertical_reference: string
|
||||
summary: {
|
||||
metric_label: string
|
||||
metric_value: number
|
||||
metric_unit: string
|
||||
aggregation_method: string
|
||||
primary_metric_key: string
|
||||
metrics: VectorSelectionMetric[]
|
||||
}
|
||||
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