142 lines
5.2 KiB
TypeScript
142 lines
5.2 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
bboxesEqual,
|
|
isMunicipalityAreaName,
|
|
isSelectionBoundedDataset,
|
|
normalizeBboxFromCorners,
|
|
parseBboxInput,
|
|
productCoversZones,
|
|
resultMetricLabel,
|
|
selectedAreaCoverageZones,
|
|
selectionAnalysisScale,
|
|
selectionAreaSquareMetres,
|
|
selectionDimensions,
|
|
splitSelectionBbox,
|
|
} from './mapWorkspaceUtils'
|
|
import type { VectorSelectionBBox, VectorSelectionResponse } from '../../types'
|
|
|
|
describe('map workspace selection guards', () => {
|
|
it('normalizes drag corners into an EPSG:4326 bbox', () => {
|
|
expect(normalizeBboxFromCorners([5.2, 51.3], [4.8, 50.9])).toEqual({
|
|
min_x: 4.8,
|
|
min_y: 50.9,
|
|
max_x: 5.2,
|
|
max_y: 51.3,
|
|
crs: 'EPSG:4326',
|
|
})
|
|
})
|
|
|
|
it('rejects empty, inverted and degenerate manual selections', () => {
|
|
expect(parseBboxInput({ min_x: '', min_y: '', max_x: '', max_y: '' })).toBeNull()
|
|
expect(parseBboxInput({ min_x: '5', min_y: '51', max_x: '4', max_y: '52' })).toBeNull()
|
|
expect(parseBboxInput({ min_x: '4', min_y: '51', max_x: '4', max_y: '52' })).toBeNull()
|
|
})
|
|
|
|
it('treats sub-nanodegree bbox drift as the same persisted selection', () => {
|
|
const bbox = normalizeBboxFromCorners([4.98, 51.15], [5.02, 51.19])
|
|
expect(bboxesEqual(bbox, { ...bbox, max_x: bbox.max_x + 1e-10 })).toBe(true)
|
|
expect(bboxesEqual(bbox, { ...bbox, max_x: bbox.max_x + 1e-5 })).toBe(false)
|
|
})
|
|
|
|
it('computes a positive bounded selection area and renders governed metrics', () => {
|
|
const bbox = normalizeBboxFromCorners([5.0, 51.0], [5.01, 51.01])
|
|
expect(selectionAreaSquareMetres(bbox)).toBeGreaterThan(700_000)
|
|
expect(selectionAreaSquareMetres(bbox)).toBeLessThan(900_000)
|
|
|
|
const result = {
|
|
feature_count: 12,
|
|
truncated: false,
|
|
summary: {
|
|
metric_key: 'forest_area',
|
|
metric_value: 14.236,
|
|
metric_unit: 'ha',
|
|
},
|
|
} as unknown as VectorSelectionResponse
|
|
expect(resultMetricLabel(result)).toBe('14,24 ha')
|
|
})
|
|
|
|
it('maps national work areas to regional provider zones without merging sources', () => {
|
|
expect(selectedAreaCoverageZones('Belgium land')).toEqual([
|
|
'belgium',
|
|
'flanders',
|
|
'wallonia',
|
|
'brussels',
|
|
])
|
|
expect(selectedAreaCoverageZones('RC Ardennes inland')).toEqual(['wallonia'])
|
|
expect(selectedAreaCoverageZones('Brussels-Capital Region')).toEqual(['brussels'])
|
|
expect(selectedAreaCoverageZones('Language boundary')).toEqual(['flanders', 'wallonia'])
|
|
expect(productCoversZones(['wallonia'], ['flanders', 'wallonia'])).toBe(true)
|
|
expect(productCoversZones(['brussels'], ['wallonia'])).toBe(false)
|
|
})
|
|
|
|
it('recognizes Dutch and release-golden municipality areas as local scope', () => {
|
|
expect(isMunicipalityAreaName('Gemeente Mol')).toBe(true)
|
|
expect(isMunicipalityAreaName('RC Golden - Mol municipality')).toBe(true)
|
|
expect(isMunicipalityAreaName('Flanders')).toBe(false)
|
|
expect(isMunicipalityAreaName('RC Golden - Kempen region')).toBe(false)
|
|
})
|
|
|
|
it('distinguishes selection-bounded acquisitions from reusable regional datasets', () => {
|
|
expect(isSelectionBoundedDataset({
|
|
geometry_clipped_to_selection: true,
|
|
bbox_epsg4326: [4.55, 50.58, 4.56, 50.59],
|
|
})).toBe(true)
|
|
expect(isSelectionBoundedDataset({
|
|
coverage_scope: 'bounded_selection',
|
|
bbox_epsg4326: [5.1, 51.17, 5.11, 51.18],
|
|
})).toBe(true)
|
|
expect(isSelectionBoundedDataset({
|
|
geometry_clipped_to_selection: false,
|
|
bbox_epsg4326: [2.5, 49.5, 6.4, 51.6],
|
|
})).toBe(false)
|
|
expect(isSelectionBoundedDataset({
|
|
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')
|
|
})
|
|
})
|