Complete the V1 analysis handoff
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-17 02:52:15 +02:00
parent d9bc8fdbed
commit 03c6154cac
9 changed files with 240 additions and 14 deletions
+62 -11
View File
@@ -625,8 +625,8 @@ function copyText(text: string): void {
fallbackCopyText(text)
}
function downloadJsonFile(filename: string, payload: unknown): void {
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/geo+json' })
function downloadJsonFile(filename: string, payload: unknown, contentType = 'application/json'): void {
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: contentType })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
@@ -716,6 +716,8 @@ interface MapWorkspaceProps {
onRunOrthophotoAnalysis: (bbox: VectorSelectionBBox) => Promise<boolean>
onSelectOrthophotoProduct: (productKey: string) => void
onClearQualityEvidence?: () => void
onOpenAssistant: () => void
onOpenExports: () => void
}
export function MapWorkspace({
@@ -797,6 +799,8 @@ export function MapWorkspace({
onRunOrthophotoAnalysis,
onSelectOrthophotoProduct,
onClearQualityEvidence,
onOpenAssistant,
onOpenExports,
}: MapWorkspaceProps): JSX.Element {
const [advancedMode, setAdvancedMode] = useState(false)
const [activeThemeId, setActiveThemeId] = useState<DataThemeId>(() => {
@@ -1135,7 +1139,7 @@ export function MapWorkspace({
if (!selectedFeatureGeoJson) {
return
}
downloadJsonFile(selectedFeatureFilename, selectedFeatureGeoJson)
downloadJsonFile(selectedFeatureFilename, selectedFeatureGeoJson, 'application/geo+json')
}
const copySelectedMapFeatureProperties = () => {
@@ -1192,22 +1196,46 @@ export function MapWorkspace({
if (!mapSelectionResult) {
return
}
downloadJsonFile(DEFAULT_AREA_SELECTION_FILENAME, mapSelectionResult.geojson)
downloadJsonFile(DEFAULT_AREA_SELECTION_FILENAME, mapSelectionResult.geojson, 'application/geo+json')
}
const copyAreaSelection = () => {
copyText(JSON.stringify(mapSelectionResult?.geojson ?? { type: 'FeatureCollection', features: [] }, null, 2))
}
const downloadActiveThemeSelection = () => {
const downloadActiveThemeResult = () => {
if (!activeSelectionResult) {
return
}
downloadJsonFile(`${activeTheme.id}-selection.geojson`, activeSelectionResult.geojson)
if (activeThemeDataset?.dataset_type === 'raster') {
downloadJsonFile(`${activeTheme.id}-analysis.json`, {
project_id: selectedProjectId,
area_id: selectedMapArea?.id ?? null,
area_name: selectedMapArea?.name ?? null,
theme: activeTheme,
dataset_id: activeThemeDataset.id,
dataset_name: activeThemeDataset.name,
source_name: activeThemeDataset.source_name,
result: activeSelectionResult,
})
return
}
downloadJsonFile(`${activeTheme.id}-selection.geojson`, activeSelectionResult.geojson, 'application/geo+json')
}
const copyActiveThemeSelection = () => {
copyText(JSON.stringify(activeSelectionResult?.geojson ?? { type: 'FeatureCollection', features: [] }, null, 2))
const copyActiveThemeResult = () => {
copyText(JSON.stringify(activeSelectionResult ?? {}, null, 2))
}
const downloadTemporalComparison = () => {
if (!temporalComparison) {
return
}
downloadJsonFile(`${activeTheme.id}-evolution-${temporalComparison.earlier.observed_at.slice(0, 10)}-${temporalComparison.later.observed_at.slice(0, 10)}.json`, temporalComparison)
}
const copyTemporalComparison = () => {
copyText(JSON.stringify(temporalComparison ?? {}, null, 2))
}
const saveAreaSelectionExport = () => {
@@ -1888,7 +1916,10 @@ export function MapWorkspace({
<strong>{theme.label}</strong>
<small>{dataset ? getDatasetSourceDisplayName(dataset) : 'Geen bron gekoppeld'}</small>
</span>
<b>{item ? resultMetricLabel(item.result) : dataset ? 'Niet bevraagd' : 'Bron ontbreekt'}</b>
<span className="geo-theme-result-value">
<b>{item ? resultMetricLabel(item.result) : dataset ? 'Niet bevraagd' : 'Bron ontbreekt'}</b>
{item?.result.summary ? <small>{item.result.summary.metric_label}</small> : null}
</span>
</div>
)
})}
@@ -1930,8 +1961,28 @@ export function MapWorkspace({
{analysisMode === 'current' ? (
<div className="geo-result-actions">
<button className="secondary-action" disabled={!activeSelectionResult || activeThemeDataset?.dataset_type === 'raster'} type="button" onClick={downloadActiveThemeSelection}>Download GeoJSON</button>
<button className="secondary-action" disabled={!activeSelectionResult} type="button" onClick={copyActiveThemeSelection}>Kopieer gegevens</button>
<button className="secondary-action" disabled={!activeSelectionResult} type="button" onClick={downloadActiveThemeResult}>
{activeThemeDataset?.dataset_type === 'raster' ? 'Download analyse' : 'Download GeoJSON'}
</button>
<button className="secondary-action" disabled={!activeSelectionResult} type="button" onClick={copyActiveThemeResult}>Kopieer gegevens</button>
</div>
) : null}
{analysisMode === 'evolution' && temporalComparison ? (
<div className="geo-result-actions">
<button className="secondary-action" type="button" onClick={downloadTemporalComparison}>Download vergelijking</button>
<button className="secondary-action" type="button" onClick={copyTemporalComparison}>Kopieer vergelijking</button>
</div>
) : null}
{activeSelectionResult || temporalComparison ? (
<div className="geo-result-next-actions" aria-label="Volgende stap">
<span>
<strong>Analyse klaar</strong>
<small>Stel een vraag over dit gebied of open je bewaarde resultaten.</small>
</span>
<button className="primary-action" type="button" onClick={onOpenAssistant}>Stel AI-vraag</button>
<button className="secondary-action" type="button" onClick={onOpenExports}>Open downloads</button>
</div>
) : null}
</>