From 9db0ac7811e7b035b6c0a4647a96335692e03425 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 16 Jul 2026 00:01:14 +0200 Subject: [PATCH] Complete regional history live validation --- CHANGELOG.md | 13 ++++++ ...sprint169_long_context_name_readability.py | 3 +- ...t_sprint209_regional_historical_landuse.py | 4 ++ docs/CODEX_EXECUTION_LOG.md | 46 +++++++++++++++---- docs/TODO.md | 2 +- frontend/README.md | 5 ++ frontend/src/App.tsx | 6 ++- frontend/src/components/map/MapWorkspace.tsx | 10 ++++ 8 files changed, 77 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 705c5e3f..b5d44d57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,19 @@ feature limits and one canonical regional Dataset upload per theme/year. - Extended the regional time-series coordinator and release packaging without changing API contracts, database migrations or frontend architecture. +- Provisioned all nine snapshots on live Tower/PostGIS: 207,717 persisted + polygon features across three complete 28-municipality temporal series. + Every Dataset is ready, has one immutable DatasetVersion and was reused by + the idempotency run without duplicate persistence. +- Added the exact-area fast path for these already-clipped regional artifacts, + reducing complete-Kempen comparisons from minutes to 0.9-4.1 seconds in the + live audit while preserving the calculated values. +- Suppressed inapplicable generic line metrics for historical polygon classes; + results now show governed hectares plus object counts, never artificial + `0 km` road/watercourse measurements. +- Kept the top workbench source context synchronized with the selected + Evolution series. Browser verification covered complete Kempen, Mol and a + 3440 x 1440 viewport with no console warnings or errors. ## Sprint 208 Governed VMM flood-hazard scenarios (2026-07-15) diff --git a/backend/tests/test_sprint169_long_context_name_readability.py b/backend/tests/test_sprint169_long_context_name_readability.py index 87d7cf64..87f927fb 100644 --- a/backend/tests/test_sprint169_long_context_name_readability.py +++ b/backend/tests/test_sprint169_long_context_name_readability.py @@ -15,7 +15,8 @@ def test_long_context_names_remain_compact_and_inspectable() -> None: assert "selectedProject?.name === REGIONAL_WORKSPACE_PROJECT_NAME" in app assert "? REGIONAL_WORKSPACE_LABEL" in app - assert "const datasetContextLabel = analysisMapLayerActive && mapFeatureCollection" in app + assert "const datasetContextLabel = activeWorkspace === 'map' && mapContextSourceLabel" in app + assert ": analysisMapLayerActive && mapFeatureCollection" in app assert "? getDatasetDisplayName(selectedDataset)" in app assert "`${mapLayerLabel} · controle vereist`" in app assert "{projectContextLabel}" in app diff --git a/backend/tests/test_sprint209_regional_historical_landuse.py b/backend/tests/test_sprint209_regional_historical_landuse.py index d72caafc..d947d4f9 100644 --- a/backend/tests/test_sprint209_regional_historical_landuse.py +++ b/backend/tests/test_sprint209_regional_historical_landuse.py @@ -239,6 +239,10 @@ def test_upload_contract_is_regional_temporal_and_partition_audited(tmp_path: Pa def test_regional_historical_operator_is_packaged_and_release_checked() -> None: dockerfile = (ROOT / "deploy/unraid/Dockerfile.all-in-one").read_text(encoding="utf-8") readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8") + app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") + workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") assert "COPY scripts/provision_regional_historical_landuse.py" in dockerfile assert "py_compile scripts/provision_regional_historical_landuse.py" in readiness + assert "onSetContextSourceLabel={setMapContextSourceLabel}" in app + assert "analysisMode === 'evolution' ? activeTemporalSeriesGroup?.label" in workspace diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index d1b3eee0..2e12948a 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -8833,12 +8833,39 @@ Implemented: - Extended `provision_regional_timeseries.py`, all-in-one packaging, readiness compilation and operator/source documentation. -Validation evidence before live provisioning: -- Fourteen focused partition, raw-evidence, clipping, assembly, upload, - coordinator and packaging tests pass. -- The official source catalog still reports the production WFS and the three - governed historical collections. Live partition provisioning and browser - Evolution validation follow in this pass. +Validation and live evidence: +- The final release gate passes with 685 backend tests, frontend typecheck and + production build, one Alembic head (`202607150001`) and script syntax checks. +- Live Tower/PostGIS provisioning completed all 252 municipality/theme/year + partitions and nine combined snapshots. No snapshot has an empty municipal + partition. The retained regional historical audit cache is about 518 MB. +- Persisted feature counts are: buildings 14,245 / 22,036 / 69,869; water + 9,010 / 13,571 / 8,188; roads 6,305 / 33,578 / 30,915 for 1778 / 1873 / + 1969 respectively. Total persisted geometry count is 207,717. +- Each of the nine ready Datasets belongs to regional Area + `46ab5614-fcf8-4e63-8ea8-6049f34c14fd`, exposes one immutable + DatasetVersion/checksum and participates in one of three ordered temporal + series. A second complete operator run returned `existing` for all nine with + identical audited feature counts. +- The live audit found that the full-Area fast path did not yet recognize the + new clipped operator. Adding its governed provenance reduced exact complete- + Kempen comparisons from multi-minute redundant clipping to 4.0 s for + buildings, 0.9 s for water and 1.9 s for roads. +- Complete-Kempen 1778 -> 1969 results are 481.62 -> 1,833.23 ha built land, + 1,546.89 -> 2,198.80 ha mapped water and 4,382.49 -> 6,978.12 ha mapped + roads. These are map-class surfaces, not modern footprints, centreline + lengths, bathymetry or volume. +- Browser validation selected the historical buildings series, analyzed the + complete persisted Kempen Area and then municipality Mol. Mol returned + 24.35 / 32.45 / 120.81 ha for 1778 / 1873 / 1969 and explicitly disclosed + the cartographic-method and unstable-object-identity limitations. +- The irrelevant generic `0 km` metrics for polygon-only historical water and + road classes were removed. The workbench source context now follows the + selected Evolution series. Normal and 3440 x 1440 visual checks found no + overlap; browser console warnings/errors were empty. +- Each deployment passed live PostGIS 3.6 connectivity, required schema/index + checks, database collation, Alembic head `202607150001`, frontend/API proxy + and icon verification. Known limitations: - Historical `buildings` are mapped built land-use surfaces, not individual @@ -8849,6 +8876,7 @@ Known limitations: continuous equivalent to modern GRB or 10 m land-use products. Next: -- Run the complete nine-snapshot operator on live Tower/PostGIS, verify all 28 - partition manifests and then validate regional/municipality Evolution in the - browser before marking the backlog item complete. +- Keep permanent inland bathymetry blocked on a governed depth source. The + next implementation pass can focus on current-only nature/terrain/flood + regional expansion or on a benchmarked data-refresh scheduler; it must not + reinterpret these historical surfaces as modern object lineage. diff --git a/docs/TODO.md b/docs/TODO.md index f002d159..f4c110b9 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -26,7 +26,7 @@ - [x] Add bounded historical orthophoto acquisition for official 1971-2025 products with a map overlay and no current-GRB QA on old imagery. - [x] Add BWK/Natura 2000 through an explicit provider/operator contract. - [x] Add annual agricultural-use parcels through an explicit provider/operator contract. -- [ ] Extend the official 1778/1873/1969 historical buildings, water and roads series from Mol to the approved regional scope with partitioned source audits. +- [x] Extend the official 1778/1873/1969 historical buildings, water and roads series from Mol to the approved regional scope with partitioned source audits. - [x] Connect a drawn rectangle to bounded official orthophoto acquisition, local configured-YOLO detection and persisted GRB QA. ## Governed source expansion backlog diff --git a/frontend/README.md b/frontend/README.md index c44bdbdb..99004faf 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -31,6 +31,11 @@ selectable and is never merged into the same trend. Regional building, water and road evolution uses the same selector after the partition-audited 1778/1873/1969 snapshots are provisioned. Historical built surfaces never replace current GRB building footprints in Latest state. +The persistent top context bar follows the selected historical series while +Evolution is active, so its source label cannot disagree with the result panel. +Because these historical WFS classes are polygonal map surfaces, their metrics +are hectares plus a supporting object count; generic live road/water line +metrics are deliberately not shown. Selection results use dataset-specific PostGIS summaries with end-user units. Building footprints, forest, water surfaces and parcels show intersected diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 14d7e479..3ea4b771 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -65,6 +65,7 @@ function App(): JSX.Element { const [activeWorkspace, setActiveWorkspace] = useState('map') const [inspectorOpen, setInspectorOpen] = useState(false) const [mapContentMode, setMapContentMode] = useState<'dataset' | 'analysis'>('dataset') + const [mapContextSourceLabel, setMapContextSourceLabel] = useState(null) useEffect(() => { window.scrollTo({ top: 0, left: 0 }) setInspectorOpen(false) @@ -715,7 +716,9 @@ function App(): JSX.Element { ? REGIONAL_WORKSPACE_LABEL : selectedProject?.name ?? 'Geen werkruimte' const areaContextLabel = selectedArea?.name ?? (areas.length > 0 ? 'Kies een gebied' : 'Geen gebied') - const datasetContextLabel = analysisMapLayerActive && mapFeatureCollection + const datasetContextLabel = activeWorkspace === 'map' && mapContextSourceLabel + ? mapContextSourceLabel + : analysisMapLayerActive && mapFeatureCollection ? `${mapLayerLabel} · controle vereist` : selectedDataset ? getDatasetDisplayName(selectedDataset) @@ -1035,6 +1038,7 @@ function App(): JSX.Element { selectedMapDatasetId={selectedDataset && availableMapDatasets.some((dataset) => dataset.id === selectedDataset.id) ? selectedDataset.id : ''} selectedFeature={selectedMapFeature} onSelectMapArea={setSelectedMapAreaId} + onSetContextSourceLabel={setMapContextSourceLabel} onOpenDatasetInMap={openDatasetInMap} onSetAreaLayerVisible={setAreaLayerVisible} onSetAreaLayerOpacity={setAreaLayerOpacity} diff --git a/frontend/src/components/map/MapWorkspace.tsx b/frontend/src/components/map/MapWorkspace.tsx index 93440b56..a181c14d 100644 --- a/frontend/src/components/map/MapWorkspace.tsx +++ b/frontend/src/components/map/MapWorkspace.tsx @@ -559,6 +559,7 @@ interface MapWorkspaceProps { availableMapDatasets: DatasetCreateResponse[] selectedMapDatasetId: string onSelectMapArea: (areaId: string) => void + onSetContextSourceLabel: (label: string | null) => void onOpenDatasetInMap: (dataset: DatasetCreateResponse) => void onSetAreaLayerVisible: (visible: boolean) => void onSetAreaLayerOpacity: (opacity: number) => void @@ -639,6 +640,7 @@ export function MapWorkspace({ availableMapDatasets, selectedMapDatasetId, onSelectMapArea, + onSetContextSourceLabel, onOpenDatasetInMap, onSetAreaLayerVisible, onSetAreaLayerOpacity, @@ -776,6 +778,14 @@ export function MapWorkspace({ const activeTemporalSeriesGroup = activeTemporalSeriesGroups.find((group) => group.key === selectedTemporalSeriesKey) ?? activeTemporalSeriesGroups[0] const activeTemporalSeries = activeTemporalSeriesGroup?.items ?? EMPTY_TEMPORAL_SERIES + + useEffect(() => { + onSetContextSourceLabel( + analysisMode === 'evolution' ? activeTemporalSeriesGroup?.label ?? null : null, + ) + return () => onSetContextSourceLabel(null) + }, [activeTemporalSeriesGroup?.label, analysisMode, onSetContextSourceLabel]) + const themeResults = useMemo( () => themeInsights.flatMap((insight) => {