Add governed buildings register snapshot
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-15 16:56:08 +02:00
parent 48c0d21fed
commit 553e457b26
16 changed files with 1779 additions and 20 deletions
+8
View File
@@ -484,6 +484,14 @@ Object additions/removals stay hidden because annual parcel identity is not
stable. The Sources inventory only labels the series available after real
Datasets exist.
When the governed Buildings and Addresses Register snapshot is loaded for Mol,
the `Bebouwing` theme automatically prefers that richer Dataset only while the
exact Mol Area is active. It shows register lifecycle, unit/address aggregate
metrics and GRB reconciliation without exposing address labels. Selecting
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.
## Useful repository scripts
- `bash scripts/frontend_install.sh`
@@ -108,6 +108,12 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
)
const bwkDatasets = ready.filter((dataset) => dataset.source_name === 'inbo_bwk_natura2000')
const agricultureDatasets = ready.filter((dataset) => dataset.source_name === 'agentschap_landbouw_zeevisserij_agricultural_parcels')
const buildingsRegisterDatasets = ready.filter(
(dataset) => dataset.source_name === 'digitaal_vlaanderen_buildings_addresses_register',
)
const latestBuildingsRegister = [...buildingsRegisterDatasets].sort(
(left, right) => new Date(right.observed_at ?? 0).getTime() - new Date(left.observed_at ?? 0).getTime(),
)[0]
const agricultureYears = agricultureDatasets
.flatMap((dataset) => dataset.observed_at ? [new Date(dataset.observed_at).getUTCFullYear()] : [])
const pendingSources = AVAILABLE_SOURCES.filter((source) => {
@@ -115,6 +121,7 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
if (source.key === 'historical_orthophoto') return historicalOrthophotos.length === 0
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
return true
})
const themes = Object.keys(THEME_LABELS).map((theme) => {
@@ -174,7 +181,7 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
))}
</div>
{waterinfoDatasets.length > 0 || historicalOrthophotos.length > 0 || bwkDatasets.length > 0 || agricultureDatasets.length > 0 ? (
{waterinfoDatasets.length > 0 || historicalOrthophotos.length > 0 || bwkDatasets.length > 0 || agricultureDatasets.length > 0 || buildingsRegisterDatasets.length > 0 ? (
<div className="source-catalog-loaded" aria-label="Aanvullende ingeladen bronnen">
{waterinfoDatasets.length > 0 ? (
<article>
@@ -204,6 +211,17 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
<p>Oppervlakte en officiële hoofdteeltgroepen zijn historisch vergelijkbaar; perceelidentiteiten blijven bewust niet gekoppeld tussen jaren.</p>
</article>
) : null}
{latestBuildingsRegister ? (
<article>
<strong>Gebouwen- en Adressenregister</strong>
<span>
{(latestBuildingsRegister.feature_count ?? 0).toLocaleString('nl-BE')} gebouwen · {' '}
{Number(latestBuildingsRegister.source_metadata?.['building_unit_count'] ?? 0).toLocaleString('nl-BE')} eenheden · {' '}
{Number(latestBuildingsRegister.source_metadata?.['linked_address_count'] ?? 0).toLocaleString('nl-BE')} gekoppelde adressen
</span>
<p>Registerstatus en geaggregeerde koppelingen voor Mol; adreslabels en persoonsgegevens worden niet in de kaartlaag getoond.</p>
</article>
) : null}
</div>
) : null}
+20 -5
View File
@@ -120,8 +120,22 @@ function datasetMatchesTheme(dataset: DatasetCreateResponse, theme: DataTheme):
return theme.tokens.some((token) => searchText.includes(token))
}
function pickThemeDataset(datasets: DatasetCreateResponse[], theme: DataTheme): DatasetCreateResponse | null {
const candidates = datasets.filter((dataset) => datasetMatchesTheme(dataset, theme))
function datasetCoversSelectedArea(dataset: DatasetCreateResponse, selectedAreaId: string | null): boolean {
const coverageScope = String(dataset.source_metadata?.['coverage_scope'] ?? '')
if (coverageScope !== 'municipality' || !dataset.area_id) {
return true
}
return Boolean(selectedAreaId) && dataset.area_id === selectedAreaId
}
function pickThemeDataset(
datasets: DatasetCreateResponse[],
theme: DataTheme,
selectedAreaId: string | null,
): DatasetCreateResponse | null {
const candidates = datasets.filter(
(dataset) => datasetMatchesTheme(dataset, theme) && datasetCoversSelectedArea(dataset, selectedAreaId),
)
candidates.sort((left, right) => {
const score = (dataset: DatasetCreateResponse) =>
(dataset.reference_layer_name && theme.tokens.includes(dataset.reference_layer_name.toLowerCase()) ? 1_000_000 : 0) +
@@ -129,6 +143,7 @@ function pickThemeDataset(datasets: DatasetCreateResponse[], theme: DataTheme):
(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 === 'digitaal_vlaanderen_buildings_addresses_register' ? 120_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)
@@ -636,9 +651,9 @@ export function MapWorkspace({
const themeDatasetMap = useMemo(
() =>
Object.fromEntries(
DATA_THEMES.map((theme) => [theme.id, pickThemeDataset(availableMapDatasets, theme)]),
DATA_THEMES.map((theme) => [theme.id, pickThemeDataset(availableMapDatasets, theme, selectedMapAreaId)]),
) as Record<DataThemeId, DatasetCreateResponse | null>,
[availableMapDatasets],
[availableMapDatasets, selectedMapAreaId],
)
const activeTheme = DATA_THEMES.find((theme) => theme.id === activeThemeId) ?? DATA_THEMES[0]
const activeThemeMapStyle = DATA_THEME_MAP_STYLES[activeTheme.id]
@@ -740,7 +755,7 @@ export function MapWorkspace({
}, [activeTemporalSeries])
useEffect(() => {
if (advancedMode || !activeThemeDataset || (selectedMapDataset && datasetMatchesTheme(selectedMapDataset, activeTheme))) {
if (advancedMode || !activeThemeDataset || selectedMapDataset?.id === activeThemeDataset.id) {
return
}
onOpenDatasetInMap(activeThemeDataset)
+2
View File
@@ -9,6 +9,7 @@ const DATASET_LABEL_BY_LAYER: Record<string, string> = {
forest: 'Bos en groen',
nature_value: 'Natuurwaarde',
agriculture: 'Landbouwgebruikspercelen',
building_registry: 'Gebouwenregister',
regional_boundary: 'Grens vervoerregio Kempen',
municipality_boundaries: 'Gemeentegrenzen Kempen',
}
@@ -17,6 +18,7 @@ const DATASET_SOURCE_LABELS: Record<string, string> = {
department_omgeving_land_use: 'Departement Omgeving',
agentschap_landbouw_zeevisserij_agricultural_parcels: 'Agentschap Landbouw en Zeevisserij',
digitaal_vlaanderen_orthophoto: 'Digitaal Vlaanderen',
digitaal_vlaanderen_buildings_addresses_register: 'Digitaal Vlaanderen',
grb: 'GRB',
historical_landuse: 'Digitaal Vlaanderen',
inbo_bwk_natura2000: 'INBO',