feat: add semantic GIS selection metrics
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 06:07:34 +02:00
parent 3ff2d07f3c
commit 0baa9b069c
16 changed files with 520 additions and 22 deletions
+23 -1
View File
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react'
import GeoMap from '../GeoMap'
import type { AreaRead, DatasetCreateResponse, DetectionQaResult, MapViewportState, ProjectRead, QaComparisonResult, VectorSelectionBBox, VectorSelectionResponse } from '../../types'
import type { AreaRead, DatasetCreateResponse, DetectionQaResult, MapViewportState, ProjectRead, QaComparisonResult, VectorSelectionBBox, VectorSelectionMetric, VectorSelectionResponse } from '../../types'
import { featureCollectionBounds } from '../../lib/geojsonBounds'
import { useMapThemeSelectionInsights } from '../../hooks/useMapThemeSelectionInsights'
import { useTemporalComparison } from '../../hooks/useTemporalComparison'
@@ -222,6 +222,11 @@ function resultMetricLabel(result: VectorSelectionResponse): string {
return `${result.summary.metric_value.toLocaleString('nl-BE', { maximumFractionDigits })} ${result.summary.metric_unit}`
}
function selectionMetricLabel(metric: VectorSelectionMetric): string {
const maximumFractionDigits = metric.metric_unit === 'inwoners' || metric.metric_unit === 'objecten' ? 0 : 2
return `${metric.metric_value.toLocaleString('nl-BE', { maximumFractionDigits })} ${metric.metric_unit}`
}
function formatTemporalMetric(value: number, unit: string): string {
const maximumFractionDigits = unit === 'inwoners' || unit === 'objecten' ? 0 : 2
return `${value.toLocaleString('nl-BE', { maximumFractionDigits })} ${unit}`
@@ -648,6 +653,9 @@ export function MapWorkspace({
const activeMetricValue = activeSelectionResult?.summary?.metric_value ?? selectedResultTotal
const activeMetricUnit = activeSelectionResult?.summary?.metric_unit ?? 'objecten'
const activeMetricLabel = activeSelectionResult?.summary?.metric_label ?? activeTheme.shortLabel
const activeSupportingMetrics = (activeSelectionResult?.summary?.metrics ?? []).filter(
(metric) => metric.metric_key !== activeSelectionResult?.summary?.primary_metric_key,
)
const activeSecondaryMetric = selectedAreaSquareMetres && selectedAreaSquareMetres > 0
? activeMetricUnit === 'ha'
? `${((activeMetricValue * 10_000) / selectedAreaSquareMetres * 100).toLocaleString('nl-BE', { maximumFractionDigits: 1 })}% dekking`
@@ -1350,6 +1358,17 @@ export function MapWorkspace({
</div>
</div>
{activeSupportingMetrics.length > 0 ? (
<div className="geo-supporting-metrics" aria-label="Aanvullende gebiedsmetingen">
{activeSupportingMetrics.map((metric) => (
<div key={metric.metric_key}>
<span>{metric.metric_label}</span>
<strong>{selectionMetricLabel(metric)}</strong>
</div>
))}
</div>
) : null}
<div className="geo-theme-results">
<div className="geo-results-title-row">
<h4>Alle beschikbare themas</h4>
@@ -1376,6 +1395,9 @@ export function MapWorkspace({
{analysisMode === 'current' && activeSelectionResult?.truncated ? (
<p className="geo-data-notice">De telling is volledig; op de kaart en in de tabel worden maximaal {activeSelectionResult.limit.toLocaleString('nl-BE')} objecten getoond.</p>
) : null}
{analysisMode === 'current' && activeSelectionResult?.summary?.warning ? (
<p className="geo-data-notice">{activeSelectionResult.summary.warning}</p>
) : null}
{mapSelectionError ? <p className="error">{mapSelectionError}</p> : null}
{themeResultsError ? <p className="error">{themeResultsError}</p> : null}
{temporalComparisonError ? <p className="error">{temporalComparisonError}</p> : null}