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