fix(map): make area analysis scale-aware
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-21 21:59:56 +02:00
parent 80631607f7
commit 20829f1a24
20 changed files with 619 additions and 56 deletions
@@ -8,9 +8,12 @@ import {
productCoversZones,
resultMetricLabel,
selectedAreaCoverageZones,
selectionAnalysisScale,
selectionAreaSquareMetres,
selectionDimensions,
splitSelectionBbox,
} from './mapWorkspaceUtils'
import type { VectorSelectionResponse } from '../../types'
import type { VectorSelectionBBox, VectorSelectionResponse } from '../../types'
describe('map workspace selection guards', () => {
it('normalizes drag corners into an EPSG:4326 bbox', () => {
@@ -90,4 +93,49 @@ describe('map workspace selection guards', () => {
geometry_clipped_to_selection: true,
})).toBe(false)
})
it('classifies local, regional and overview selections before provider calls', () => {
const bbox = (widthDegrees: number, heightDegrees: number): VectorSelectionBBox => ({
min_x: 5,
min_y: 51,
max_x: 5 + widthDegrees,
max_y: 51 + heightDegrees,
crs: 'EPSG:4326',
})
expect(selectionAnalysisScale(bbox(0.1, 0.1))).toBe('detail')
expect(selectionAnalysisScale(bbox(0.35, 0.25))).toBe('regional')
expect(selectionAnalysisScale(bbox(1, 1))).toBe('overview')
})
it('splits regional selections into provider-safe tiles without changing the outer bounds', () => {
const selection: VectorSelectionBBox = {
min_x: 5,
min_y: 51,
max_x: 5.5,
max_y: 51.35,
crs: 'EPSG:4326',
}
const tiles = splitSelectionBbox(selection)
expect(tiles.length).toBeGreaterThan(1)
expect(Math.min(...tiles.map((tile) => tile.min_x))).toBe(selection.min_x)
expect(Math.min(...tiles.map((tile) => tile.min_y))).toBe(selection.min_y)
expect(Math.max(...tiles.map((tile) => tile.max_x))).toBe(selection.max_x)
expect(Math.max(...tiles.map((tile) => tile.max_y))).toBe(selection.max_y)
for (const tile of tiles) {
const dimensions = selectionDimensions(tile)
expect(dimensions.widthMetres).toBeLessThanOrEqual(18_100)
expect(dimensions.heightMetres).toBeLessThanOrEqual(18_100)
}
})
it('refuses an unbounded detail fan-out', () => {
const selection: VectorSelectionBBox = {
min_x: 4,
min_y: 50,
max_x: 6,
max_y: 52,
crs: 'EPSG:4326',
}
expect(() => splitSelectionBbox(selection)).toThrow('detailpartities')
})
})