fix: make regional time series discoverable
This commit is contained in:
+5
-1
@@ -6,7 +6,7 @@ The persisted `Kempen Regional Workbench` is the automatic operational data cont
|
||||
|
||||
The user-facing shell is task based: `Kaart`, `Bronnen`, `Kwaliteit`, `Beeldanalyse`, `Downloads`, `Status` and `Beheer`. Internal benchmark projects, raw dataset metadata, provider capabilities, model registry details and QA evidence remain accessible through labelled advanced disclosures instead of competing with the normal workflow.
|
||||
|
||||
Detection defaults to the configured local YOLO asset and automatically selects an available raster and active model asset where possible. The model registry and preflight remain honest when PyTorch, Ultralytics, a local model file or a tile manifest is unavailable. The active building profile is operational but remains review-required: its recorded coverage-aware benchmark is approximately precision 0.590, recall 0.577 and F1 0.582 over seven positive AOIs, with zero detections in the empty-background control. Another training pass is intentionally blocked until the generated false-positive and false-negative review decisions are completed.
|
||||
Detection defaults to the configured local YOLO asset and automatically selects an available raster and active model asset where possible. The model registry and preflight remain honest when PyTorch, Ultralytics, a local model file or a tile manifest is unavailable. The active building profile is operational but remains review-required: its current coverage-aligned benchmark is approximately precision 0.614, recall 0.606 and F1 0.607 over seven positive AOIs, with zero detections in all three pure-empty controls. A reviewed challenger remains inactive because it produced two detections in empty Postel forest.
|
||||
|
||||
Map-driven building analysis uses the documented footprint-IoU `0.25` and
|
||||
distinguishes model candidates from verified buildings. It shows persisted
|
||||
@@ -20,6 +20,10 @@ current, while `Evolution` lets the operator compare an earlier and later
|
||||
snapshot from the same series over a drawn rectangle. Results show units,
|
||||
absolute/percentage change, estimate status and source limitations.
|
||||
Added/removed/modified overlays only appear for stable source identities.
|
||||
When Evolution is opened from a current-only theme, the explorer automatically
|
||||
switches to the first available persisted series. Theme cards then show either
|
||||
the number and range of historical observations or `Alleen huidige toestand`,
|
||||
so a single GRB snapshot can no longer strand the workflow in an empty state.
|
||||
When a theme has multiple valid methodologies, a compact series selector keeps
|
||||
them explicit. Forest therefore defaults to the official modern 2013-2025
|
||||
10 m series, while the separate 1778-1969 historical map series remains
|
||||
|
||||
@@ -613,10 +613,15 @@ export function MapWorkspace({
|
||||
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
|
||||
const activeTemporalSeriesGroups = useMemo(
|
||||
() => listThemeTemporalSeries(availableMapDatasets, activeTheme),
|
||||
[activeTheme, availableMapDatasets],
|
||||
const themeTemporalSeriesMap = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
DATA_THEMES.map((theme) => [theme.id, listThemeTemporalSeries(availableMapDatasets, theme)]),
|
||||
) as Record<DataThemeId, TemporalSeriesGroup[]>,
|
||||
[availableMapDatasets],
|
||||
)
|
||||
const activeTemporalSeriesGroups = themeTemporalSeriesMap[activeTheme.id]
|
||||
const availableEvolutionThemes = DATA_THEMES.filter((theme) => themeTemporalSeriesMap[theme.id].length > 0)
|
||||
const activeTemporalSeriesGroup = activeTemporalSeriesGroups.find((group) => group.key === selectedTemporalSeriesKey)
|
||||
?? activeTemporalSeriesGroups[0]
|
||||
const activeTemporalSeries = activeTemporalSeriesGroup?.items ?? EMPTY_TEMPORAL_SERIES
|
||||
@@ -808,7 +813,10 @@ export function MapWorkspace({
|
||||
}
|
||||
|
||||
const selectDataTheme = (theme: DataTheme) => {
|
||||
const dataset = themeDatasetMap[theme.id]
|
||||
const temporalGroup = themeTemporalSeriesMap[theme.id][0]
|
||||
const dataset = analysisMode === 'evolution'
|
||||
? temporalGroup?.items[temporalGroup.items.length - 1] ?? null
|
||||
: themeDatasetMap[theme.id]
|
||||
if (!dataset) {
|
||||
return
|
||||
}
|
||||
@@ -820,6 +828,16 @@ export function MapWorkspace({
|
||||
const setExplorerMode = (mode: 'current' | 'evolution') => {
|
||||
setAnalysisMode(mode)
|
||||
clearTemporalComparison()
|
||||
if (mode !== 'evolution' || activeTemporalSeriesGroups.length > 0) {
|
||||
return
|
||||
}
|
||||
const fallbackTheme = availableEvolutionThemes[0]
|
||||
const fallbackGroup = fallbackTheme ? themeTemporalSeriesMap[fallbackTheme.id][0] : null
|
||||
const fallbackDataset = fallbackGroup?.items[fallbackGroup.items.length - 1]
|
||||
if (fallbackTheme && fallbackDataset) {
|
||||
setActiveThemeId(fallbackTheme.id)
|
||||
onOpenDatasetInMap(fallbackDataset)
|
||||
}
|
||||
}
|
||||
|
||||
const loadAllThemeResults = async (bbox: VectorSelectionBBox, areaId?: string) => {
|
||||
@@ -985,11 +1003,19 @@ export function MapWorkspace({
|
||||
<div className="geo-theme-list">
|
||||
{DATA_THEMES.map((theme) => {
|
||||
const dataset = themeDatasetMap[theme.id]
|
||||
const temporalGroups = themeTemporalSeriesMap[theme.id]
|
||||
const temporalGroup = temporalGroups[0]
|
||||
const evolutionAvailable = temporalGroups.length > 0
|
||||
const available = Boolean(dataset) && (analysisMode === 'current' || evolutionAvailable)
|
||||
const active = activeThemeId === theme.id
|
||||
const firstObservation = temporalGroup?.items[0]?.observed_at
|
||||
const lastObservation = temporalGroup?.items[temporalGroup.items.length - 1]?.observed_at
|
||||
const firstYear = firstObservation ? new Date(firstObservation).getUTCFullYear() : null
|
||||
const lastYear = lastObservation ? new Date(lastObservation).getUTCFullYear() : null
|
||||
return (
|
||||
<button
|
||||
className={active ? 'geo-theme-option geo-theme-option-active' : 'geo-theme-option'}
|
||||
disabled={!dataset}
|
||||
disabled={!available}
|
||||
key={theme.id}
|
||||
type="button"
|
||||
onClick={() => selectDataTheme(theme)}
|
||||
@@ -998,9 +1024,23 @@ export function MapWorkspace({
|
||||
<span className={`geo-theme-symbol geo-theme-symbol-${theme.id}`} aria-hidden="true" />
|
||||
<span>
|
||||
<strong>{theme.label}</strong>
|
||||
<small>{dataset ? `${(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0).toLocaleString('nl-BE')} objecten beschikbaar` : 'Bron nog niet ingeladen'}</small>
|
||||
<small>
|
||||
{analysisMode === 'evolution'
|
||||
? evolutionAvailable
|
||||
? `${temporalGroup.items.length} meetmomenten${firstYear && lastYear ? ` · ${firstYear}-${lastYear}` : ''}`
|
||||
: dataset
|
||||
? 'Alleen huidige toestand'
|
||||
: 'Bron nog niet ingeladen'
|
||||
: dataset
|
||||
? `${(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0).toLocaleString('nl-BE')} objecten beschikbaar`
|
||||
: 'Bron nog niet ingeladen'}
|
||||
</small>
|
||||
</span>
|
||||
<i>{dataset ? 'Beschikbaar' : 'Ontbreekt'}</i>
|
||||
<i>
|
||||
{analysisMode === 'evolution'
|
||||
? evolutionAvailable ? 'Tijdreeks' : dataset ? 'Alleen huidig' : 'Ontbreekt'
|
||||
: dataset ? 'Beschikbaar' : 'Ontbreekt'}
|
||||
</i>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
@@ -1012,7 +1052,7 @@ export function MapWorkspace({
|
||||
{analysisOverlayActive
|
||||
? mapLayerLabel
|
||||
: analysisMode === 'evolution'
|
||||
? activeTemporalSeriesGroup?.label ?? 'Geen tijdreeks beschikbaar'
|
||||
? activeTemporalSeriesGroup?.label ?? 'Nog geen historische reeks ingeladen'
|
||||
: activeThemeDataset ? getDatasetDisplayName(activeThemeDataset) : 'Geen databron beschikbaar'}
|
||||
</strong>
|
||||
<small>
|
||||
@@ -1021,7 +1061,7 @@ export function MapWorkspace({
|
||||
: analysisMode === 'evolution'
|
||||
? activeTemporalSeries.length >= 2
|
||||
? `${activeTemporalSeries.length} officiële meetmomenten · ${formatObservationDate(activeTemporalSeries[0].observed_at)} tot ${formatObservationDate(activeTemporalSeries[activeTemporalSeries.length - 1].observed_at)}`
|
||||
: 'Minstens twee expliciet gedateerde snapshots zijn vereist.'
|
||||
: 'Voor dit thema is nog geen tweede officieel meetmoment beschikbaar.'
|
||||
: activeThemeDataset
|
||||
? `${getDatasetSourceDisplayName(activeThemeDataset)} · ${formatObservationDate(activeThemeDataset.observed_at)}`
|
||||
: activeTheme.description}
|
||||
|
||||
Reference in New Issue
Block a user