<template> <div v-if="isOnline" :class="['sidebar-container', { expanded }]" > <div class="layers-icon" @click="toggleSidebar()" > <!-- // ! svg point d'interrogation pas accepté par linter --> <!-- <?xml version="1.0" encoding="iso-8859-1"?> --> <svg id="Capa_1" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 491.203 491.203" style="enable-background: new 0 0 491.203 491.203" xml:space="preserve" > <g> <g> <!-- eslint-disable max-len --> <path d="M487.298,326.733l-62.304-37.128l62.304-37.128c2.421-1.443,3.904-4.054,3.904-6.872s-1.483-5.429-3.904-6.872 l-62.304-37.128l62.304-37.128c3.795-2.262,5.038-7.172,2.776-10.968c-0.68-1.142-1.635-2.096-2.776-2.776l-237.6-141.6 c-2.524-1.504-5.669-1.504-8.192,0l-237.6,141.6c-3.795,2.262-5.038,7.172-2.776,10.968c0.68,1.142,1.635,2.096,2.776,2.776 l62.304,37.128L3.905,238.733c-3.795,2.262-5.038,7.172-2.776,10.968c0.68,1.142,1.635,2.096,2.776,2.776l62.304,37.128 L3.905,326.733c-3.795,2.262-5.038,7.172-2.776,10.968c0.68,1.142,1.635,2.096,2.776,2.776l237.6,141.6 c2.526,1.494,5.666,1.494,8.192,0l237.6-141.6c3.795-2.262,5.038-7.172,2.776-10.968 C489.393,328.368,488.439,327.414,487.298,326.733z M23.625,157.605L245.601,25.317l221.976,132.288L245.601,289.893 L23.625,157.605z M23.625,245.605l58.208-34.68l159.672,95.2c2.524,1.504,5.668,1.504,8.192,0l159.672-95.2l58.208,34.68 L245.601,377.893L23.625,245.605z M245.601,465.893L23.625,333.605l58.208-34.68l159.672,95.2c2.524,1.504,5.668,1.504,8.192,0 l159.672-95.2l58.208,34.68L245.601,465.893z" /> <!--eslint-enable--> </g> </g> </svg> </div> <div class="basemaps-title"> <h4> Fonds cartographiques </h4> </div> <LayerSelector v-for="basemap in baseMaps" :key="`list-${basemap.id}`" :basemap="basemap" :selected-query-layer="selectedQueryLayer" :active="basemap.active" @addLayers="addLayers" @activateGroup="activateGroup" @onlayerMove="onlayerMove" @onOpacityUpdate="onOpacityUpdate" @onQueryLayerChange="onQueryLayerChange" /> </div> </template> <script> import { mapState } from 'vuex'; import LayerSelector from '@/components/Map/LayerSelector.vue'; import mapService from '@/services/map-service'; export default { name: 'SidebarLayers', components: { LayerSelector }, data() { return { baseMaps: [], expanded: false, projectSlug: this.$route.params.slug, selectedQueryLayer: '', }; }, computed: { ...mapState([ 'isOnline', ]), ...mapState('map', [ 'availableLayers', 'basemaps' ]), activeBasemap() { return this.baseMaps.find((baseMap) => baseMap.active); }, activeBasemapIndex() { return this.baseMaps.findIndex((el) => el.id === this.activeBasemap.id); }, activeQueryableLayers() { return this.baseMaps[this.activeBasemapIndex].layers.filter((layer) => layer.queryable); }, }, mounted() { // clone object to not modify data in store, using json parse instead of spread operator that modifies data, for instance when navigating to basemap administration page this.baseMaps = JSON.parse(JSON.stringify(this.basemaps)); const mapOptions = JSON.parse(localStorage.getItem('geocontrib-map-options')) || {}; if (mapOptions && mapOptions[this.projectSlug]) { // If already in the storage, we need to check if the admin did some // modification in the basemaps on the server side. The rule is: if one layer has been added // or deleted in the server, then we reset the localstorage. const baseMapsFromLocalstorage = mapOptions[this.projectSlug]['basemaps']; const areChanges = this.areChangesInBasemaps( this.baseMaps, baseMapsFromLocalstorage ); if (areChanges) { mapOptions[this.projectSlug] = { basemaps: this.baseMaps, 'current-basemap-index': 0, }; localStorage.setItem( 'geocontrib-map-options', JSON.stringify(mapOptions) ); } else if (baseMapsFromLocalstorage) { this.baseMaps = baseMapsFromLocalstorage; } } if (this.baseMaps.length > 0) { // if an active layers has been set earlier... if (mapOptions[this.projectSlug] && mapOptions[this.projectSlug]['current-basemap-index']) { // ...then set the concerned layer with active property at true this.baseMaps.find((baseMap) => baseMap.id === mapOptions[this.projectSlug]['current-basemap-index']).active = true; } else { // set the first basemap as active this.baseMaps[0].active = true; } this.addLayers(this.activeBasemap); this.setSelectedQueryLayer(); } else { mapService.addLayers( null, this.$store.state.configuration.DEFAULT_BASE_MAP_SERVICE, this.$store.state.configuration.DEFAULT_BASE_MAP_OPTIONS, this.$store.state.configuration.DEFAULT_BASE_MAP_SCHEMA_TYPE ); } }, methods: { toggleSidebar(value) { this.expanded = value !== undefined ? value : !this.expanded; }, // Check if there are changes in the basemaps settings. Changes are detected if: // - one basemap has been added or deleted // - one layer has been added or deleted to a basemap areChangesInBasemaps(basemapFromServer, basemapFromLocalstorage) { // prevent undefined later even if in this case the function is not called anyway if (!basemapFromLocalstorage) return false; let isSameBasemaps = false; let isSameLayers = true; let isSameTitles = true; // Compare the length and the id values of the basemaps const idBasemapsServer = basemapFromServer.map((b) => b.id).sort(); const idBasemapsLocalstorage = basemapFromLocalstorage.map((b) => b.id).sort() || {}; isSameBasemaps = idBasemapsServer.length === idBasemapsLocalstorage.length && idBasemapsServer.every( (value, index) => value === idBasemapsLocalstorage[index] ); // if basemaps changed, return that changed occured to avoid more processing if (!isSameBasemaps) return true; outer_block: { // For each basemap from the server, compare the length and id values of the layers for (const serverBasemap of basemapFromServer) { // loop over basemaps from localStorage and check if layers id & queryable setting match with the layers from the server // we don't check opacity since it would detect a change and reinit each time the user set it. It would need to be stored separatly like current basemap index for (const localBasemap of basemapFromLocalstorage) { if (serverBasemap.id === localBasemap.id) { isSameLayers = serverBasemap.layers.length === localBasemap.layers.length && serverBasemap.layers.every( (layer, index) => layer.id === localBasemap.layers[index].id && layer.queryable === localBasemap.layers[index].queryable ); if (!isSameLayers) { break outer_block; } } } } // Compare basemaps titles const titlesBasemapsServer = basemapFromServer .map((b) => b.title) .sort(); const titlesBasemapsLocalstorage = basemapFromLocalstorage.map((b) => b.title).sort() || {}; isSameTitles = titlesBasemapsServer.every( (title, index) => title === titlesBasemapsLocalstorage[index] ); if (!isSameTitles) { break outer_block; } } return !(isSameBasemaps && isSameLayers && isSameTitles); }, addLayers(baseMap) { baseMap.layers.forEach((layer) => { const layerOptions = this.availableLayers.find((l) => l.id === layer.id); layer = Object.assign(layer, layerOptions); layer.options.basemapId = baseMap.id; }); mapService.removeLayers(); // Reverse is done because the first layer in order has to be added in the map in last. // Slice is done because reverse() changes the original array, so we make a copy first mapService.addLayers(baseMap.layers.slice().reverse(), null, null, null,); }, activateGroup(basemapId) { //* to activate a basemap, we need to set the active property to true and set the others to false this.baseMaps = this.baseMaps.map((bm) => { return { ...bm, active: bm.id === basemapId ? true : false }; }); this.setSelectedQueryLayer(); //* add the basemap that was clicked to the map this.addLayers(this.baseMaps.find((bm) => bm.id === basemapId)); //* to persist the settings, we set the localStorage mapOptions per project this.setLocalstorageMapOptions({ 'current-basemap-index': basemapId }); }, onlayerMove() { // Get ids of the draggable layers in its current order. const currentLayersIdInOrder = Array.from( document.querySelectorAll('.content.active .layer-item.transition.visible') ).map((el) => parseInt(el.attributes['data-id'].value)); // Create an array to put the original layers in the same order. let movedLayers = []; for (const layerId of currentLayersIdInOrder) { movedLayers.push( this.activeBasemap.layers.find((el) => el.id === layerId) ); } // Remove existing layers undefined movedLayers = movedLayers.filter(function (x) { return x !== undefined; }); const eventOrder = new CustomEvent('change-layers-order', { detail: { layers: movedLayers, }, }); document.dispatchEvent(eventOrder); // report layers order change in the basemaps object before saving them this.baseMaps[this.activeBasemapIndex].layers = movedLayers; // Save the basemaps options into the localstorage this.setLocalstorageMapOptions({ basemaps: this.baseMaps }); }, onOpacityUpdate(data) { const { layerId, opacity } = data; // retrieve layer to update opacity this.baseMaps[this.activeBasemapIndex].layers.find((layer) => layer.id === layerId).opacity = opacity; // Save the basemaps options into the localstorage this.setLocalstorageMapOptions({ basemaps: this.baseMaps }); }, activateQueryLayer(layerTitle) { const baseMapLayer = this.baseMaps[this.activeBasemapIndex].layers.find((l) => l.title === layerTitle); if (baseMapLayer) { // remove any query property in all layers and set query property at true for selected layer this.baseMaps[this.activeBasemapIndex].layers.forEach((l) => delete l.query); baseMapLayer['query'] = true; // update selected query layer this.setSelectedQueryLayer(); } else { console.error('No such param \'query\' found among basemap[0].layers'); } }, onQueryLayerChange(layerTitle) { this.activateQueryLayer(layerTitle); this.setLocalstorageMapOptions({ basemaps: this.baseMaps }); }, // retrieve selected query layer in active basemap (to be called when mounting and when changing active basemap) setSelectedQueryLayer() { const currentQueryLayer = this.baseMaps[this.activeBasemapIndex].layers.find((l) => l.query === true); if (currentQueryLayer) { this.selectedQueryLayer = currentQueryLayer.title; } else if (this.activeQueryableLayers[0]) { // if no current query layer previously selected by user this.activateQueryLayer(this.activeQueryableLayers[0].title); // then activate the first available query layer of the active basemap } }, setLocalstorageMapOptions(newOptionObj) { let mapOptions = localStorage.getItem('geocontrib-map-options') || {}; mapOptions = mapOptions.length ? JSON.parse(mapOptions) : {}; mapOptions[this.projectSlug] = { ...mapOptions[this.projectSlug], ...newOptionObj, }; localStorage.setItem( 'geocontrib-map-options', JSON.stringify(mapOptions) ); }, }, }; </script>