From 25686803ac8c4c23b61d3f75d1ed9572a79b5365 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Thu, 27 Jul 2023 12:01:16 +0100 Subject: [PATCH 1/7] Add dataset layer predefined bounds support Fix NASA-IMPACT/veda-config-ghg#21 --- .../components/common/mapbox/index.tsx | 2 ++ .../mapbox/layers/raster-timeseries.tsx | 19 ++++++++---- .../components/common/mapbox/layers/utils.ts | 2 ++ .../mapbox/layers/vector-timeseries.tsx | 29 ++++++++++++++++--- .../components/datasets/s-explore/index.tsx | 1 + mock/datasets/no2.data.mdx | 6 ++-- parcel-resolver-veda/index.d.ts | 1 + 7 files changed, 49 insertions(+), 11 deletions(-) diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index eaf22a64d..83aff43dc 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -422,6 +422,7 @@ function MapboxMapComponent( date={date} sourceParams={baseLayerResolvedData.sourceParams} zoomExtent={baseLayerResolvedData.zoomExtent} + bounds={baseLayerResolvedData.bounds} onStatusChange={onBaseLayerStatusChange} /> )} @@ -471,6 +472,7 @@ function MapboxMapComponent( date={compareToDate ?? undefined} sourceParams={compareLayerResolvedData.sourceParams} zoomExtent={compareLayerResolvedData.zoomExtent} + bounds={compareLayerResolvedData.bounds} onStatusChange={onCompareLayerStatusChange} /> )} diff --git a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx index a6db13516..1dde4ec4f 100644 --- a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx @@ -16,6 +16,7 @@ import { featureCollection, point } from '@turf/helpers'; import { useMapStyle } from './styles'; import { checkFitBoundsFromLayer, + FIT_BOUNDS_PADDING, getFilterPayload, getMergedBBox, requestQuickCache, @@ -34,8 +35,6 @@ import { // Whether or not to print the request logs. const LOG = true; -const FIT_BOUNDS_PADDING = 32; - export interface MapLayerRasterTimeseriesProps { id: string; stacCol: string; @@ -43,6 +42,7 @@ export interface MapLayerRasterTimeseriesProps { mapInstance: MapboxMap; sourceParams?: Record; zoomExtent?: number[]; + bounds?: number[]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; isHidden?: boolean; idSuffix?: string; @@ -72,6 +72,7 @@ export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { mapInstance, sourceParams, zoomExtent, + bounds, onStatusChange, isHidden, idSuffix = '' @@ -470,10 +471,18 @@ export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { if (!stacCollection.length) return; const layerBounds = getMergedBBox(stacCollection); - if (checkFitBoundsFromLayer(layerBounds, mapInstance)) { - mapInstance.fitBounds(layerBounds, { padding: FIT_BOUNDS_PADDING }); + // Prefer layer defined bounds to STAC collection bounds. + const usableBounds = (bounds?.length === 4 ? bounds : layerBounds) as [ + number, + number, + number, + number + ]; + + if (checkFitBoundsFromLayer(usableBounds, mapInstance)) { + mapInstance.fitBounds(usableBounds, { padding: FIT_BOUNDS_PADDING }); } - }, [mapInstance, stacCol, stacCollection]); + }, [mapInstance, stacCol, bounds, stacCollection]); return null; } diff --git a/app/scripts/components/common/mapbox/layers/utils.ts b/app/scripts/components/common/mapbox/layers/utils.ts index 91f46f731..5c213d4bd 100644 --- a/app/scripts/components/common/mapbox/layers/utils.ts +++ b/app/scripts/components/common/mapbox/layers/utils.ts @@ -370,6 +370,8 @@ export function getMergedBBox(features: StacFeature[]) { ) as [number, number, number, number]; } +export const FIT_BOUNDS_PADDING = 32; + export function checkFitBoundsFromLayer( layerBounds?: [number, number, number, number], mapInstance?: MapboxMap diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index bf200d927..497bd8055 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -12,7 +12,12 @@ import { Feature } from 'geojson'; import { endOfDay, startOfDay } from 'date-fns'; import centroid from '@turf/centroid'; -import { requestQuickCache, useLayerInteraction } from './utils'; +import { + checkFitBoundsFromLayer, + FIT_BOUNDS_PADDING, + requestQuickCache, + useLayerInteraction +} from './utils'; import { useMapStyle } from './styles'; import { useCustomMarker } from './custom-marker'; @@ -26,6 +31,7 @@ export interface MapLayerVectorTimeseriesProps { mapInstance: MapboxMap; sourceParams?: Record; zoomExtent?: number[]; + bounds?: number[]; onStatusChange?: (result: { status: ActionStatus; id: string }) => void; isHidden?: boolean; idSuffix?: string; @@ -39,6 +45,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { mapInstance, sourceParams, zoomExtent, + bounds, onStatusChange, isHidden, idSuffix = '' @@ -67,7 +74,9 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { controller }); - setFeaturesApiEndpoint(data.links.find((l) => l.rel === 'external').href); + setFeaturesApiEndpoint( + data.links.find((l) => l.rel === 'external').href + ); onStatusChange?.({ status: S_SUCCEEDED, id }); } catch (error) { if (!controller.signal.aborted) { @@ -85,7 +94,6 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { }; }, [mapInstance, id, stacCol, date, onStatusChange]); - const markerLayout = useCustomMarker(mapInstance); // @@ -192,7 +200,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { 'source-layer': 'default', layout: { ...(markerLayout as any), - visibility: isHidden ? 'none' : 'visible', + visibility: isHidden ? 'none' : 'visible' }, paint: { 'icon-color': theme.color?.infographicB, @@ -266,5 +274,18 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { onClick: onPointsClick }); + // + // FitBounds when needed + // + useEffect(() => { + if (bounds?.length !== 4) return; + + const b = bounds as [number, number, number, number]; + + if (checkFitBoundsFromLayer(b, mapInstance)) { + mapInstance.fitBounds(b, { padding: FIT_BOUNDS_PADDING }); + } + }, [mapInstance, bounds]); + return null; } diff --git a/app/scripts/components/datasets/s-explore/index.tsx b/app/scripts/components/datasets/s-explore/index.tsx index 9b63e8c29..5f4d9be74 100644 --- a/app/scripts/components/datasets/s-explore/index.tsx +++ b/app/scripts/components/datasets/s-explore/index.tsx @@ -215,6 +215,7 @@ function DatasetsExplore() { useEffect(() => { setPanelRevealed(!isMediumDown); }, [isMediumDown]); + // When the panel changes resize the map after a the animation finishes. useEffect(() => { const id = setTimeout( diff --git a/mock/datasets/no2.data.mdx b/mock/datasets/no2.data.mdx index b64ea8519..b4313ab58 100644 --- a/mock/datasets/no2.data.mdx +++ b/mock/datasets/no2.data.mdx @@ -37,8 +37,9 @@ taxonomy: layers: - id: no2-monthly stacCol: no2-monthly - name: No2 + name: No2 PT type: raster + bounds: [-10, 36, -5, 42] description: Levels in 10¹⁵ molecules cm⁻². Darker colors indicate higher nitrogen dioxide (NO₂) levels associated and more activity. Lighter colors indicate lower levels of NO₂ and less activity. zoomExtent: - 0 @@ -73,7 +74,8 @@ layers: - "#050308" - id: no2-monthly-2 stacCol: no2-monthly - name: No2 + name: No2 US + bounds: [-124, 29, -65, 49] type: raster description: Levels in 10¹⁵ molecules cm⁻². Darker colors indicate higher nitrogen dioxide (NO₂) levels associated and more activity. Lighter colors indicate lower levels of NO₂ and less activity. zoomExtent: diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index 420fe6b71..3578f925e 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -26,6 +26,7 @@ declare module 'veda' { interface DatasetLayerCommonProps { zoomExtent?: number[]; + bounds?: number[]; sourceParams?: Record; } From 1983f819431f02f2e24553d072b47d09f3617f20 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Thu, 27 Jul 2023 12:28:58 +0100 Subject: [PATCH 2/7] Update documentation --- docs/content/frontmatter/layer.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/content/frontmatter/layer.md b/docs/content/frontmatter/layer.md index 2fd07ef98..fa7fec6fb 100644 --- a/docs/content/frontmatter/layer.md +++ b/docs/content/frontmatter/layer.md @@ -16,6 +16,7 @@ initialDatetime: 'oldest' | 'newest' | Date(YYYY-MM-DD) = 'newest' description: string projection: Projection zoomExtent: [int, int] | null | fn(bag) +bounds: [int, int, int, int] | null | fn(bag) sourceParams: [key]: value | fn(bag) compare: Compare @@ -67,6 +68,20 @@ These values may vary greatly depending on the layer being added but some may be `string` The colormap to use for the layer. One of https://cogeotiff.github.io/rio-tiler/colormap/#default-rio-tilers-colormaps +**bounds** +`[int, int, int, int] | fn(bag)` +Initial bounds for the map. This is useful for datasets that are not global, and for which the STAC bounds are not appropriate. + +This property should be an array with 4 numbers, representing the minimum and maximum longitude and latitude values, in that order. +Example (world bounds) +```yml +bounds: [-180, -90, 180, 90] +``` + +Note on bounds and dataset layer switching: +The exploration map will always prioritize the position set in the url. This is so that the user can share a link to a specific location. However, upon load the map will check if the position set in the url is within or overlapping the bounds of the dataset layer. If it is not, the map will switch to the dataset layer bounds avoiding showing an empty map when the user shares a link to a location that is not within the dataset layer bounds. +If there are no bounds set in the dataset configuration, the bbox from the STAC catalog will be used if available, otherwise it will default to the world bounds. + ### Projection **projection** From 1484b828bbbf3fedfffd841855d6323fc448d964 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Thu, 27 Jul 2023 15:19:44 +0100 Subject: [PATCH 3/7] Improve docs Co-authored-by: Erik Escoffier --- docs/content/frontmatter/layer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/frontmatter/layer.md b/docs/content/frontmatter/layer.md index fa7fec6fb..15b575833 100644 --- a/docs/content/frontmatter/layer.md +++ b/docs/content/frontmatter/layer.md @@ -72,7 +72,7 @@ These values may vary greatly depending on the layer being added but some may be `[int, int, int, int] | fn(bag)` Initial bounds for the map. This is useful for datasets that are not global, and for which the STAC bounds are not appropriate. -This property should be an array with 4 numbers, representing the minimum and maximum longitude and latitude values, in that order. +This property should be an array with 4 numbers, representing the minimum and maximum longitude and latitude values, in the following order: [minLongitude, minLatitude, maxLongitude, maxLatitude]. Example (world bounds) ```yml bounds: [-180, -90, 180, 90] From 66a4977bc3a772283b17f36b947a575c6362cec2 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Thu, 27 Jul 2023 16:22:53 +0100 Subject: [PATCH 4/7] Generalize fit bounds function --- .../mapbox/layers/raster-timeseries.tsx | 23 ++++---------- .../components/common/mapbox/layers/utils.ts | 28 +++++++++++++++++ .../mapbox/layers/vector-timeseries.tsx | 31 ++++++++++--------- mock/datasets/fire.data.mdx | 10 +----- 4 files changed, 52 insertions(+), 40 deletions(-) diff --git a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx index 1dde4ec4f..45ea69511 100644 --- a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx @@ -15,11 +15,11 @@ import { featureCollection, point } from '@turf/helpers'; import { useMapStyle } from './styles'; import { - checkFitBoundsFromLayer, FIT_BOUNDS_PADDING, getFilterPayload, getMergedBBox, requestQuickCache, + useFitBbox, useLayerInteraction } from './utils'; import { useCustomMarker } from './custom-marker'; @@ -467,22 +467,11 @@ export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { // // FitBounds when needed // - useEffect(() => { - if (!stacCollection.length) return; - const layerBounds = getMergedBBox(stacCollection); - - // Prefer layer defined bounds to STAC collection bounds. - const usableBounds = (bounds?.length === 4 ? bounds : layerBounds) as [ - number, - number, - number, - number - ]; - - if (checkFitBoundsFromLayer(usableBounds, mapInstance)) { - mapInstance.fitBounds(usableBounds, { padding: FIT_BOUNDS_PADDING }); - } - }, [mapInstance, stacCol, bounds, stacCollection]); + const layerBounds = useMemo( + () => (stacCollection.length ? getMergedBBox(stacCollection) : undefined), + [stacCollection] + ); + useFitBbox(mapInstance, bounds, layerBounds); return null; } diff --git a/app/scripts/components/common/mapbox/layers/utils.ts b/app/scripts/components/common/mapbox/layers/utils.ts index 5c213d4bd..31bc5c2de 100644 --- a/app/scripts/components/common/mapbox/layers/utils.ts +++ b/app/scripts/components/common/mapbox/layers/utils.ts @@ -431,3 +431,31 @@ export function useLayerInteraction({ }; }, [layerId, mapInstance, onClick]); } + + +type OptionalBbox = number[] | undefined | null; + +/** + * Centers on the given bounds if the current position is not within the bounds. + * Gives preference to the layer defined bounds over the STAC collection bounds. + * + * @param mapInstance Mapbox instance + * @param initialBbox Bounding box from the layer + * @param stacBbox Bounds from the STAC collection + */ +export function useFitBbox( + mapInstance: MapboxMap, + initialBbox: OptionalBbox, + stacBbox: OptionalBbox +) { + useEffect(() => { + // Prefer layer defined bounds to STAC collection bounds. + const bounds = (initialBbox ?? stacBbox) as + | [number, number, number, number] + | undefined; + + if (bounds?.length && checkFitBoundsFromLayer(bounds, mapInstance)) { + mapInstance.fitBounds(bounds, { padding: FIT_BOUNDS_PADDING }); + } + }, [mapInstance, initialBbox, stacBbox]); +} diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index 497bd8055..1a153214d 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -13,9 +13,8 @@ import { endOfDay, startOfDay } from 'date-fns'; import centroid from '@turf/centroid'; import { - checkFitBoundsFromLayer, - FIT_BOUNDS_PADDING, requestQuickCache, + useFitBbox, useLayerInteraction } from './utils'; import { useMapStyle } from './styles'; @@ -54,6 +53,8 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { const theme = useTheme(); const { updateStyle } = useMapStyle(); const [featuresApiEndpoint, setFeaturesApiEndpoint] = useState(''); + const [featuresBbox, setFeaturesBbox] = + useState<[number, number, number, number]>(); const [minZoom, maxZoom] = zoomExtent ?? [0, 20]; @@ -74,9 +75,19 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { controller }); - setFeaturesApiEndpoint( - data.links.find((l) => l.rel === 'external').href - ); + const endpoint = data.links.find((l) => l.rel === 'external').href; + setFeaturesApiEndpoint(endpoint); + + const featuresData = await requestQuickCache({ + url: endpoint, + method: 'GET', + controller + }); + + if (featuresData.extent.spatial.bbox) { + setFeaturesBbox(featuresData.extent.spatial.bbox[0]); + } + onStatusChange?.({ status: S_SUCCEEDED, id }); } catch (error) { if (!controller.signal.aborted) { @@ -277,15 +288,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { // // FitBounds when needed // - useEffect(() => { - if (bounds?.length !== 4) return; - - const b = bounds as [number, number, number, number]; - - if (checkFitBoundsFromLayer(b, mapInstance)) { - mapInstance.fitBounds(b, { padding: FIT_BOUNDS_PADDING }); - } - }, [mapInstance, bounds]); + useFitBbox(mapInstance, bounds, featuresBbox); return null; } diff --git a/mock/datasets/fire.data.mdx b/mock/datasets/fire.data.mdx index b7fe01504..8de00c53b 100644 --- a/mock/datasets/fire.data.mdx +++ b/mock/datasets/fire.data.mdx @@ -23,17 +23,9 @@ taxonomy: values: - COx layers: - - id: eis_fire_fireline - stacCol: eis_fire_fireline - name: Fire - type: vector - description: eis_fire_fireline - zoomExtent: - - 5 - - 20 - id: eis_fire_perimeter stacCol: eis_fire_perimeter - name: Fire Perimeter + name: Fire type: vector description: eis_fire_perimeter zoomExtent: From d5cb80312c24a309f1bd15691a17ea8c3ff7fee5 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Mon, 31 Jul 2023 10:09:52 +0100 Subject: [PATCH 5/7] Center bounds only if no map position is set --- app/scripts/components/common/mapbox/index.tsx | 1 + .../common/mapbox/layers/raster-timeseries.tsx | 5 +++-- app/scripts/components/common/mapbox/layers/utils.ts | 8 +++++--- .../common/mapbox/layers/vector-timeseries.tsx | 11 ++++------- app/scripts/components/datasets/s-explore/index.tsx | 6 +++++- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index 83aff43dc..beeedef51 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -419,6 +419,7 @@ function MapboxMapComponent( id={`base-${baseLayerResolvedData.id}`} stacCol={baseLayerResolvedData.stacCol} mapInstance={mapRef.current} + urlPosition={initialPosition} date={date} sourceParams={baseLayerResolvedData.sourceParams} zoomExtent={baseLayerResolvedData.zoomExtent} diff --git a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx index 45ea69511..afc1e2061 100644 --- a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx @@ -75,7 +75,8 @@ export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { bounds, onStatusChange, isHidden, - idSuffix = '' + idSuffix = '', + urlPosition } = props; const theme = useTheme(); @@ -471,7 +472,7 @@ export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { () => (stacCollection.length ? getMergedBBox(stacCollection) : undefined), [stacCollection] ); - useFitBbox(mapInstance, bounds, layerBounds); + useFitBbox(mapInstance, urlPosition, bounds, layerBounds); return null; } diff --git a/app/scripts/components/common/mapbox/layers/utils.ts b/app/scripts/components/common/mapbox/layers/utils.ts index 31bc5c2de..fd7851ddd 100644 --- a/app/scripts/components/common/mapbox/layers/utils.ts +++ b/app/scripts/components/common/mapbox/layers/utils.ts @@ -133,7 +133,7 @@ export const getCompareLayerData = ( type: otherLayer.type, name: otherLayer.name, description: otherLayer.description, - legend: otherLayer.legend, + legend: otherLayer.legend, stacCol: otherLayer.stacCol, zoomExtent: zoomExtent ?? otherLayer.zoomExtent, sourceParams: defaultsDeep({}, sourceParams, otherLayer.sourceParams), @@ -432,7 +432,6 @@ export function useLayerInteraction({ }, [layerId, mapInstance, onClick]); } - type OptionalBbox = number[] | undefined | null; /** @@ -445,10 +444,13 @@ type OptionalBbox = number[] | undefined | null; */ export function useFitBbox( mapInstance: MapboxMap, + urlPosition: any, initialBbox: OptionalBbox, stacBbox: OptionalBbox ) { useEffect(() => { + if (urlPosition) return; + // Prefer layer defined bounds to STAC collection bounds. const bounds = (initialBbox ?? stacBbox) as | [number, number, number, number] @@ -457,5 +459,5 @@ export function useFitBbox( if (bounds?.length && checkFitBoundsFromLayer(bounds, mapInstance)) { mapInstance.fitBounds(bounds, { padding: FIT_BOUNDS_PADDING }); } - }, [mapInstance, initialBbox, stacBbox]); + }, [mapInstance, urlPosition, initialBbox, stacBbox]); } diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index 1a153214d..cf4ed58a7 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -12,11 +12,7 @@ import { Feature } from 'geojson'; import { endOfDay, startOfDay } from 'date-fns'; import centroid from '@turf/centroid'; -import { - requestQuickCache, - useFitBbox, - useLayerInteraction -} from './utils'; +import { requestQuickCache, useFitBbox, useLayerInteraction } from './utils'; import { useMapStyle } from './styles'; import { useCustomMarker } from './custom-marker'; @@ -47,7 +43,8 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { bounds, onStatusChange, isHidden, - idSuffix = '' + idSuffix = '', + urlPosition } = props; const theme = useTheme(); @@ -288,7 +285,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { // // FitBounds when needed // - useFitBbox(mapInstance, bounds, featuresBbox); + useFitBbox(mapInstance, urlPosition, bounds, featuresBbox); return null; } diff --git a/app/scripts/components/datasets/s-explore/index.tsx b/app/scripts/components/datasets/s-explore/index.tsx index 5f4d9be74..2a8556e15 100644 --- a/app/scripts/components/datasets/s-explore/index.tsx +++ b/app/scripts/components/datasets/s-explore/index.tsx @@ -569,7 +569,11 @@ function DatasetsExplore() { compareDate={selectedCompareDatetime ?? undefined} isComparing={isComparing} initialPosition={mapPosition ?? undefined} - onPositionChange={setMapPosition} + onPositionChange={(v) => { + if (v.userInitiated) { + setMapPosition(v); + } + }} projection={mapProjection ?? projectionDefault} onProjectionChange={setMapProjection} /> From 58f232ba75f56f81c7f6e0f6e7e166697a52edf7 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Wed, 2 Aug 2023 17:12:08 +0100 Subject: [PATCH 6/7] Update documentation Co-authored-by: Jonas --- docs/content/frontmatter/layer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/frontmatter/layer.md b/docs/content/frontmatter/layer.md index 15b575833..5f84556de 100644 --- a/docs/content/frontmatter/layer.md +++ b/docs/content/frontmatter/layer.md @@ -70,7 +70,7 @@ These values may vary greatly depending on the layer being added but some may be **bounds** `[int, int, int, int] | fn(bag)` -Initial bounds for the map. This is useful for datasets that are not global, and for which the STAC bounds are not appropriate. +Initial bounds for the map. This is useful for adjusting the initial view on datasets for which the STAC bounds are not appropriate. This property should be an array with 4 numbers, representing the minimum and maximum longitude and latitude values, in the following order: [minLongitude, minLatitude, maxLongitude, maxLatitude]. Example (world bounds) From f65452678f56d762e0bbb777b8c7ad6908a54ee0 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Wed, 2 Aug 2023 17:36:01 +0100 Subject: [PATCH 7/7] Update variables and types --- app/scripts/components/common/mapbox/index.tsx | 2 +- .../common/mapbox/layers/raster-timeseries.tsx | 5 +++-- app/scripts/components/common/mapbox/layers/utils.ts | 12 +++++++----- .../common/mapbox/layers/vector-timeseries.tsx | 5 +++-- app/scripts/components/datasets/s-explore/index.tsx | 2 ++ 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/scripts/components/common/mapbox/index.tsx b/app/scripts/components/common/mapbox/index.tsx index beeedef51..23e692f5d 100644 --- a/app/scripts/components/common/mapbox/index.tsx +++ b/app/scripts/components/common/mapbox/index.tsx @@ -419,7 +419,7 @@ function MapboxMapComponent( id={`base-${baseLayerResolvedData.id}`} stacCol={baseLayerResolvedData.stacCol} mapInstance={mapRef.current} - urlPosition={initialPosition} + isPositionSet={!!initialPosition} date={date} sourceParams={baseLayerResolvedData.sourceParams} zoomExtent={baseLayerResolvedData.zoomExtent} diff --git a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx index afc1e2061..8826a3683 100644 --- a/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/raster-timeseries.tsx @@ -46,6 +46,7 @@ export interface MapLayerRasterTimeseriesProps { onStatusChange?: (result: { status: ActionStatus; id: string }) => void; isHidden?: boolean; idSuffix?: string; + isPositionSet?: boolean; } export interface StacFeature { @@ -76,7 +77,7 @@ export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { onStatusChange, isHidden, idSuffix = '', - urlPosition + isPositionSet } = props; const theme = useTheme(); @@ -472,7 +473,7 @@ export function MapLayerRasterTimeseries(props: MapLayerRasterTimeseriesProps) { () => (stacCollection.length ? getMergedBBox(stacCollection) : undefined), [stacCollection] ); - useFitBbox(mapInstance, urlPosition, bounds, layerBounds); + useFitBbox(mapInstance, !!isPositionSet, bounds, layerBounds); return null; } diff --git a/app/scripts/components/common/mapbox/layers/utils.ts b/app/scripts/components/common/mapbox/layers/utils.ts index fd7851ddd..d7300b357 100644 --- a/app/scripts/components/common/mapbox/layers/utils.ts +++ b/app/scripts/components/common/mapbox/layers/utils.ts @@ -435,21 +435,23 @@ export function useLayerInteraction({ type OptionalBbox = number[] | undefined | null; /** - * Centers on the given bounds if the current position is not within the bounds. - * Gives preference to the layer defined bounds over the STAC collection bounds. + * Centers on the given bounds if the current position is not within the bounds, + * and there's no user defined position (via user initiated map movement). Gives + * preference to the layer defined bounds over the STAC collection bounds. * * @param mapInstance Mapbox instance + * @param isUserPositionSet Whether the user has set a position * @param initialBbox Bounding box from the layer * @param stacBbox Bounds from the STAC collection */ export function useFitBbox( mapInstance: MapboxMap, - urlPosition: any, + isUserPositionSet: boolean, initialBbox: OptionalBbox, stacBbox: OptionalBbox ) { useEffect(() => { - if (urlPosition) return; + if (isUserPositionSet) return; // Prefer layer defined bounds to STAC collection bounds. const bounds = (initialBbox ?? stacBbox) as @@ -459,5 +461,5 @@ export function useFitBbox( if (bounds?.length && checkFitBoundsFromLayer(bounds, mapInstance)) { mapInstance.fitBounds(bounds, { padding: FIT_BOUNDS_PADDING }); } - }, [mapInstance, urlPosition, initialBbox, stacBbox]); + }, [mapInstance, isUserPositionSet, initialBbox, stacBbox]); } diff --git a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx index cf4ed58a7..3300a3d41 100644 --- a/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx +++ b/app/scripts/components/common/mapbox/layers/vector-timeseries.tsx @@ -30,6 +30,7 @@ export interface MapLayerVectorTimeseriesProps { onStatusChange?: (result: { status: ActionStatus; id: string }) => void; isHidden?: boolean; idSuffix?: string; + isPositionSet?: boolean; } export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { @@ -44,7 +45,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { onStatusChange, isHidden, idSuffix = '', - urlPosition + isPositionSet } = props; const theme = useTheme(); @@ -285,7 +286,7 @@ export function MapLayerVectorTimeseries(props: MapLayerVectorTimeseriesProps) { // // FitBounds when needed // - useFitBbox(mapInstance, urlPosition, bounds, featuresBbox); + useFitBbox(mapInstance, !!isPositionSet, bounds, featuresBbox); return null; } diff --git a/app/scripts/components/datasets/s-explore/index.tsx b/app/scripts/components/datasets/s-explore/index.tsx index 2a8556e15..7f7450ae1 100644 --- a/app/scripts/components/datasets/s-explore/index.tsx +++ b/app/scripts/components/datasets/s-explore/index.tsx @@ -570,6 +570,8 @@ function DatasetsExplore() { isComparing={isComparing} initialPosition={mapPosition ?? undefined} onPositionChange={(v) => { + // Only store the map position if the change was initiated by + // the user. if (v.userInitiated) { setMapPosition(v); }