extract and test the map rectangle interaction state

Corner tracking for a two-click rectangle lived inline in the component and
was never exercised. The rule worth pinning is that a first corner yields no
rectangle at all: acting on it would analyse a zero-width area.

The hook holds only interaction state — drawing mode, placed corner, the
coordinate fields. What happens with a finished rectangle stays in the
workspace, which owns retiring stale results and starting the analysis.
Injecting that would have made the hook depend on values declared after it,
which is what a first attempt at a wider extraction ran into.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jens
2026-08-22 19:01:45 +02:00
co-authored by Claude Opus 5
parent 9c29577b82
commit 6177eecef5
3 changed files with 132 additions and 136 deletions
+18 -21
View File
@@ -6,6 +6,7 @@ import { useMapThemeSelectionInsights, type MapThemeQuery } from '../../hooks/us
import { useOfficialMapProducts } from '../../hooks/useOfficialMapProducts'
import { useTemporalComparison } from '../../hooks/useTemporalComparison'
import { isValueRampRasterSource, useMapImageOverlays } from '../../hooks/useMapImageOverlays'
import { useMapRectangleSelection } from '../../hooks/useMapRectangleSelection'
import { getDatasetDisplayName, getDatasetSourceDisplayName } from '../../lib/datasetDisplay'
import { TemporalTrendChart } from './TemporalTrendChart'
import { MunicipalitySearch } from './MunicipalitySearch'
@@ -18,7 +19,6 @@ import {
formatPerformanceDuration,
} from '../../lib/performanceBudget'
import {
bboxToInputState,
bboxesEqual,
copyText,
datasetIntersectsSelection,
@@ -31,7 +31,6 @@ import {
getFeatureCollectionBBox,
getFeatureGeometrySummary,
isMunicipalityAreaName,
normalizeBboxFromCorners,
operationalScopeProjectLabel,
parseBboxInput,
persistedDatasetSupportsSelection,
@@ -318,9 +317,13 @@ export function MapWorkspace({
const [selectedTemporalSeriesKey, setSelectedTemporalSeriesKey] = useState('')
const [earlierDatasetId, setEarlierDatasetId] = useState('')
const [laterDatasetId, setLaterDatasetId] = useState('')
const [bboxSelectionMode, setBboxSelectionMode] = useState(false)
const [firstSelectionCorner, setFirstSelectionCorner] = useState<[number, number] | null>(null)
const [bboxInput, setBboxInput] = useState(bboxToInputState(mapSelectionBbox))
const rectangle = useMapRectangleSelection(mapSelectionBbox)
const {
drawing: bboxSelectionMode,
setDrawing: setBboxSelectionMode,
bboxInput,
setBboxInput,
} = rectangle
const [fullWorkflowRunning, setFullWorkflowRunning] = useState(false)
const [fullWorkflowStatus, setFullWorkflowStatus] = useState('Klaar om de volledige GIS-werkstroom uit te voeren.')
const [fullWorkflowError, setFullWorkflowError] = useState<string | null>(null)
@@ -970,7 +973,7 @@ export function MapWorkspace({
}, [activeSelectionResult])
useEffect(() => {
setBboxInput(bboxToInputState(mapSelectionBbox))
rectangle.showBbox((mapSelectionBbox))
}, [mapSelectionBbox])
useEffect(() => {
@@ -1059,7 +1062,7 @@ export function MapWorkspace({
const setSelectionBbox = (bbox: VectorSelectionBBox | null) => {
onSetMapSelectionBbox(bbox)
setBboxInput(bboxToInputState(bbox))
rectangle.showBbox((bbox))
}
const areaIdForSelection = (bbox: VectorSelectionBBox | null): string | undefined => (
@@ -1067,7 +1070,7 @@ export function MapWorkspace({
)
const startBboxSelection = () => {
setFirstSelectionCorner(null)
rectangle.reset()
clearThemeInsights()
clearTemporalComparison()
setResultsPanelOpen(false)
@@ -1075,14 +1078,10 @@ export function MapWorkspace({
}
const handleMapCoordinateSelect = (coordinate: [number, number]) => {
if (!firstSelectionCorner) {
setFirstSelectionCorner(coordinate)
return
}
const bbox = normalizeBboxFromCorners(firstSelectionCorner, coordinate)
// The first click only places a corner; there is no rectangle to act on yet.
const bbox = rectangle.placeCorner(coordinate)
if (!bbox) return
setSelectionBbox(bbox)
setFirstSelectionCorner(null)
setBboxSelectionMode(false)
clearThemeInsights()
clearTemporalComparison()
setResultsPanelOpen(false)
@@ -1099,9 +1098,8 @@ export function MapWorkspace({
const clearAreaSelection = () => {
mapAnalysisRequestSequence.current += 1
setMapAnalysisDurationMs(null)
setBboxSelectionMode(false)
setFirstSelectionCorner(null)
setBboxInput(bboxToInputState(null))
rectangle.reset()
rectangle.showBbox((null))
setResultsPanelOpen(false)
clearThemeInsights()
clearTemporalComparison()
@@ -1449,8 +1447,7 @@ export function MapWorkspace({
}
const handleMapBboxSelect = (bbox: VectorSelectionBBox) => {
setFirstSelectionCorner(null)
setBboxSelectionMode(false)
rectangle.reset()
clearThemeInsights()
clearTemporalComparison()
setResultsPanelOpen(false)
@@ -2930,7 +2927,7 @@ export function MapWorkspace({
</span>
</div>
<div className="bbox-select-status">
<span>{bboxSelectionMode ? (firstSelectionCorner ? 'Klik de tegenoverliggende hoek' : 'Klik de eerste hoek op de kaart') : 'Begrenzing EPSG:4326'}</span>
<span>{bboxSelectionMode ? (rectangle.firstCorner ? 'Klik de tegenoverliggende hoek' : 'Klik de eerste hoek op de kaart') : 'Begrenzing EPSG:4326'}</span>
<strong>{formatBboxLabel(currentSelectionBbox)}</strong>
</div>
<div className="bbox-select-grid" aria-label="Coördinaten van de gebiedsselectie">