From 7fa061b6b4307c43e5761f8fd2eff97dc872ea19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1muel=20Fekete?= Date: Wed, 20 Nov 2024 16:58:43 +0100 Subject: [PATCH] feat: dont reload map when theme or selected map changes (#47) --- src/components/Map/VectorTileLayer.tsx | 26 ++++-- src/operations/map/MapOperations.ts | 111 +++++++++++++++++-------- 2 files changed, 96 insertions(+), 41 deletions(-) diff --git a/src/components/Map/VectorTileLayer.tsx b/src/components/Map/VectorTileLayer.tsx index 43e4d93e..3ed163bc 100644 --- a/src/components/Map/VectorTileLayer.tsx +++ b/src/components/Map/VectorTileLayer.tsx @@ -3,7 +3,7 @@ import 'mapbox-gl/dist/mapbox-gl.css'; import { LeafletContextInterface, useLeafletContext } from '@react-leaflet/core'; import mapboxgl from 'mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax import { useTheme } from 'next-themes'; -import React, { RefObject, useEffect, useRef } from 'react'; +import React, { RefObject, useEffect, useRef, useState } from 'react'; import { useSelectedMap } from '@/domain/contexts/SelectedMapContext'; import { MapProps } from '@/domain/props/MapProps'; @@ -14,6 +14,7 @@ export default function VectorTileLayer({ countries, disputedAreas }: MapProps) const context: LeafletContextInterface = useLeafletContext(); const mapContainer: RefObject = useRef(null); const { selectedMapType } = useSelectedMap(); + const [map, setMap] = useState(); mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN as string; @@ -23,18 +24,33 @@ export default function VectorTileLayer({ countries, disputedAreas }: MapProps) { countries, disputedAreas }, mapContainer ); + baseMap.on('load', () => setMap(baseMap)); MapOperations.setMapInteractionFunctionality(baseMap); MapOperations.synchronizeLeafletMapbox(baseMap, mapContainer, context); // The following layers currently don't work due to CORS issues. - MapOperations.addRainfallLayer(baseMap, selectedMapType); - MapOperations.addVegetationLayer(baseMap, selectedMapType); - MapOperations.addFCSLayer(baseMap, selectedMapType); + MapOperations.initRainfallLayer(baseMap); + MapOperations.initVegetationLayer(baseMap); + MapOperations.initFCSLayer(baseMap); return () => { baseMap.remove(); context.map.off('move'); + setMap(undefined); }; - }, [context, theme, selectedMapType]); + }, [context]); + + useEffect(() => { + if (map) { + MapOperations.removeActiveMapLayer(map); + MapOperations.addMapAsLayer(map, selectedMapType); + } + }, [map, selectedMapType]); + + useEffect(() => { + if (map) { + MapOperations.changeMapTheme(map, theme === 'dark'); + } + }, [theme]); return
; } diff --git a/src/operations/map/MapOperations.ts b/src/operations/map/MapOperations.ts index 64345162..5d079099 100644 --- a/src/operations/map/MapOperations.ts +++ b/src/operations/map/MapOperations.ts @@ -192,30 +192,42 @@ export class MapOperations { }); } - static addFCSLayer(baseMap: mapboxgl.Map, selectedMapType: GlobalInsight) { + static changeMapTheme(baseMap: mapboxgl.Map, isDark: boolean) { + const mapColors: MapColorsType = getColors(isDark); + baseMap.setPaintProperty('ocean', 'background-color', mapColors.ocean); + baseMap.setPaintProperty('countries-base', 'fill-color', mapColors.countriesBase); + baseMap.setPaintProperty('countries-inactive', 'fill-color', mapColors.inactiveCountriesOverlay); + baseMap.setPaintProperty('countries-hover', 'fill-color', mapColors.outline); + baseMap.setPaintProperty('country-borders', 'line-color', mapColors.outline); + baseMap.setPaintProperty('mapbox-roads', 'line-color', mapColors.roads); + } + + static FCS_RASTER = 'fcsRaster'; + + static FCS_LAYER = 'fcsLayer'; + + static RAINFALL_RASTER = 'rainfallRaster'; + + static RAINFALL_LAYER = 'rainfallLayer'; + + static VEGETATION_RASTER = 'vegetationRaster'; + + static VEGETATION_LAYER = 'vegetationLayer'; + + static initFCSLayer(baseMap: mapboxgl.Map) { baseMap.on('load', () => { - baseMap.addSource('fcsRaster', { + baseMap.addSource(this.FCS_RASTER, { type: 'raster', tiles: ['https://static.hungermapdata.org/proteus_tiles/{z}/{x}/{y}.png'], tileSize: 256, scheme: 'tms', }); - - baseMap.addLayer( - { - id: 'fcsLayer', - type: 'raster', - source: 'fcsRaster', - layout: { visibility: selectedMapType === GlobalInsight.FOOD ? 'visible' : 'none' }, - }, - 'countries-inactive' - ); }); } - static addRainfallLayer(baseMap: mapboxgl.Map, selectedMapType: GlobalInsight) { + static initRainfallLayer(baseMap: mapboxgl.Map) { baseMap.on('load', () => { - baseMap.addSource('rainfallRaster', { + baseMap.addSource(this.RAINFALL_RASTER, { type: 'raster', tiles: [`https://dev.api.earthobservation.vam.wfp.org/tiles/latest/r3q_dekad/{z}/{x}/{y}.png`], tileSize: 256, @@ -223,22 +235,12 @@ export class MapOperations { maxzoom: 7, bounds: [-180, -49, 180, 49], }); - - baseMap.addLayer( - { - id: 'rainfallLayer', - type: 'raster', - source: 'rainfallRaster', - layout: { visibility: selectedMapType === GlobalInsight.RAINFALL ? 'visible' : 'none' }, - }, - 'countries-inactive' - ); }); } - static addVegetationLayer(baseMap: mapboxgl.Map, selectedMapType: GlobalInsight) { + static initVegetationLayer(baseMap: mapboxgl.Map) { baseMap.on('load', () => { - baseMap.addSource('vegetationRaster', { + baseMap.addSource(this.VEGETATION_RASTER, { type: 'raster', tiles: [`https://dev.api.earthobservation.vam.wfp.org/tiles/latest/viq_dekad/{z}/{x}/{y}.png`], tileSize: 256, @@ -246,16 +248,53 @@ export class MapOperations { maxzoom: 7, bounds: [-180, -60, 180, 80], }); - - baseMap.addLayer( - { - id: 'vegetationLayer', - type: 'raster', - source: 'vegetationRaster', - layout: { visibility: selectedMapType === GlobalInsight.VEGETATION ? 'visible' : 'none' }, - }, - 'countries-inactive' - ); }); } + + static addMapAsLayer(baseMap: mapboxgl.Map, selectedMap: GlobalInsight) { + switch (selectedMap) { + case GlobalInsight.FOOD: + baseMap.addLayer( + { + id: this.FCS_LAYER, + type: 'raster', + source: this.FCS_RASTER, + }, + 'countries-inactive' + ); + break; + case GlobalInsight.VEGETATION: + baseMap.addLayer( + { + id: this.VEGETATION_LAYER, + type: 'raster', + source: this.VEGETATION_RASTER, + }, + 'countries-inactive' + ); + break; + case GlobalInsight.RAINFALL: + baseMap.addLayer( + { + id: this.RAINFALL_LAYER, + type: 'raster', + source: this.RAINFALL_RASTER, + }, + 'countries-inactive' + ); + break; + default: + } + } + + static removeActiveMapLayer(baseMap: mapboxgl.Map) { + const layers = baseMap.getStyle()?.layers; + if (!layers) return; + const layerToRemove = layers.find((layer) => + // TODO make sure to update this list with the new layers! + [this.FCS_LAYER, this.VEGETATION_LAYER, this.RAINFALL_LAYER].includes(layer.id) + ); + if (!layerToRemove) return; + baseMap.removeLayer(layerToRemove.id); + } }