From 9614307669283ac8b76bb1f42981e2f8725de6cc Mon Sep 17 00:00:00 2001 From: Jens Date: Sat, 22 Aug 2026 14:56:37 +0200 Subject: [PATCH] send the map selection to change detection and show the modified class The comparison hook runs before the selection state is declared in App, so the selection is read through a getter at the moment the run starts rather than captured at render. The panel gains the modified count, which until now was folded into removed plus added. Co-Authored-By: Claude Opus 5 --- frontend/src/App.tsx | 2 ++ .../components/analysis/ChangeDetectionPanel.tsx | 4 ++++ frontend/src/hooks/useChangeDetectionWorkflow.ts | 16 +++++++++++++++- frontend/src/types.ts | 13 +++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e5d88b15..27bc2235 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -305,6 +305,8 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA selectedProjectId, availableVectorDatasets, loadDatasetJobs, + // Bind the comparison to whatever the operator has selected on the map. + getSelection: () => ({ bbox: mapSelectionBbox ?? null, areaId: selectedMapAreaId ?? null }), }) const { qaCandidateDatasetId, diff --git a/frontend/src/components/analysis/ChangeDetectionPanel.tsx b/frontend/src/components/analysis/ChangeDetectionPanel.tsx index 276ba9e5..b057c41d 100644 --- a/frontend/src/components/analysis/ChangeDetectionPanel.tsx +++ b/frontend/src/components/analysis/ChangeDetectionPanel.tsx @@ -120,6 +120,10 @@ export function ChangeDetectionPanel({ {result.removed_count} Verdwenen +
+ {result.modified_count ?? 0} + Gewijzigd +
{result.unchanged_count} Ongewijzigd diff --git a/frontend/src/hooks/useChangeDetectionWorkflow.ts b/frontend/src/hooks/useChangeDetectionWorkflow.ts index 9f41fcf2..7d1749eb 100644 --- a/frontend/src/hooks/useChangeDetectionWorkflow.ts +++ b/frontend/src/hooks/useChangeDetectionWorkflow.ts @@ -1,18 +1,29 @@ import { useState } from 'react' import { analysisApi } from '../services/api' -import type { ChangeDetectionSummary, DatasetCreateResponse } from '../types' +import type { ChangeDetectionSummary, DatasetCreateResponse, VectorSelectionBBox } from '../types' import { formatError } from '../lib/formatError' interface ChangeDetectionWorkflowOptions { selectedProjectId: string | null availableVectorDatasets: DatasetCreateResponse[] loadDatasetJobs: (projectId: string, datasetId: string) => Promise + /** + * Reads the active map selection when the comparison is started. Without a + * selection the comparison covers both datasets entire, which is rarely the + * question being asked and returns a payload no map can draw. + * + * A getter rather than a value because this hook runs before the selection + * state is declared in the component; it is only ever called from a user + * event, long after that binding is initialised. + */ + getSelection?: () => { bbox: VectorSelectionBBox | null; areaId: string | null } } export function useChangeDetectionWorkflow({ selectedProjectId, availableVectorDatasets, loadDatasetJobs, + getSelection, }: ChangeDetectionWorkflowOptions) { const [changeSourceDatasetId, setChangeSourceDatasetId] = useState('') const [changeTargetDatasetId, setChangeTargetDatasetId] = useState('') @@ -41,12 +52,15 @@ export function useChangeDetectionWorkflow({ setChangeDetectionError(null) setChangeDetectionResult(null) setRunningChangeDetection(true) + const selection = getSelection?.() ?? { bbox: null, areaId: null } try { const job = await analysisApi.runChangeDetection({ source_dataset_id: sourceDatasetId, target_dataset_id: targetDatasetId, iou_threshold: changeIouThreshold, include_unchanged: changeIncludeUnchanged, + ...(selection.bbox ? { bbox: selection.bbox } : {}), + ...(selection.areaId ? { area_id: selection.areaId } : {}), }) if (job.status !== 'success') { throw new Error(job.error_message || 'Change detection job failed') diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 7293d7ca..05385524 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -1055,7 +1055,13 @@ export interface ChangeDetectionRequest { source_dataset_id: string target_dataset_id: string iou_threshold: number + /** Below this the footprints are separate objects rather than one redrawn. */ + modified_threshold?: number include_unchanged: boolean + /** Without a selection the comparison covers both datasets in full. */ + bbox?: VectorSelectionBBox | null + area_id?: string | null + preview_limit?: number } export interface ChangeDetectionSummary { @@ -1065,8 +1071,15 @@ export interface ChangeDetectionSummary { target_feature_count: number added_count: number removed_count: number + /** A footprint that was redrawn, not demolished and rebuilt. */ + modified_count?: number unchanged_count: number iou_threshold: number + modified_iou_threshold?: number | null + selection_area_id?: string | null + /** Counts cover the whole selection; the GeoJSON is capped. */ + preview_limit?: number | null + preview_truncated?: boolean warnings: string[] generated_at: string geojson: GeoJSON.FeatureCollection