diff --git a/src/mapper/src/lib/components/map/layer-switcher.svelte b/src/mapper/src/lib/components/map/layer-switcher.svelte index f305426fc..036793786 100644 --- a/src/mapper/src/lib/components/map/layer-switcher.svelte +++ b/src/mapper/src/lib/components/map/layer-switcher.svelte @@ -26,34 +26,49 @@ map = new Map({ import { onDestroy } from 'svelte'; import { clickOutside } from '$lib/utils/clickOutside'; + type MapLibreStylePlusMetadata = maplibregl.StyleSpecification & { + metadata: { + thumbnail?: string; + }; + }; + type Props = { styles: maplibregl.StyleSpecification[]; + selectedStyleName?: string | undefined; map: maplibregl.Map | undefined; sourcesIdToReAdd: string[]; - switchToNewestStyle: boolean; }; - const { styles, map, sourcesIdToReAdd, switchToNewestStyle = false }: Props = $props(); + const { styles, selectedStyleName, map, sourcesIdToReAdd }: Props = $props(); let allStyles: MapLibreStylePlusMetadata[] | [] = $state([]); let selectedStyleUrl: string | undefined = $state(undefined); - let isClosed = $state(true); + // This variable is used for updating the prop selectedStyleName dynamically + let reactiveStyleSelection: MapLibreStylePlusMetadata | undefined = $state(undefined); let isOpen = $state(false); - let isFirstLoad = true; + // Get style info when styles are updated $effect(() => { if (styles.length > 0) { + // We do not await this to avoid complicating reactive logic fetchStyleInfo(); } else { allStyles = []; } }); - type MapLibreStylePlusMetadata = maplibregl.StyleSpecification & { - metadata: { - thumbnail?: string; - }; - }; + $effect(() => { + // Set initial selected style + reactiveStyleSelection = allStyles.find((style) => style.name === selectedStyleName) || allStyles[0]; + selectedStyleUrl = reactiveStyleSelection?.metadata?.thumbnail; + }); + + // Update the map when a new style is selected + $effect(() => { + if (reactiveStyleSelection) { + selectStyle(reactiveStyleSelection); + } + }); /** * Extract the raster thumbnail root tile, or return an empty string. @@ -105,9 +120,7 @@ map = new Map({ // Process the current map style const currentMapStyle = map?.getStyle(); if (currentMapStyle) { - const processedStyle = processStyle(currentMapStyle); - selectedStyleUrl = processedStyle?.metadata?.thumbnail || undefined; - processedStyles.push(processedStyle); + processedStyles.push(processStyle(currentMapStyle)); } // Process additional styles (download first if style is URL) @@ -121,77 +134,48 @@ map = new Map({ } } - // Filter out duplicate styles based on `name` field - const deduplicatedStyles = [...processedStyles].filter( + // Deduplicate styles by `name` + allStyles = processedStyles.filter( (style, index, self) => self.findIndex((s) => s.name === style.name) === index, ); - - // If a new style is added later, we automatically switch - // to that style, if switchToNewestStyle is true - if (switchToNewestStyle && !isFirstLoad) { - // Determine new styles only on subsequent updates - const newStyles = deduplicatedStyles.filter( - style => !allStyles.find(existingStyle => existingStyle.name === style.name) - ); - - // Handle new styles (e.g., auto-switch to the newest style) - if (newStyles.length > 0) { - selectStyle(newStyles[newStyles.length - 1]); - } - } - - // Update allStyles only if there are changes - if (JSON.stringify(allStyles) !== JSON.stringify(deduplicatedStyles)) { - allStyles = deduplicatedStyles; - } - - isFirstLoad = false; } function selectStyle(style: MapLibreStylePlusMetadata) { - // returns all the map style i.e. all layers, sources const currentMapStyle = map?.getStyle(); - // reAddLayers: user defined layers that needs to be preserved - const reAddLayers = currentMapStyle?.layers?.filter((layer) => { - return sourcesIdToReAdd?.includes(layer?.source); - }); - - // reAddSources: user defined sources that needs to be preserved - const reAddSources = Object?.entries(currentMapStyle?.sources) - ?.filter(([key]) => sourcesIdToReAdd?.includes(key)) - ?.map(([key, value]) => ({ [key]: value })); + if (style.name === currentMapStyle.name) return; selectedStyleUrl = style.metadata.thumbnail; - // changes to selected base layer (note: user defined layer and sources are lost) + // Apply the selected style to the map + // being sure to save sources and layers to add back on top + const reAddLayers = currentMapStyle?.layers?.filter((layer) => sourcesIdToReAdd.includes(layer.source)); + const reAddSources = Object.entries(currentMapStyle?.sources || {}) + .filter(([key]) => sourcesIdToReAdd.includes(key)) + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); + map?.setStyle(style); - isClosed = !isClosed; - - // reapply user defined source - if (reAddSources?.length > 0) { - for (const reAddSource of reAddSources) { - for (const [id, source] of Object.entries(reAddSource)) { - if (!map?.getStyle().sources[id]) { - map?.addSource(id, source); - } + + // Re-add sources and layers + if (reAddSources) { + Object.entries(reAddSources).forEach(([id, source]) => { + if (!map?.getStyle().sources[id]) { + map?.addSource(id, source); } - } + }); } - // reapply user defined layers - if (reAddLayers?.length > 0) { - for (const layer of reAddLayers) { - if (!map?.getStyle().layers.find((layerx) => layerx?.id === layer.id)) { + + if (reAddLayers) { + reAddLayers.forEach((layer) => { + if (!map?.getStyle().layers.find((l) => l.id === layer.id)) { map?.addLayer(layer); } - } + }); } } - onDestroy(() => { allStyles = []; selectedStyleUrl = undefined; - isClosed = true; }); diff --git a/src/mapper/src/lib/components/map/main.svelte b/src/mapper/src/lib/components/map/main.svelte index 5cae8a8d8..59aa05b64 100644 --- a/src/mapper/src/lib/components/map/main.svelte +++ b/src/mapper/src/lib/components/map/main.svelte @@ -61,6 +61,7 @@ let map: maplibregl.Map | undefined = $state(); let loaded: boolean = $state(false); + let selectedBaselayer: string = $state('OSM'); let taskAreaClicked: boolean = $state(false); let toggleGeolocationStatus: boolean = $state(false); let projectSetupStep = $state(null); @@ -153,10 +154,6 @@ } }); - let taskAreaClicked: boolean = $state(false); - let toggleGeolocationStatus: boolean = $state(false); - let projectSetupStep = $state(null); - $effect(() => { projectSetupStep = +projectSetupStepStore.projectSetupStep; }); @@ -209,6 +206,7 @@ const offlineBasemapFile = await readFileFromOPFS(`${projectId}/basemap.pmtiles`); if (offlineBasemapFile) { await loadOfflinePmtiles(projectId); + selectedBaselayer = 'PMTiles'; } }); @@ -255,7 +253,7 @@ {map} styles={allBaseLayers} sourcesIdToReAdd={['tasks', 'entities', 'geolocation']} - switchToNewestStyle={true} + selectedStyleName={selectedBaselayer} > diff --git a/src/mapper/src/lib/fs/opfs.ts b/src/mapper/src/lib/fs/opfs.ts index a2e87c540..16e4d68fd 100644 --- a/src/mapper/src/lib/fs/opfs.ts +++ b/src/mapper/src/lib/fs/opfs.ts @@ -20,7 +20,7 @@ export async function readFileFromOPFS(filePath: string): Promise { if (!filename) { return null; // Invalid path } - console.log(`Getting OPFS file: ${filename}`); + // console.log(`Getting OPFS file: ${filename}`); const fileHandle = await currentDirectoryHandle.getFileHandle(filename); const fileData = await fileHandle.getFile(); // Read the file return fileData;