feat: add source-grounded evolution and Ollama assistant
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import type { DatasetCreateResponse } from '../../types'
|
||||
|
||||
interface SourceCatalogPanelProps {
|
||||
datasets: DatasetCreateResponse[]
|
||||
}
|
||||
|
||||
const THEME_LABELS: Record<string, string> = {
|
||||
buildings: 'Bebouwing',
|
||||
population: 'Bevolking',
|
||||
forest: 'Bos',
|
||||
water: 'Water',
|
||||
roads: 'Wegen en transport',
|
||||
parcels: 'Percelen',
|
||||
}
|
||||
|
||||
const AVAILABLE_SOURCES = [
|
||||
{
|
||||
name: 'Historische orthofoto’s',
|
||||
owner: 'Digitaal Vlaanderen',
|
||||
coverage: '1971 en 1979-1990; aanvullende jaargangen bestaan afzonderlijk',
|
||||
value: 'Visuele evolutie en toekomstige beeldvergelijking',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/orthofotomozaiek-kleinschalig-zomeropnamen',
|
||||
},
|
||||
{
|
||||
name: 'Biologische Waarderingskaart / Natura 2000',
|
||||
owner: 'INBO',
|
||||
coverage: 'Toestand 2025',
|
||||
value: 'Natuurwaarde, biotopen en habitattypen',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/biologische-waarderingskaart-en-natura-2000-habitatkaart-toestand-2025',
|
||||
},
|
||||
{
|
||||
name: 'Landbouwgebruikspercelen',
|
||||
owner: 'Agentschap Landbouw en Zeevisserij',
|
||||
coverage: 'Jaarlijkse bestanden',
|
||||
value: 'Landbouwoppervlakte, teelten en perceelevolutie',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/open-geodata-landbouwgebruikspercelen',
|
||||
},
|
||||
{
|
||||
name: 'Gebouwen- en adressenregister',
|
||||
owner: 'Digitaal Vlaanderen',
|
||||
coverage: 'Continu geactualiseerd',
|
||||
value: 'Gebouwstatus, levensloop en adressen als aanvulling op GRB',
|
||||
url: 'https://www.vlaanderen.be/datavindplaats/catalogus/gebouwen-en-adressenregister',
|
||||
},
|
||||
{
|
||||
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',
|
||||
url: 'https://www.vlaanderen.be/digitaal-vlaanderen/onze-diensten-en-platformen/earth-observation-data-science-eodas/het-digitaal-hoogtemodel/digitaal-hoogtemodel-vlaanderen-ii',
|
||||
},
|
||||
{
|
||||
name: 'Waterinfo en VMM-metingen',
|
||||
owner: 'Vlaamse Milieumaatschappij',
|
||||
coverage: 'Meetpunten en tijdreeksen voor waterstand, debiet en neerslag',
|
||||
value: 'Hydrologische toestand; geen gebiedsdekkend watervolume zonder bodemprofiel',
|
||||
url: 'https://waterinfo.vlaanderen.be/',
|
||||
},
|
||||
]
|
||||
|
||||
function datasetTheme(dataset: DatasetCreateResponse): string | null {
|
||||
const configured = String(dataset.source_metadata?.['theme'] ?? dataset.reference_layer_name ?? '').toLowerCase()
|
||||
if (configured === 'built' || configured === 'building') return 'buildings'
|
||||
if (configured === 'transport' || configured === 'road') return 'roads'
|
||||
if (configured in THEME_LABELS) return configured
|
||||
return null
|
||||
}
|
||||
|
||||
export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.Element {
|
||||
const ready = datasets.filter((dataset) => dataset.status === 'ready')
|
||||
const themes = Object.keys(THEME_LABELS).map((theme) => {
|
||||
const matches = ready.filter((dataset) => datasetTheme(dataset) === theme)
|
||||
const temporal = matches.filter((dataset) => dataset.temporal_series_key && dataset.observed_at)
|
||||
const years = temporal.map((dataset) => new Date(dataset.observed_at as string).getUTCFullYear())
|
||||
const latest = [...matches].sort(
|
||||
(left, right) => new Date(right.observed_at ?? right.imported_at ?? 0).getTime() - new Date(left.observed_at ?? left.imported_at ?? 0).getTime(),
|
||||
)[0]
|
||||
return {
|
||||
theme,
|
||||
label: THEME_LABELS[theme],
|
||||
datasetCount: matches.length,
|
||||
temporalCount: temporal.length,
|
||||
firstYear: years.length ? Math.min(...years) : null,
|
||||
lastYear: years.length ? Math.max(...years) : null,
|
||||
source: latest?.source_name ?? latest?.source ?? null,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<section className="workspace-panel source-catalog-panel" aria-label="Beschikbare databronnen">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<span className="section-kicker">Broninventaris</span>
|
||||
<h2>Wat is werkelijk beschikbaar?</h2>
|
||||
<p className="muted">Ingeladen bronnen staan direct klaar voor de kaart. Andere officiële bronnen worden pas gebruikt na een gecontroleerde import.</p>
|
||||
</div>
|
||||
<span className="status-badge status-badge-ready">{ready.length} datasets klaar</span>
|
||||
</div>
|
||||
|
||||
<div className="source-catalog-grid">
|
||||
{themes.map((theme) => (
|
||||
<article className="source-catalog-card" key={theme.theme}>
|
||||
<div>
|
||||
<strong>{theme.label}</strong>
|
||||
<span>{theme.source ? theme.source.replaceAll('_', ' ') : 'Nog niet ingeladen'}</span>
|
||||
</div>
|
||||
<b>{theme.datasetCount > 0 ? 'Beschikbaar' : 'Ontbreekt'}</b>
|
||||
<p>
|
||||
{theme.temporalCount >= 2 && theme.firstYear && theme.lastYear
|
||||
? `${theme.temporalCount} officiële meetmomenten · ${theme.firstYear}-${theme.lastYear}`
|
||||
: 'Alleen de huidige toestand is vergelijkbaar beschikbaar.'}
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<details className="source-opportunity-list">
|
||||
<summary>Officiële bronnen die hierna kunnen worden ingeladen</summary>
|
||||
<div>
|
||||
{AVAILABLE_SOURCES.map((source) => (
|
||||
<article key={source.name}>
|
||||
<div>
|
||||
<strong>{source.name}</strong>
|
||||
<span>{source.owner} · {source.coverage}</span>
|
||||
</div>
|
||||
<p>{source.value}</p>
|
||||
<a href={source.url} target="_blank" rel="noreferrer">Bekijk officiële bron</a>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user