Prefer persisted sources in map federation
This commit is contained in:
@@ -34,6 +34,10 @@
|
|||||||
reported as an NGI administrative boundary layer.
|
reported as an NGI administrative boundary layer.
|
||||||
- Made bounded API coverage spatially honest: a persisted provider selection
|
- Made bounded API coverage spatially honest: a persisted provider selection
|
||||||
is operational only for selections contained by its retained source bbox.
|
is operational only for selections contained by its retained source bbox.
|
||||||
|
- Kept reusable persisted datasets ahead of on-demand catalog placeholders in
|
||||||
|
the workbench, so national Statbel 2025 population data is no longer
|
||||||
|
mislabeled as a 2019 thematic raster. Selection-bounded SPW/UrbIS results
|
||||||
|
remain available through the exact on-demand acquisition path.
|
||||||
|
|
||||||
## Autonomous Belgium and North Sea RC program (2026-07-17)
|
## Autonomous Belgium and North Sea RC program (2026-07-17)
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ def test_regional_on_demand_sources_require_a_bounded_drawn_selection() -> None:
|
|||||||
assert "regionalOnDemandThemeActive" in workspace
|
assert "regionalOnDemandThemeActive" in workspace
|
||||||
assert "regionalRasterThemeActive || regionalOnDemandThemeActive" in workspace
|
assert "regionalRasterThemeActive || regionalOnDemandThemeActive" in workspace
|
||||||
assert "Teken een begrensde rechthoek voor deze regionale analyse." in workspace
|
assert "Teken een begrensde rechthoek voor deze regionale analyse." in workspace
|
||||||
assert "Vlaamse kaartbronnen worden begrensd opgehaald, bewaard en hergebruikt" in workspace
|
assert "regionale kaartbronnen worden begrensd opgehaald, bewaard en hergebruikt" in workspace
|
||||||
|
|
||||||
|
|
||||||
def test_frontend_does_not_contact_external_map_services_directly() -> None:
|
def test_frontend_does_not_contact_external_map_services_directly() -> None:
|
||||||
|
|||||||
@@ -31,6 +31,19 @@
|
|||||||
- Bounded provider materialization now also checks its persisted
|
- Bounded provider materialization now also checks its persisted
|
||||||
`bbox_epsg4326`; one small PICC/UrbIS/GRB/raster acquisition can no longer
|
`bbox_epsg4326`; one small PICC/UrbIS/GRB/raster acquisition can no longer
|
||||||
claim that an entire region is locally loaded.
|
claim that an entire region is locally loaded.
|
||||||
|
- The live governed Statbel apply materialized the reviewed 2025 `NEW` edition
|
||||||
|
as Dataset `ee0a46e5-6139-4b0a-93b5-9dabb3b3dfc7`: 20,781 statistical
|
||||||
|
sectors, 565 municipalities, four repaired geometries and a reconciled
|
||||||
|
national total of 11,825,551 inhabitants (11,817,897 spatially located plus
|
||||||
|
7,654 explicitly unlocated).
|
||||||
|
- Live bounded PICC and UrbIS persistence proved the same service path:
|
||||||
|
54 Walloon building geometries retained in Dataset
|
||||||
|
`0c3c0a11-c00e-4c84-87be-8ffa64302dec` and 47 Brussels building geometries
|
||||||
|
in Dataset `de53ccc0-a494-4c4e-bd0d-16d807bfefe7`.
|
||||||
|
- Corrected the map catalog precedence so reusable persisted datasets win over
|
||||||
|
on-demand placeholders. National Statbel 2025 population is now presented
|
||||||
|
as the active source, while selection-bounded PICC/UrbIS acquisitions remain
|
||||||
|
available only through an exact bounded request.
|
||||||
- Focused backend and frontend suites passed before full release validation;
|
- Focused backend and frontend suites passed before full release validation;
|
||||||
full local and Tower evidence follows in the final P5 gate.
|
full local and Tower evidence follows in the final P5 gate.
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ applicable live bounded journey fails.
|
|||||||
|
|
||||||
## P0 - Existing national layers become usable
|
## P0 - Existing national layers become usable
|
||||||
|
|
||||||
**State: implemented locally; live release evidence pending under P5.**
|
**State: complete; live national and maritime source journeys proven.**
|
||||||
|
|
||||||
### Work
|
### Work
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ applicable live bounded journey fails.
|
|||||||
|
|
||||||
## P1 - National Statbel population
|
## P1 - National Statbel population
|
||||||
|
|
||||||
**State: implementation complete; reviewed live national materialization pending.**
|
**State: complete; reviewed 2025 national edition materialized live.**
|
||||||
|
|
||||||
### Work
|
### Work
|
||||||
|
|
||||||
@@ -120,9 +120,17 @@ applicable live bounded journey fails.
|
|||||||
from the same authoritative Statbel edition.
|
from the same authoritative Statbel edition.
|
||||||
- Mol remains within its documented baseline tolerance.
|
- Mol remains within its documented baseline tolerance.
|
||||||
|
|
||||||
|
### Live evidence
|
||||||
|
|
||||||
|
- Reviewed dataset: `ee0a46e5-6139-4b0a-93b5-9dabb3b3dfc7`.
|
||||||
|
- 20,781 persisted sectors and all 565 municipalities are represented.
|
||||||
|
- National reconciliation: 11,825,551 inhabitants, including 7,654 explicitly
|
||||||
|
retained as unlocated rather than silently assigned to geometry.
|
||||||
|
- A bounded Mol selection returns an explicitly labelled area-weighted estimate.
|
||||||
|
|
||||||
## P2 - Wallonia bounded topographic baseline
|
## P2 - Wallonia bounded topographic baseline
|
||||||
|
|
||||||
**State: implemented locally; live Wallonia journey pending under P5.**
|
**State: complete; bounded Wallonia acquisition and analysis proven live.**
|
||||||
|
|
||||||
### Governed source
|
### Governed source
|
||||||
|
|
||||||
@@ -158,9 +166,16 @@ Initial allowlisted layers:
|
|||||||
- Provider failures and record-limit truncation fail closed.
|
- Provider failures and record-limit truncation fail closed.
|
||||||
- No direct provider-to-`vector_features` write exists.
|
- No direct provider-to-`vector_features` write exists.
|
||||||
|
|
||||||
|
### Live evidence
|
||||||
|
|
||||||
|
- Dataset `0c3c0a11-c00e-4c84-87be-8ffa64302dec` retained 54 valid building
|
||||||
|
geometries for the bounded Wallonia smoke selection.
|
||||||
|
- The persisted selection reported 54 buildings and 0.525363 ha without
|
||||||
|
extending operational coverage beyond the retained bbox.
|
||||||
|
|
||||||
## P3 - Brussels bounded UrbIS baseline
|
## P3 - Brussels bounded UrbIS baseline
|
||||||
|
|
||||||
**State: implemented locally; live Brussels journey pending under P5.**
|
**State: complete; bounded Brussels acquisition and analysis proven live.**
|
||||||
|
|
||||||
### Governed source
|
### Governed source
|
||||||
|
|
||||||
@@ -183,6 +198,13 @@ capabilities probe before implementation is marked operational.
|
|||||||
parcels.
|
parcels.
|
||||||
- Catalogue-only or failed capabilities evidence remains `not_configured`.
|
- Catalogue-only or failed capabilities evidence remains `not_configured`.
|
||||||
|
|
||||||
|
### Live evidence
|
||||||
|
|
||||||
|
- Dataset `de53ccc0-a494-4c4e-bd0d-16d807bfefe7` retained 47 UrbIS building
|
||||||
|
geometries for the bounded Brussels smoke selection.
|
||||||
|
- The persisted selection reported 47 buildings and 0.528894 ha without
|
||||||
|
claiming coverage outside the retained bbox.
|
||||||
|
|
||||||
## P4 - Maritime and bathymetry hardening
|
## P4 - Maritime and bathymetry hardening
|
||||||
|
|
||||||
**State: complete with MDK acquisition visibly blocked by strict-TLS evidence.**
|
**State: complete with MDK acquisition visibly blocked by strict-TLS evidence.**
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ maritieme databronnen bruikbaar in de volgende versie.
|
|||||||
|
|
||||||
- [x] P0: bestaande NGI/RBINS-lagen als kaartthema en selectieanalyse ontsluiten.
|
- [x] P0: bestaande NGI/RBINS-lagen als kaartthema en selectieanalyse ontsluiten.
|
||||||
- [x] P0: readiness baseren op bruikbare analysethema's, niet alleen datasetaantal.
|
- [x] P0: readiness baseren op bruikbare analysethema's, niet alleen datasetaantal.
|
||||||
- [ ] P1: nationale Statbel-populatieflow veilig operationaliseren.
|
- [x] P1: nationale Statbel-populatieflow veilig operationaliseren.
|
||||||
- [x] P2: bounded SPW PICC voor Waalse gebouwen, wegen en hydrographie.
|
- [x] P2: bounded SPW PICC voor Waalse gebouwen, wegen en hydrographie.
|
||||||
- [x] P3: bounded UrbIS voor Brusselse gebouwen en percelen.
|
- [x] P3: bounded UrbIS voor Brusselse gebouwen en percelen.
|
||||||
- [x] P4: maritieme thema's harden en bathymetrie fail-closed onderzoeken.
|
- [x] P4: maritieme thema's harden en bathymetrie fail-closed onderzoeken.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
getFeatureBBox,
|
getFeatureBBox,
|
||||||
getFeatureCollectionBBox,
|
getFeatureCollectionBBox,
|
||||||
getFeatureGeometrySummary,
|
getFeatureGeometrySummary,
|
||||||
|
isSelectionBoundedDataset,
|
||||||
normalizeBboxFromCorners,
|
normalizeBboxFromCorners,
|
||||||
operationalScopeProjectLabel,
|
operationalScopeProjectLabel,
|
||||||
parseBboxInput,
|
parseBboxInput,
|
||||||
@@ -411,6 +412,9 @@ function datasetCoversSelectedArea(
|
|||||||
selectedAreaName: string | null | undefined,
|
selectedAreaName: string | null | undefined,
|
||||||
regionalScope = false,
|
regionalScope = false,
|
||||||
): boolean {
|
): boolean {
|
||||||
|
if (isSelectionBoundedDataset(dataset.source_metadata)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
const selectedZones = selectedAreaCoverageZones(selectedAreaName)
|
const selectedZones = selectedAreaCoverageZones(selectedAreaName)
|
||||||
const configuredZones = dataset.source_metadata?.['coverage_zones']
|
const configuredZones = dataset.source_metadata?.['coverage_zones']
|
||||||
if (selectedZones && Array.isArray(configuredZones) && configuredZones.length > 0) {
|
if (selectedZones && Array.isArray(configuredZones) && configuredZones.length > 0) {
|
||||||
@@ -929,21 +933,27 @@ export function MapWorkspace({
|
|||||||
}
|
}
|
||||||
if (flandersScopeSelected && officialMapProducts.thematic.length > 0) {
|
if (flandersScopeSelected && officialMapProducts.thematic.length > 0) {
|
||||||
for (const product of officialMapProducts.thematic) {
|
for (const product of officialMapProducts.thematic) {
|
||||||
|
if (!result[product.theme]) {
|
||||||
result[product.theme] = null
|
result[product.theme] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (flandersScopeSelected && officialMapProducts.grb.length > 0) {
|
if (flandersScopeSelected && officialMapProducts.grb.length > 0) {
|
||||||
for (const product of officialMapProducts.grb) {
|
for (const product of officialMapProducts.grb) {
|
||||||
|
if (!result[product.key]) {
|
||||||
result[product.key] = null
|
result[product.key] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (officialMapProducts.officialVector.length > 0) {
|
if (officialMapProducts.officialVector.length > 0) {
|
||||||
for (const product of officialMapProducts.officialVector.filter((item) =>
|
for (const product of officialMapProducts.officialVector.filter((item) =>
|
||||||
productCoversZones(item.coverage_zones, selectedCoverageZones),
|
productCoversZones(item.coverage_zones, selectedCoverageZones),
|
||||||
)) {
|
)) {
|
||||||
|
if (!result[product.theme]) {
|
||||||
result[product.theme] = null
|
result[product.theme] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}, [
|
}, [
|
||||||
availableMapDatasets,
|
availableMapDatasets,
|
||||||
@@ -1077,7 +1087,9 @@ export function MapWorkspace({
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}, [onDemandProductsForZones, selectedCoverageZones])
|
}, [onDemandProductsForZones, selectedCoverageZones])
|
||||||
const activeOnDemandMapProduct = onDemandProductMap.get(activeTheme.id) ?? null
|
const activeOnDemandMapProduct = themeDatasetMap[activeTheme.id]
|
||||||
|
? null
|
||||||
|
: onDemandProductMap.get(activeTheme.id) ?? null
|
||||||
const activeThemeMapStyle = DATA_THEME_MAP_STYLES[activeTheme.id]
|
const activeThemeMapStyle = DATA_THEME_MAP_STYLES[activeTheme.id]
|
||||||
const analysisOverlayActive = mapContentMode === 'analysis' && analysisLayerAvailable && Boolean(mapFeatureCollection)
|
const analysisOverlayActive = mapContentMode === 'analysis' && analysisLayerAvailable && Boolean(mapFeatureCollection)
|
||||||
const selectedOrthophotoProduct = orthophotoProducts.find((item) => item.key === selectedOrthophotoProductKey) ?? null
|
const selectedOrthophotoProduct = orthophotoProducts.find((item) => item.key === selectedOrthophotoProductKey) ?? null
|
||||||
@@ -1665,6 +1677,16 @@ export function MapWorkspace({
|
|||||||
: []
|
: []
|
||||||
const availableThemes: Array<MapThemeQuery<DataThemeId>> = []
|
const availableThemes: Array<MapThemeQuery<DataThemeId>> = []
|
||||||
for (const theme of DATA_THEMES) {
|
for (const theme of DATA_THEMES) {
|
||||||
|
const dataset = themeDatasetMap[theme.id]
|
||||||
|
if (dataset) {
|
||||||
|
availableThemes.push({
|
||||||
|
themeId: theme.id,
|
||||||
|
dataset,
|
||||||
|
partitioned: regionalScopeSelected
|
||||||
|
&& (isPartitionedRaster(dataset) || isPartitionedBathymetry(dataset)),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
const onDemandProducts = resolvedProducts.filter((product) => product.theme === theme.id)
|
const onDemandProducts = resolvedProducts.filter((product) => product.theme === theme.id)
|
||||||
if (onDemandProducts.length > 0) {
|
if (onDemandProducts.length > 0) {
|
||||||
for (const onDemandProduct of onDemandProducts) {
|
for (const onDemandProduct of onDemandProducts) {
|
||||||
@@ -1679,15 +1701,6 @@ export function MapWorkspace({
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const dataset = themeDatasetMap[theme.id]
|
|
||||||
if (dataset) {
|
|
||||||
availableThemes.push({
|
|
||||||
themeId: theme.id,
|
|
||||||
dataset,
|
|
||||||
partitioned: regionalScopeSelected
|
|
||||||
&& (isPartitionedRaster(dataset) || isPartitionedBathymetry(dataset)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await loadThemeInsights(bbox, availableThemes, areaId)
|
await loadThemeInsights(bbox, availableThemes, areaId)
|
||||||
}
|
}
|
||||||
@@ -1992,7 +2005,7 @@ export function MapWorkspace({
|
|||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
{officialMapProductsLoading && selectedProjectId ? (
|
{officialMapProductsLoading && selectedProjectId ? (
|
||||||
<p className="geo-data-notice">Beschikbare Vlaamse kaartbronnen worden gecontroleerd…</p>
|
<p className="geo-data-notice">Beschikbare officiële regionale kaartbronnen worden gecontroleerd…</p>
|
||||||
) : null}
|
) : null}
|
||||||
{officialMapProductsError && selectedProjectId ? (
|
{officialMapProductsError && selectedProjectId ? (
|
||||||
<p className="error">{officialMapProductsError}</p>
|
<p className="error">{officialMapProductsError}</p>
|
||||||
@@ -2154,7 +2167,7 @@ export function MapWorkspace({
|
|||||||
: regionalRasterThemeActive
|
: regionalRasterThemeActive
|
||||||
? 'Teken een rechthoek; de juiste gemeentelijke rasters worden automatisch gecombineerd.'
|
? 'Teken een rechthoek; de juiste gemeentelijke rasters worden automatisch gecombineerd.'
|
||||||
: onDemandThemeActive
|
: onDemandThemeActive
|
||||||
? 'Teken een rechthoek; officiële Vlaamse kaartbronnen worden begrensd opgehaald, bewaard en hergebruikt.'
|
? 'Teken een rechthoek; officiële regionale kaartbronnen worden begrensd opgehaald, bewaard en hergebruikt.'
|
||||||
: regionalBathymetryThemeActive
|
: regionalBathymetryThemeActive
|
||||||
? 'Teken een rechthoek of analyseer Vlaanderen; alleen overlappende VHA-partities worden samengevoegd.'
|
? 'Teken een rechthoek of analyseer Vlaanderen; alleen overlappende VHA-partities worden samengevoegd.'
|
||||||
: 'Sleep een rechthoek of analyseer het volledige werkgebied.'}
|
: 'Sleep een rechthoek of analyseer het volledige werkgebied.'}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import {
|
import {
|
||||||
bboxesEqual,
|
bboxesEqual,
|
||||||
|
isSelectionBoundedDataset,
|
||||||
normalizeBboxFromCorners,
|
normalizeBboxFromCorners,
|
||||||
parseBboxInput,
|
parseBboxInput,
|
||||||
productCoversZones,
|
productCoversZones,
|
||||||
@@ -63,4 +64,18 @@ describe('map workspace selection guards', () => {
|
|||||||
expect(productCoversZones(['wallonia'], ['flanders', 'wallonia'])).toBe(true)
|
expect(productCoversZones(['wallonia'], ['flanders', 'wallonia'])).toBe(true)
|
||||||
expect(productCoversZones(['brussels'], ['wallonia'])).toBe(false)
|
expect(productCoversZones(['brussels'], ['wallonia'])).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('distinguishes selection-bounded acquisitions from reusable regional datasets', () => {
|
||||||
|
expect(isSelectionBoundedDataset({
|
||||||
|
geometry_clipped_to_selection: true,
|
||||||
|
bbox_epsg4326: [4.55, 50.58, 4.56, 50.59],
|
||||||
|
})).toBe(true)
|
||||||
|
expect(isSelectionBoundedDataset({
|
||||||
|
geometry_clipped_to_selection: false,
|
||||||
|
bbox_epsg4326: [2.5, 49.5, 6.4, 51.6],
|
||||||
|
})).toBe(false)
|
||||||
|
expect(isSelectionBoundedDataset({
|
||||||
|
geometry_clipped_to_selection: true,
|
||||||
|
})).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ export function productCoversZones(productZones: string[], selectedZones: string
|
|||||||
return selectedZones === null || selectedZones.some((zone) => productZones.includes(zone))
|
return selectedZones === null || selectedZones.some((zone) => productZones.includes(zone))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isSelectionBoundedDataset(
|
||||||
|
sourceMetadata: Record<string, unknown> | null | undefined,
|
||||||
|
): boolean {
|
||||||
|
return sourceMetadata?.['geometry_clipped_to_selection'] === true
|
||||||
|
&& Array.isArray(sourceMetadata?.['bbox_epsg4326'])
|
||||||
|
&& sourceMetadata['bbox_epsg4326'].length === 4
|
||||||
|
}
|
||||||
|
|
||||||
export function operationalScopeProjectLabel(project: ProjectRead): string {
|
export function operationalScopeProjectLabel(project: ProjectRead): string {
|
||||||
if (project.name === MOL_PROJECT_NAME) {
|
if (project.name === MOL_PROJECT_NAME) {
|
||||||
return 'Mol'
|
return 'Mol'
|
||||||
|
|||||||
Reference in New Issue
Block a user