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 <noreply@anthropic.com>
This commit is contained in:
Jens
2026-08-22 14:56:37 +02:00
co-authored by Claude Opus 5
parent ea0e401690
commit 9614307669
4 changed files with 34 additions and 1 deletions
+2
View File
@@ -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,
@@ -120,6 +120,10 @@ export function ChangeDetectionPanel({
<span className="metric">{result.removed_count}</span>
<span>Verdwenen</span>
</div>
<div>
<span className="metric">{result.modified_count ?? 0}</span>
<span>Gewijzigd</span>
</div>
<div>
<span className="metric">{result.unchanged_count}</span>
<span>Ongewijzigd</span>
@@ -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<void>
/**
* 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')
+13
View File
@@ -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