fix: scope evolution series to work area
This commit is contained in:
@@ -50,6 +50,7 @@ import {
|
||||
selectionFeatureLimit,
|
||||
selectionMetricLabel,
|
||||
splitSelectionBbox,
|
||||
temporalDatasetAreaMatch,
|
||||
} from './mapWorkspaceUtils'
|
||||
|
||||
const DEFAULT_SELECTED_FEATURE_FILENAME = 'selected-feature.geojson'
|
||||
@@ -584,22 +585,43 @@ function temporalSeriesLabel(items: DatasetCreateResponse[]): string {
|
||||
return range ? `${source} (${range})` : source
|
||||
}
|
||||
|
||||
function listThemeTemporalSeries(datasets: DatasetCreateResponse[], theme: DataTheme): TemporalSeriesGroup[] {
|
||||
function listThemeTemporalSeries(
|
||||
datasets: DatasetCreateResponse[],
|
||||
theme: DataTheme,
|
||||
selectedAreaId: string | null,
|
||||
): TemporalSeriesGroup[] {
|
||||
const groups = new Map<string, DatasetCreateResponse[]>()
|
||||
for (const dataset of datasets) {
|
||||
if (!datasetMatchesTheme(dataset, theme) || !dataset.temporal_series_key || !dataset.observed_at) {
|
||||
continue
|
||||
}
|
||||
if (temporalDatasetAreaMatch(dataset, selectedAreaId) === 'other') {
|
||||
continue
|
||||
}
|
||||
const items = groups.get(dataset.temporal_series_key) ?? []
|
||||
items.push(dataset)
|
||||
groups.set(dataset.temporal_series_key, items)
|
||||
}
|
||||
return Array.from(groups.entries())
|
||||
const series = Array.from(groups.entries())
|
||||
.map(([key, items]) => {
|
||||
const ordered = deduplicateTemporalSnapshots(items)
|
||||
return { key, label: temporalSeriesLabel(ordered), items: ordered }
|
||||
})
|
||||
.filter((group) => group.items.length >= 2)
|
||||
|
||||
const sourcesWithExactAreaSeries = new Set(
|
||||
series
|
||||
.filter((group) => group.items.some((item) => temporalDatasetAreaMatch(item, selectedAreaId) === 'exact'))
|
||||
.map((group) => group.items[0]?.source_name)
|
||||
.filter(Boolean),
|
||||
)
|
||||
|
||||
return series
|
||||
.filter((group) => {
|
||||
const sourceName = group.items[0]?.source_name
|
||||
if (!sourceName || !sourcesWithExactAreaSeries.has(sourceName)) return true
|
||||
return group.items.some((item) => temporalDatasetAreaMatch(item, selectedAreaId) === 'exact')
|
||||
})
|
||||
.sort((left, right) => {
|
||||
if (right.items.length !== left.items.length) {
|
||||
return right.items.length - left.items.length
|
||||
@@ -1442,9 +1464,9 @@ export function MapWorkspace({
|
||||
const themeTemporalSeriesMap = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
DATA_THEMES.map((theme) => [theme.id, listThemeTemporalSeries(availableMapDatasets, theme)]),
|
||||
DATA_THEMES.map((theme) => [theme.id, listThemeTemporalSeries(availableMapDatasets, theme, selectedMapAreaId)]),
|
||||
) as Record<DataThemeId, TemporalSeriesGroup[]>,
|
||||
[availableMapDatasets],
|
||||
[availableMapDatasets, selectedMapAreaId],
|
||||
)
|
||||
const activeTemporalSeriesGroups = themeTemporalSeriesMap[activeTheme.id]
|
||||
const availableEvolutionThemes = DATA_THEMES.filter((theme) =>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
selectionDimensions,
|
||||
selectionFeatureLimit,
|
||||
splitSelectionBbox,
|
||||
temporalDatasetAreaMatch,
|
||||
} from './mapWorkspaceUtils'
|
||||
import type { VectorSelectionBBox, VectorSelectionResponse } from '../../types'
|
||||
|
||||
@@ -98,6 +99,12 @@ describe('map workspace selection guards', () => {
|
||||
})).toBe(false)
|
||||
})
|
||||
|
||||
it('distinguishes exact, unscoped and foreign temporal work-area snapshots', () => {
|
||||
expect(temporalDatasetAreaMatch({ area_id: 'wallonia-golden' }, 'wallonia-golden')).toBe('exact')
|
||||
expect(temporalDatasetAreaMatch({ area_id: 'other-area' }, 'wallonia-golden')).toBe('other')
|
||||
expect(temporalDatasetAreaMatch({ area_id: null }, 'wallonia-golden')).toBe('unscoped')
|
||||
})
|
||||
|
||||
it('classifies local, regional and overview selections before provider calls', () => {
|
||||
const bbox = (widthDegrees: number, heightDegrees: number): VectorSelectionBBox => ({
|
||||
min_x: 5,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { featureCollectionBounds } from '../../lib/geojsonBounds'
|
||||
import type {
|
||||
DatasetCreateResponse,
|
||||
ProjectRead,
|
||||
VectorSelectionBBox,
|
||||
VectorSelectionMetric,
|
||||
@@ -50,6 +51,16 @@ export function isSelectionBoundedDataset(
|
||||
&& sourceMetadata['bbox_epsg4326'].length === 4
|
||||
}
|
||||
|
||||
export type TemporalAreaMatch = 'exact' | 'unscoped' | 'other'
|
||||
|
||||
export function temporalDatasetAreaMatch(
|
||||
dataset: Pick<DatasetCreateResponse, 'area_id'>,
|
||||
selectedAreaId: string | null,
|
||||
): TemporalAreaMatch {
|
||||
if (!selectedAreaId || !dataset.area_id) return 'unscoped'
|
||||
return dataset.area_id === selectedAreaId ? 'exact' : 'other'
|
||||
}
|
||||
|
||||
export function operationalScopeProjectLabel(project: ProjectRead): string {
|
||||
if (project.name === MOL_PROJECT_NAME) {
|
||||
return 'Mol'
|
||||
|
||||
Reference in New Issue
Block a user