Skip to content
Snippets Groups Projects
SidebarLayers.vue 12.7 KiB
Newer Older
  <div
    v-if="isOnline"
    :class="['sidebar-container', { expanded }]"
  >
    <!-- <div class="sidebar-layers"></div> -->
    <div
      class="layers-icon"
      @click="expanded = !expanded"
    >
      <!-- // ! 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

        <!-- <span data-tooltip="Il est possible pour chaque fond cartographique de modifier l'ordre des couches"
            data-position="bottom left">
Florent Lavelle's avatar
Florent Lavelle committed
                <i class="question circle outline icon"></em>
    <div
      v-for="basemap in baseMaps"
      :key="`list-${basemap.id}`"
      class="basemaps-items ui accordion styled"
    >
      <div
        :class="{ active: isActive(basemap) }"
        class="basemap-item title"
        @click="activateGroup(basemap)"
      >
        {{ basemap.title }}
      </div>
        :id="`queryable-layers-selector-${basemap.id}`"
Florent Lavelle's avatar
Florent Lavelle committed
        <strong>Couche requêtable</strong>
          :options="getQueryableLayers(basemap)"
          :selected="selectedQueryLayer"
          :search="true"
          @update:selection="onQueryLayerChange($event)"
        :id="`list-${basemap.id}`"
        :class="{ active: isActive(basemap) }"
        class="content"
        :data-basemap-index="basemap.id"
          :key="basemap.id + '-' + layer.id + '-' + index"
          class="layer-item transition visible item list-group-item"
          :data-id="layer.id"
          <p class="layer-handle-sort">
Florent Lavelle's avatar
Florent Lavelle committed
            <i
              class="th icon"
              aria-hidden="true"
            />
            {{ layer.title }}
          </p>
          <label>Opacité &nbsp;<span>(%)</span></label>
          <div class="range-container">
            <input
              type="range"
              min="0"
              max="1"
              :value="layer.opacity"
              step="0.01"
              @change="updateOpacity($event, layer)"
            ><output class="range-output-bubble">{{
              getOpacity(layer.opacity)
            }}</output>
          <div class="ui divider" />
import { mapState } from 'vuex';
import Sortable from 'sortablejs';
import Dropdown from '@/components/Dropdown.vue';
DESPRES Damien's avatar
DESPRES Damien committed
import mapService from '@/services/map-service';
  name: 'SidebarLayers',
  components: {
    Dropdown,
  },
Florent Lavelle's avatar
Florent Lavelle committed
      sortable: null
    ...mapState([
      'isOnline',
    ]),
    ...mapState('map', [
      'availableLayers'
    ]),
  },

  mounted() {
    this.baseMaps = this.$store.state.map.basemaps;
Florent Lavelle's avatar
Florent Lavelle committed
    const project = this.$route.params.slug;
    const mapOptions =
      JSON.parse(localStorage.getItem('geocontrib-map-options')) || {};
    if (mapOptions && mapOptions[project]) {
      // 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[project]['basemaps'];
      const areChanges = this.areChangesInBasemaps(
        this.baseMaps,
        baseMapsFromLocalstorage
      );

      if (areChanges) {
        mapOptions[project] = {
          'map-options': this.baseMaps,
          'current-basemap-index': 0,
        };
        localStorage.setItem(
          'geocontrib-map-options',
          JSON.stringify(mapOptions)
        );
      } else {
        this.baseMaps = baseMapsFromLocalstorage;
      }
    }

    if (this.baseMaps.length > 0) {
      this.baseMaps[0].active = true;
      this.activeBasemap = this.baseMaps[0];
      this.addLayers(this.baseMaps[0]);
    } 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
      );
    }
    setTimeout(this.initSortable.bind(this), 1000);
    isActive(basemap) {
Timothee P's avatar
Timothee P committed
      return basemap.active !== undefined && basemap.active;
    activateGroup(basemap) {
      this.baseMaps.forEach((basemap) => (basemap.active = false));
      basemap.active = true;
      basemap.title += ' '; //weird!! Force refresh
      let mapOptions = localStorage.getItem('geocontrib-map-options') || {};
      mapOptions = mapOptions.length ? JSON.parse(mapOptions) : {};
Florent Lavelle's avatar
Florent Lavelle committed
      const project = this.$route.params.slug;
        'current-basemap-index': basemap.id,
        'geocontrib-map-options',
    updateOpacity(event, layer) {
DESPRES Damien's avatar
DESPRES Damien committed
      mapService.updateOpacity(layer.id, event.target.value);
      layer.opacity = event.target.value;
    getOpacity(opacity) {
      return Math.round(parseFloat(opacity) * 100);

    onQueryLayerChange(layer) {
      this.selectedQueryLayer = layer.name;
DESPRES Damien's avatar
DESPRES Damien committed
      this.baseMaps[0].layers.find((l) => l.title === layer.name).query = true;
      layer.query = true;
Florent Lavelle's avatar
Florent Lavelle committed
      const queryableLayer = baseMap.layers.filter((l) => l.queryable === true);
      // Get the names of the current layers in order.
      const currentLayersNamesInOrder = Array.from(
        document.getElementsByClassName('layer-item transition visible')
      // Create an array to put the layers in order.
      let movedLayers = [];
      for (const layerName of currentLayersNamesInOrder) {
        movedLayers.push(
          this.activeBasemap.layers.filter((el) => el.title === layerName)[0]
        );
      }
      // Remove existing layers undefined
      movedLayers = movedLayers.filter(function (x) {
        return x !== undefined;
      });
      const eventOrder = new CustomEvent('change-layers-order', {
      document.dispatchEvent(eventOrder);
      // Save the basemaps options into the localstorage
    setLocalstorageMapOptions(basemaps) {
      let mapOptions = localStorage.getItem('geocontrib-map-options') || {};
      mapOptions = mapOptions.length ? JSON.parse(mapOptions) : {};
Florent Lavelle's avatar
Florent Lavelle committed
      const project = this.$route.params.slug;
      mapOptions[project] = {
        ...mapOptions[project],
        'geocontrib-map-options',
      this.baseMaps.forEach((basemap) => {
Florent Lavelle's avatar
Florent Lavelle committed
        const element = document.getElementById(`list-${basemap.id}`);
        if (element) {
          this. sortable = new Sortable(element, {
DESPRES Damien's avatar
DESPRES Damien committed
            animation: 150,
            handle: '.layer-handle-sort', // The element that is active to drag
            ghostClass: 'blue-background-class',
            dragClass: 'white-opacity-background-class',
DESPRES Damien's avatar
DESPRES Damien committed
            onEnd: this.onlayerMove.bind(this),
          });
Florent Lavelle's avatar
Florent Lavelle committed
        } else {
          console.error(`list-${basemap.id} not found in dom`);
DESPRES Damien's avatar
DESPRES Damien committed
        }

      });
    },
    // 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 = {}) {
      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.length
        ? basemapFromLocalstorage.map((b) => b.id).sort()
        : {};
      isSameBasemaps =
        idBasemapsServer.length === idBasemapsLocalstorage.length &&
        idBasemapsServer.every(
          (value, index) => value === idBasemapsLocalstorage[index]
        );
      // For each basemap, compare the length and id values of the layers
      outer_block: {
Florent Lavelle's avatar
Florent Lavelle committed
        for (const basemapServer of basemapFromServer) {
          const idLayersServer = basemapServer.layers.map((b) => b.id).sort();
Florent Lavelle's avatar
Florent Lavelle committed
            for (const basemapLocalstorage of basemapFromLocalstorage) {
              if (basemapServer.id === basemapLocalstorage.id) {
Florent Lavelle's avatar
Florent Lavelle committed
                const idLayersLocalstorage = basemapLocalstorage.layers
                  .map((b) => b.id)
                  .sort();
                isSameLayers =
                  idLayersServer.length === idLayersLocalstorage.length &&
                  idLayersServer.every(
                    (value, index) => value === idLayersLocalstorage[index]
                  );
                if (!isSameLayers) {
                  break outer_block;
                }
        // Compare basemaps titles
        const titlesBasemapsServer = basemapFromServer
          .map((b) => b.title)
          .sort();
        const titlesBasemapsLocalstorage = basemapFromLocalstorage.length
          ? basemapFromLocalstorage.map((b) => b.title).sort()
          : {};
        isSameTitles = titlesBasemapsServer.every(
          (title, index) => title === titlesBasemapsLocalstorage[index]
        );
      return !(isSameBasemaps && isSameLayers && isSameTitles);
    },

    getQueryableLayers(baseMap) {
Florent Lavelle's avatar
Florent Lavelle committed
      const queryableLayer = baseMap.layers.filter((l) => l.queryable === true);
    addLayers(baseMap) {
      baseMap.layers.forEach((layer) => {
        var 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,);
DESPRES Damien's avatar
DESPRES Damien committed
@import "../../assets/styles/sidebar-layers.css";
.queryable-layers-dropdown {
  margin-bottom: 1em;
}
.queryable-layers-dropdown > label {
  font-weight: bold;
}
Florent Lavelle's avatar
Florent Lavelle committed
</style>