diff --git a/src/App.vue b/src/App.vue
index fee4e7ee79ac8bcce90b3bf7a412877d5de02299..5c941e33ffd3add1778c4ecff63574bfde6f0394 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -68,9 +68,6 @@ export default {
 </script>
 
 <style>
-@import "./assets/styles/base.css";
-@import "./assets/resources/semantic-ui-2.4.2/semantic.min.css";
-
 .vertical {
   flex-direction: column;
   justify-content: center;
diff --git a/src/assets/styles/sidebar-layers.css b/src/assets/styles/sidebar-layers.css
index 8ffe4dfc7c0afc5f10ba0c6aadeb25cab2f40e18..32a85c3dc477ad9b2f122216dd6d2b7f11dd19b0 100644
--- a/src/assets/styles/sidebar-layers.css
+++ b/src/assets/styles/sidebar-layers.css
@@ -142,7 +142,6 @@
 }
 
 /* Layer item */
-
 .layer-item {
   padding-bottom: 0.5rem;
 }
@@ -162,6 +161,7 @@
 
 .range-container {
   display: flex;
+  min-width: 15em; /* give space for the bubble since adding a min-width to keep its shape */
 }
 
 .range-output-bubble {
@@ -170,6 +170,8 @@
   padding: 4px 7px;
   border-radius: 40px;
   background-color: #2c3e50;
+  min-width: 2em;
+  text-align: center;
 }
 
 /* Overrides default padding of semantic-ui accordion */
diff --git a/src/components/Dropdown.vue b/src/components/Dropdown.vue
index 2e9ce2602330d50e6753bb874c8df3efa18c7e22..77291019eb380735672cf83eb69fc8f7179473a1 100644
--- a/src/components/Dropdown.vue
+++ b/src/components/Dropdown.vue
@@ -40,6 +40,7 @@
     <div :class="['menu', { 'visible transition': isOpen }]">
       <div
         v-for="(option, index) in filteredOptions || ['No results found.']"
+        :id="option.name && Array.isArray(option.name) ? option.name[0] : option.name"
         :key="option + index"
         :class="[
           filteredOptions ? 'item' : 'message',
@@ -117,7 +118,7 @@ export default {
 
   created() {
     const crypto = window.crypto || window.msCrypto;
-    var array = new Uint32Array(1);
+    const array = new Uint32Array(1);
     this.identifier = Math.floor(crypto.getRandomValues(array) * 10000);
     window.addEventListener('mousedown', this.clickOutsideDropdown);
   },
diff --git a/src/components/FeatureType/SymbologySelector.vue b/src/components/FeatureType/SymbologySelector.vue
index 29f419340b0416730f9d08a41c611d28750296c8..f058de487ce2339d98028fed2841d8d344b6e973 100644
--- a/src/components/FeatureType/SymbologySelector.vue
+++ b/src/components/FeatureType/SymbologySelector.vue
@@ -1,9 +1,9 @@
 <template>
   <div>
     <div class="three fields">
-      <div class="row-title">
+      <h4 :class="['field', {'row-title' : title == 'Symbologie par défault :'}]">
         {{ title }}
-      </div>
+      </h4>
       <div class="required inline field">
         <label :for="form.color.id_for_label">{{ form.color.label }}</label>
         <input
@@ -14,25 +14,26 @@
           :name="form.color.html_name"
         >
       </div>
-      <!-- <div class="required inline field">
-        <label>Symbole</label>
-        <button
-          class="ui icon button picker-button"
-          type="button"
-          @click="openIconSelectionModal"
-        >
-          <font-awesome-icon
-            :icon="['fas', form.icon]"
-            :style="{ color: form.color.value || '#000000' }"
-            class="icon alt"
-          />
-        </button>
-      </div> -->
+      <div v-if="geomType === 'polygon' || title !== 'Symbologie par défault :'">
+        <label>Opacité &nbsp;<span>(%)</span></label>
+        <div class="range-container">
+          <input
+            id="opacity"
+            v-model="form.opacity"
+            type="range"
+            min="0"
+            max="1"
+            step="0.01"
+          >
+          <output class="range-output-bubble">
+            {{ getOpacity(form.opacity) }}
+          </output>
+        </div>
+      </div>
     </div>
     <div
       ref="iconsPickerModal"
-      :class="isIconPickerModalOpen ? 'active' : ''"
-      class="ui dimmer modal transition"
+      :class="['ui dimmer modal transition', { active: isIconPickerModalOpen }]"
     >
       <div class="header">
         Sélectionnez le symbole pour ce type de signalement :
@@ -41,8 +42,7 @@
         <div
           v-for="icon of iconsNamesList"
           :key="icon"
-          :class="form.icon === icon ? 'active' : ''"
-          class="icon-container"
+          :class="['icon-container', { active: form.icon === icon }]"
           @click="selectIcon(icon)"
         >
           <i
@@ -83,6 +83,10 @@ export default {
       type: String,
       default: 'circle'
     },
+    initOpacity: {
+      type: String,
+      default: '1'
+    },
     geomType: {
       type: String,
       default: 'Point'
@@ -104,6 +108,7 @@ export default {
           html_name: 'couleur',
           value: '#000000',
         },
+        opacity: '0.5',
       }
     };
   },
@@ -125,6 +130,9 @@ export default {
     if (this.initIcon) {
       this.form.icon = this.initIcon;
     }
+    if (this.initOpacity) {
+      this.form.opacity = this.initOpacity;
+    }
     this.$emit('set', {
       name: this.title,
       value: this.form
@@ -138,7 +146,11 @@ export default {
 
     selectIcon(icon) {
       this.form.icon = icon;
-    }
+    },
+
+    getOpacity(opacity) {
+      return Math.round(parseFloat(opacity) * 100);
+    },
   }
 };
 </script>
@@ -154,11 +166,16 @@ export default {
 .row-title {
   display: inline;
   font-size: 1.4em;
+  font-weight: normal;
   width: 33%;
   text-align: left;
   margin-left: 0.5em;
 }
 
+.default {
+  margin-bottom: 2rem;
+}
+
 #couleur {
   width: 66%;
   cursor: pointer;
diff --git a/src/components/Map/SidebarLayers.vue b/src/components/Map/SidebarLayers.vue
index d53fa84e5254f2741fc15428f9a1a87d923c013c..c2faef528133167542f4fceb9e8b418b4c04f3bc 100644
--- a/src/components/Map/SidebarLayers.vue
+++ b/src/components/Map/SidebarLayers.vue
@@ -380,7 +380,6 @@ export default {
 </script>
 
 <style>
-@import "../../assets/styles/sidebar-layers.css";
 .queryable-layers-dropdown {
   margin-bottom: 1em;
 }
diff --git a/src/components/Project/Detail/ProjectFeatureTypes.vue b/src/components/Project/Detail/ProjectFeatureTypes.vue
index 81cfd78cd9125a44120f088e849d5c02eeff45de..7cb7968533a7c1f3a17d9d919486a52e05b3075b 100644
--- a/src/components/Project/Detail/ProjectFeatureTypes.vue
+++ b/src/components/Project/Detail/ProjectFeatureTypes.vue
@@ -25,6 +25,7 @@
       </div>
       <div
         v-for="(type, index) in feature_types"
+        :id="type.title"
         :key="type.title + '-' + index"
         class="item"
       >
diff --git a/src/main.js b/src/main.js
index b5349516c720d93548c7d28f154d89092d7ebd3e..4bf957d27033d17d20e97b20b73a92e60d13335f 100644
--- a/src/main.js
+++ b/src/main.js
@@ -5,10 +5,13 @@ import App from './App.vue';
 import './registerServiceWorker';
 import router from '@/router';
 import store from '@/store';
+import './assets/styles/base.css';
+import './assets/resources/semantic-ui-2.4.2/semantic.min.css';
 import '@fortawesome/fontawesome-free/css/all.css';
 import '@fortawesome/fontawesome-free/js/all.js';
 import 'ol/ol.css';
 import '@/assets/styles/openlayers-custom.css';
+import '@/assets/styles/sidebar-layers.css';
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { fas } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
diff --git a/src/services/map-service.js b/src/services/map-service.js
index 2c253118ea88805b72957f1a4c8255e4baf8c9b4..87156fe6e52733ddd58cc98706b0da17067e0666 100644
--- a/src/services/map-service.js
+++ b/src/services/map-service.js
@@ -113,7 +113,6 @@ const mapService = {
   
   addRouterToPopup(featureTypeSlug, featureId) {
     function goToFeatureDetail() {
-      console.log(featureTypeSlug, featureId);
       router.push({
         name: 'details-signalement',
         params: {
@@ -329,15 +328,29 @@ const mapService = {
     this.addLayers(layers);
   },
 
-  retrieveFeatureColor: function (featureType, properties) {
-    const colorsStyle = featureType.colors_style;
-    if (featureType && colorsStyle && colorsStyle.custom_field_name) {
-      const currentValue = properties[colorsStyle.custom_field_name];
-      const colorStyle = colorsStyle.colors[currentValue];
-      return colorStyle ? colorStyle : featureType.color;
-    } else {
-      return featureType.color;
+  retrieveFeatureStyle: function (featureType, properties) {
+    const { colors_style, customfield_set } = featureType;
+    let { color, opacity } = featureType;
+
+    if (colors_style && colors_style.custom_field_name && customfield_set) {
+      const fieldType = customfield_set.find((el) => el.name === colors_style.custom_field_name).field_type;
+      const currentValue = properties[colors_style.custom_field_name];
+
+      if (currentValue) {
+        switch (fieldType) {
+        case 'list' :
+          color = colors_style.colors[currentValue];
+          opacity = colors_style.opacities[currentValue];
+          break;
+        case 'char': //* if the custom field is supposed to be a string
+          //* check if its current value is empty or not, to select a color | https://redmine.neogeo.fr/issues/14048
+          color = colors_style.value.colors[currentValue ? 'Non vide' : 'Vide'];
+          opacity = colors_style.value.opacities[currentValue ? 'Non vide' : 'Vide'];
+          break;
+        }
+      }
     }
+    return { color, opacity };
   },
 
   addVectorTileLayer: function (url, projectId, featureTypes, formFilters) {
@@ -367,96 +380,93 @@ const mapService = {
     const properties = feature.getProperties();
     let featureType;
     // GeoJSON
-    if(properties.feature_type){
+    if(properties && properties.feature_type){
       featureType = featureTypes
         .find((ft) => ft.slug === (properties.feature_type.slug || properties.feature_type));
     } else { //MVT
       featureType = featureTypes.find((x) => x.slug.split('-')[0] === '' + properties.feature_type_id);
     }
-    const color = this.retrieveFeatureColor(featureType, properties);
-    const colorValue =
+
+    if (featureType) {
+      const { color, opacity } = this.retrieveFeatureStyle(featureType, properties);
+      const colorValue =
       color.value && color.value.length ?
         color.value : typeof color === 'string' && color.length ?
           color : '#000000';
 
-    const rgbaColor = asArray(colorValue);
-    rgbaColor[3] = 0.5;//opacity
-    const hiddenStyle = new Style();
-
-    const defaultStyle = new Style(
-      {
-        image: new Circle({
-          fill: new Fill(
-            {
-              color: rgbaColor,
-            },
-          ),
+      const rgbaColor = asArray(colorValue);
+      rgbaColor[3] = opacity || 0.5;//opacity
+
+      const defaultStyle = new Style(
+        {
+          image: new Circle({
+            fill: new Fill(
+              {
+                color: rgbaColor,
+              },
+            ),
+            stroke: new Stroke(
+              {
+                color: colorValue,
+                width: 2,
+              },
+            ),
+            radius: 5,
+          }),
           stroke: new Stroke(
             {
               color: colorValue,
               width: 2,
             },
           ),
-          radius: 5,
-        }),
-        stroke: new Stroke(
-          {
-            color: colorValue,
-            width: 2,
-          },
-        ),
-        fill: new Fill(
-          {
-            color: rgbaColor,
-          },
-        ),
-      },
-    );
-
-    // Filtre sur le feature type
-    if(formFilters){
-      if (formFilters.type && formFilters.type.selected) {
-        if (featureType.title !== formFilters.type.selected) {
-          return hiddenStyle;
+          fill: new Fill(
+            {
+              color: rgbaColor,
+            },
+          ),
+        },
+      );
+
+      const hiddenStyle = new Style(); // hide the feature to apply filters
+      // Filtre sur le feature type
+      if(formFilters){
+        if (formFilters.type && formFilters.type.selected) {
+          if (featureType.title !== formFilters.type.selected) {
+            return hiddenStyle;
+          }
         }
-      }
-      // Filtre sur le statut
-      if (formFilters.status && formFilters.status.selected.value) {
-        if (properties.status !== formFilters.status.selected.value) {
-          return hiddenStyle;
+        // Filtre sur le statut
+        if (formFilters.status && formFilters.status.selected.value) {
+          if (properties.status !== formFilters.status.selected.value) {
+            return hiddenStyle;
+          }
         }
-      }
-      // Filtre sur le titre
-      if (formFilters.title) {
-        if (!properties.title.toLowerCase().includes(formFilters.title.toLowerCase())) {
-          return hiddenStyle;
+        // Filtre sur le titre
+        if (formFilters.title) {
+          if (!properties.title.toLowerCase().includes(formFilters.title.toLowerCase())) {
+            return hiddenStyle;
+          }
         }
       }
+      return defaultStyle;
+    } else {
+      console.error('No corresponding featureType found.');
+      return;
     }
-
-    return defaultStyle;
   },
 
   addFeatures: function (features, filter, featureTypes, addToMap = true) {
-    console.log(addToMap);
+    console.log('addToMap', addToMap);
     const drawSource = new VectorSource();
     let retour;
     // TODO verifier utilité de cette boucle et remplacer par readFeatures plutot
     features.forEach((feature) => {
-      retour = new GeoJSON().readFeature(feature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' }, featureTypes);
-      drawSource.addFeature(retour);
-      // const featureProperties = feature.properties ? feature.properties : feature;
-      // const featureType = featureTypes
-      //   .find((ft) => ft.slug === (featureProperties.feature_type.slug || featureProperties.feature_type));
-      // let filters = [];
-      // if (filter) {
-      //   const typeCheck = filter.featureType && featureProperties.feature_type.slug === filter.featureType;
-      //   const statusCheck = filter.featureStatus && featureProperties.status.value === filter.featureStatus;
-      //   const titleCheck = filter.featureTitle && featureProperties.title.includes(filter.featureTitle);
-      //   filters = [typeCheck, statusCheck, titleCheck];
-      // }
-      // console.log(featureType, filters);
-
+      try {
+        retour = new GeoJSON().readFeature(feature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' }, featureTypes);
+        drawSource.addFeature(retour);
+      } catch (err) {
+        console.error(err);
+      }
     });
     const styleFunction = (feature) => this.getStyle(feature, featureTypes, filter);
     const olLayer = new VectorLayer({
@@ -493,7 +503,7 @@ const mapService = {
         feature_type = feature.getProperties().feature_type ||
         featureTypes.find((x) => x.slug.split('-')[0] === '' + feature.getProperties().feature_type_id);
       }
-    } else { //? TPS: I couldn't find when this code is used, is this still in use ?
+    } else { //? TPD: I couldn't find when this code is used, is this still in use ?
       status = feature.status;
       if (status) status = status.name;
       date_maj = feature.updated_on;
@@ -538,7 +548,7 @@ const mapService = {
                   </div>
                   ${author}
                   `;
-    const featureId = feature.getProperties ? feature.getProperties().feature_id : feature.id;
+    const featureId = feature.getProperties ? feature.getProperties().feature_id || feature.getId() : feature.id; //* feature.id was used with leaflet, with ol feature.getId replace it, but keeping it as fallback can prevent regression
     return { html, feature_type, featureId };
   },
 
diff --git a/src/views/FeatureType/FeatureTypeSymbology.vue b/src/views/FeatureType/FeatureTypeSymbology.vue
index 39ac0a9915a6a98059e2b3c4518b20d1ea53b316..148221bec18bd9dfd5ceea3925fcd5d48da1fc23 100644
--- a/src/views/FeatureType/FeatureTypeSymbology.vue
+++ b/src/views/FeatureType/FeatureTypeSymbology.vue
@@ -34,6 +34,10 @@
         <p>{{ success }}</p>
       </div>
     </div>
+    <h1 v-if="project && feature_type">
+      Éditer la symbologie du type de signalement "{{ feature_type.title }}" pour le
+      projet "{{ project.title }}"
+    </h1>
     <div class="fourteen wide column">
       <form
         id="form-symbology-edit"
@@ -42,48 +46,40 @@
         enctype="multipart/form-data"
         class="ui form"
       >
-        <h1 v-if="project && feature_type">
-          Éditer la symbologie du type de signalement "{{ feature_type.title }}" pour le
-          projet "{{ project.title }}"
-        </h1>
         <SymbologySelector
           v-if="feature_type"
+          class="default"
           :init-color="feature_type.color"
           :init-icon="feature_type.icon"
+          :init-opacity="feature_type.opacity"
           :geom-type="feature_type.geom_type"
           @set="setDefaultStyle"
         />
+        <div class="ui divider" />
         <div
-          v-if="
-            feature_type &&
-              feature_type.customfield_set.length > 0 &&
-              feature_type.customfield_set.some(el => el.field_type === 'list')
-          "
+          v-if="customizableFields.length > 0"
+          class="field"
         >
-          <div class="ui divider" />
           <label
             id="customfield-select-label"
             for="customfield-select"
           >
-            Personnaliser la symbologie d'une liste de valeurs:
+            Champ de personnalisation de la symbologie:
           </label>
-          <select
-            id="customfield-select"
-            v-model="selectedCustomfield"
-            class="ui dropdown"
-          >
-            <option
-              v-for="customfieldList of feature_type.customfield_set.filter(el => el.field_type === 'list')"
-              :key="customfieldList.name"
-              :value="customfieldList.name"
-            >
-              {{ customfieldList.label }}
-            </option>
-          </select>
+          <span id="custom_types-dropdown">
+            <Dropdown
+              :options="customizableFields"
+              :selected="selectedCustomfield"
+              :selection.sync="selectedCustomfield"
+            />
+          </span>
         </div>
-        <div v-if="selectedCustomfield">
+        <div
+          v-if="selectedCustomfield"
+          class="field"
+        >
           <div 
-            v-for="option of feature_type.customfield_set.find(el => el.name === selectedCustomfield).options"
+            v-for="option of selectedFieldOptions"
             :key="option"
           >
             <SymbologySelector
@@ -98,13 +94,15 @@
                 feature_type.colors_style.value.icons[option] :
                 null
               "
+              :init-opacity="getOpacity(feature_type, option)"
               :geom-type="feature_type.customfield_set.geomType"
               @set="setColorsStyle"
             />
           </div>
+          <div class="ui divider" />
         </div>
-        <div class="ui divider" />
         <button
+          id="save-symbology"
           class="ui teal icon button margin-25"
           type="button"
           :disabled="!canSaveSymbology"
@@ -127,12 +125,15 @@ import { isEqual } from 'lodash';
 import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
 
 import SymbologySelector from '@/components/FeatureType/SymbologySelector.vue';
+import Dropdown from '@/components/Dropdown.vue';
+
 
 export default {
   name: 'FeatureTypeSymbology',
 
   components: {
-    SymbologySelector
+    SymbologySelector,
+    Dropdown,
   },
 
   data() {
@@ -140,7 +141,6 @@ export default {
       loading: false,
       error: null,
       success: null,
-      selectedCustomfield: null,
       form: {
         color: '#000000',
         icon: 'circle',
@@ -148,10 +148,12 @@ export default {
           fields: [],
           colors: {},
           icons: {},
+          opacities: {},
           custom_field_name: '',
           value: {
             colors: {},
-            icons: {}
+            icons: {},
+            opacities: {},
           }
         },
       },
@@ -170,12 +172,40 @@ export default {
     ...mapGetters('feature-type', [
       'feature_type'
     ]),
+    customizableFields() {
+      if (this.feature_type) {
+        let options = this.feature_type.customfield_set.filter(el => el.field_type === 'list' || el.field_type === 'char');
+        options = options.map((el) => {
+          return { name: [el.name, `(${el.field_type === 'list' ? 'Liste de valeurs' : 'Chaîne de caractères'})`], value: el };
+        });
+        return options;
+      }
+      return [];
+    },
+    selectedFieldOptions() {
+      if (this.selectedCustomfield) {
+        const customFieldSet = this.feature_type.customfield_set.find(el => el.name === this.selectedCustomfield);
+        if (customFieldSet.options.length > 0) {
+          return customFieldSet.options;
+        } else if (customFieldSet.field_type === 'char') {
+          return ['Vide', 'Non vide'];
+        }
+      }
+      return [];
+    },
+    selectedCustomfield: {
+      get() {
+        return this.form.colors_style.custom_field_name;
+      },
+      set(newValue) {
+        if (newValue && newValue.value) {
+          this.form.colors_style.custom_field_name = newValue.value.name;
+        }
+      }
+    }
   },
 
   watch: {
-    selectedCustomfield(newValue) {
-      this.form.colors_style.custom_field_name = newValue;
-    },
     feature_type(newValue) {
       if (newValue) {
         // Init form
@@ -238,12 +268,15 @@ export default {
     ]),
 
     initForm() {
-      this.form.color = JSON.parse(JSON.stringify(this.feature_type.color));
-      this.form.icon = JSON.parse(JSON.stringify(this.feature_type.icon));
+      this.form.color = JSON.parse(JSON.stringify(this.feature_type.color)); //? wouldn't be better to use lodash: https://medium.com/@pmzubar/why-json-parse-json-stringify-is-a-bad-practice-to-clone-an-object-in-javascript-b28ac5e36521
+      this.form.icon = JSON.parse(JSON.stringify(this.feature_type.icon)); //? since the library is already imported ?
       this.form.colors_style = {
         ...this.form.colors_style,
         ...JSON.parse(JSON.stringify(this.feature_type.colors_style))
       };
+      if (!this.form.colors_style.value['opacities']) { //* if the opacity values were never setted (would be better to find out why)
+        this.form.colors_style.value['opacities'] = {};
+      }
       if (this.feature_type.colors_style && Object.keys(this.feature_type.colors_style.colors).length > 0) {
         this.selectedCustomfield =
           this.feature_type.customfield_set.find(
@@ -253,17 +286,21 @@ export default {
     },
 
     setDefaultStyle(e) {
-      const value  = e.value;
-      this.form.color = value.color.value;
-      this.form.icon = value.icon;
+      const { color, icon, opacity } = e.value;
+      this.form.color = color.value;
+      this.form.icon = icon;
+      this.form.opacity = opacity;
     },
 
     setColorsStyle(e) {
       const { name, value } = e;
-      this.form.colors_style.colors[name] = value.color;
-      this.form.colors_style.icons[name] = value.icon;
-      this.form.colors_style.value.colors[name] = value.color;
-      this.form.colors_style.value.icons[name] = value.icon;
+      const { color, icon, opacity } = value;
+      this.form.colors_style.colors[name] = color;
+      this.form.colors_style.icons[name] = icon;
+      this.form.colors_style.opacities[name] = opacity;
+      this.form.colors_style.value.colors[name] = color;
+      this.form.colors_style.value.icons[name] = icon;
+      this.form.colors_style.value.opacities[name] = opacity; //? why do we need to duplicate values ? for MVT ?
     },
 
     sendFeatureSymbology() {
@@ -292,6 +329,13 @@ export default {
           console.error(err);
           this.loading = false;
         });
+    },
+
+    getOpacity(feature_type, optionName) {
+      if (feature_type.colors_style.value && feature_type.colors_style.value.opacities) {
+        return feature_type.colors_style.value.opacities[optionName];
+      }
+      return null;
     }
   }
 };
@@ -305,14 +349,14 @@ h1 {
 
 form {
   text-align: left;
-
   #customfield-select-label {
-    cursor: pointer;
+    //cursor: pointer;
     font-weight: 600;
     font-size: 1.1em;
   }
-  #customfield-select {
-    width: 50% !important;
+
+  #custom_types-dropdown > .dropdown {
+    width: 50%;
   }
 
 }
diff --git a/src/views/Project/FeaturesListAndMap.vue b/src/views/Project/FeaturesListAndMap.vue
index 0deafc36a1d5e6df32b7f50788fc95fb7609cb60..5bb4915fd29e6a1c662930de4613b6dcfca42f39 100644
--- a/src/views/Project/FeaturesListAndMap.vue
+++ b/src/views/Project/FeaturesListAndMap.vue
@@ -351,7 +351,7 @@ export default {
       // --------- End sidebar events ----------
       setTimeout(() => {
         const project_id = this.projectSlug.split('-')[0];
-        const mvtUrl = `${this.API_BASE_URL}features.mvt/`;
+        const mvtUrl = `${this.API_BASE_URL}features.mvt`;
         mapService.addVectorTileLayer(
           mvtUrl,
           project_id,