Skip to content
Snippets Groups Projects
SidebarLayers.vue 12.3 KiB
Newer Older
  <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>
Timothee P's avatar
Timothee P committed
            <!-- 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"
            />
Timothee P's avatar
Timothee P committed
            <!--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"
      @addLayers="addLayers"
      @activateGroup="activateGroup"
      @onlayerMove="onlayerMove"
      @onOpacityUpdate="onOpacityUpdate"
      @onQueryLayerChange="onQueryLayerChange"
import { mapState } from 'vuex';

import LayerSelector from '@/components/Map/LayerSelector.vue';
DESPRES Damien's avatar
DESPRES Damien committed
import mapService from '@/services/map-service';
  name: 'SidebarLayers',
  components: {
      projectSlug: this.$route.params.slug,
      selectedQueryLayer: '',
    ...mapState([
      'isOnline',
    ]),
    ...mapState('map', [
    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);
    },
    // 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 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) {
          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 {
DESPRES Damien's avatar
DESPRES Damien committed
      mapService.addLayers(
        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
    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;

        // 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) {
                serverBasemap.layers.length === localBasemap.layers.length &&
                serverBasemap.layers.every(
                  (layer, index) => layer.id === localBasemap.layers[index].id &&
                      layer.queryable === localBasemap.layers[index].queryable
        // 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]
        );
      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;
      });
DESPRES Damien's avatar
DESPRES Damien committed
      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
DESPRES Damien's avatar
DESPRES Damien committed
      mapService.addLayers(baseMap.layers.slice().reverse(), null, null, null,);
      //* 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.
      for (const layerId of currentLayersIdInOrder) {
          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)
      );
    },