<template>
<div v-frag>
<div class="fourteen wide column">
<h1 v-if="feature && currentRouteName === 'editer-signalement'">
Mise à jour du signalement "{{ feature.title || feature.feature_id }}"
</h1>
<h1
v-else-if="feature_type && currentRouteName === 'ajouter-signalement'"
>
Création d'un signalement <small>[{{ feature_type.title }}]</small>
</h1>
<form
id="form-feature-edit"
action=""
method="post"
enctype="multipart/form-data"
class="ui form"
>
<!-- Feature Fields -->
<div class="two fields">
<div :class="field_title">
<label :for="form.title.id_for_label">{{ form.title.label }}</label>
<input
:id="form.title.id_for_label"
v-model="form.title.value"
type="text"
required
:maxlength="form.title.field.max_length"
:name="form.title.html_name"
@blur="updateStore"
>
<ul
id="errorlist-title"
class="errorlist"
>
<li
v-for="error in form.title.errors"
:key="error"
>
{{ error }}
</li>
</ul>
</div>
<div class="required field">
<label :for="form.status.id_for_label">{{
form.status.label
}}</label>
<Dropdown
:options="allowedStatusChoices"
:selected="selected_status.name"
:selection.sync="selected_status"
/>
</div>
</div>
<div class="field">
<label :for="form.description.id_for_label">{{
form.description.label
}}</label>
<textarea
v-model="form.description.value"
:name="form.description.html_name"
rows="5"
@blur="updateStore"
/>
</div>
<!-- Geom Field -->
<div class="field">
<label :for="form.geom.id_for_label">{{ form.geom.label }}</label>
<!-- Import GeoImage -->
<div
v-if="feature_type && feature_type.geom_type === 'point'"
v-frag
>
<p v-if="isOffline() !== true">
<button
id="add-geo-image"
type="button"
class="ui compact button"
@click="toggleGeoRefModal"
>
<i class="file image icon" />Importer une image géoréférencée
</button>
Vous pouvez utiliser une image géoréférencée pour localiser le
signalement.
</p>
<div
v-if="showGeoRef"
class="ui dimmer modals page transition visible active"
style="display: flex !important"
>
<div
class="ui mini modal transition visible active"
style="display: block !important"
>
<i
class="close icon"
@click="toggleGeoRefModal"
/>
<div class="content">
<h3>Importer une image géoréférencée</h3>
<form
id="form-geo-image"
class="ui form"
enctype="multipart/form-data"
>
<p>
Attention, si vous avez déjà saisi une géométrie, celle
issue de l'image importée l'écrasera.
</p>
<div class="field georef-btn">
<label>Image (png ou jpeg)</label>
<label
class="ui icon button"
for="image_file"
>
<i class="file icon" />
<span class="label">{{ geoRefFileLabel }}</span>
</label>
<input
id="image_file"
ref="file"
type="file"
accept="image/jpeg, image/png"
style="display: none"
name="image_file"
class="image_file"
@change="handleFileUpload"
>
<ul
v-if="erreurUploadMessage"
class="errorlist"
>
<li>
{{ erreurUploadMessage }}
</li>
</ul>
</div>
<button
id="get-geom-from-image-file"
type="button"
:class="[
'ui compact button',
file && !erreurUploadMessage ? 'green' : 'disabled',
{ red: erreurUploadMessage },
]"
@click="georeferencement"
>
<i class="plus icon" />
Importer
</button>
</form>
</div>
</div>
</div>
<p v-if="showGeoPositionBtn">
<button
id="create-point-geoposition"
type="button"
class="ui compact button"
@click="create_point_geoposition"
>
<i class="ui map marker alternate icon" />Positionner le
signalement à partir de votre géolocalisation
</button>
</p>
<span
v-if="erreurGeolocalisationMessage"
id="erreur-geolocalisation"
>
<div class="ui negative message">
<div class="header">
Une erreur est survenue avec la fonctionnalité de
géolocalisation
</div>
<p id="erreur-geolocalisation-message">
{{ erreurGeolocalisationMessage }}
</p>
</div>
<br>
</span>
</div>
<ul
id="errorlist-geom"
class="errorlist"
>
<li
v-for="error in form.geom.errors"
:key="error"
>
{{ error }}
</li>
</ul>
<!-- Map -->
<input
:id="form.geom.id_for_label"
v-model="form.geom.value"
type="hidden"
:name="form.geom.html_name"
@blur="updateStore"
>
<div
class="ui tab active map-container"
data-tab="map"
>
<div
id="map"
ref="map"
/>
<SidebarLayers v-if="basemaps && map" />
</div>
</div>
<!-- Extra Fields -->
<div class="ui horizontal divider">
DONNÉES MÉTIER
</div>
<div
v-for="(field, index) in orderedCustomFields"
:key="field.field_type + index"
class="field"
>
<FeatureExtraForm :field="field" />
{{ field.errors }}
</div>
<!-- Pièces jointes -->
<div v-if="isOffline() !== true">
<div class="ui horizontal divider">
PIÈCES JOINTES
</div>
<div
v-if="isOffline() !== true"
id="formsets-attachment"
>
<FeatureAttachmentForm
v-for="attachForm in attachmentFormset"
:key="attachForm.dataKey"
ref="attachementForm"
:attachment-form="attachForm"
/>
</div>
<button
id="add-attachment"
type="button"
class="ui compact basic button button-hover-green"
@click="add_attachement_formset"
>
<i class="ui plus icon" />Ajouter une pièce jointe
</button>
</div>
<!-- Signalements liés -->
<div v-if="isOffline() !== true">
<div class="ui horizontal divider">
SIGNALEMENTS LIÉS
</div>
<div id="formsets-link">
<FeatureLinkedForm
v-for="linkForm in linkedFormset"
:key="linkForm.dataKey"
ref="linkedForm"
:linked-form="linkForm"
/>
</div>
<button
id="add-link"
type="button"
class="ui compact basic button button-hover-green"
@click="add_linked_formset"
>
<i class="ui plus icon" />Ajouter une liaison
</button>
</div>
<div class="ui divider" />
<button
type="button"
class="ui teal icon button"
@click="postForm"
>
<i class="white save icon" /> Enregistrer les changements
</button>
</form>
</div>
</div>
</template>
<script>
import frag from 'vue-frag';
import { mapState, mapGetters } from 'vuex';
import FeatureAttachmentForm from '@/components/feature/FeatureAttachmentForm';
import FeatureLinkedForm from '@/components/feature/FeatureLinkedForm';
import FeatureExtraForm from '@/components/feature/FeatureExtraForm';
import Dropdown from '@/components/Dropdown.vue';
import SidebarLayers from '@/components/map-layers/SidebarLayers';
import featureAPI from '@/services/feature-api';
import L from 'leaflet';
import 'leaflet-draw';
import { mapUtil } from '@/assets/js/map-util.js';
import axios from '@/axios-client.js';
import flip from '@turf/flip';
export default {
name: 'FeatureEdit',
directives: {
frag,
},
components: {
FeatureAttachmentForm,
FeatureLinkedForm,
Dropdown,
SidebarLayers,
FeatureExtraForm,
},
data() {
return {
map: null,
baseUrl: this.$store.state.configuration.BASE_URL,
file: null,
showGeoRef: false,
showGeoPositionBtn: true,
erreurGeolocalisationMessage: null,
erreurUploadMessage: null,
attachmentDataKey: 0,
linkedDataKey: 0,
form: {
title: {
errors: [],
id_for_label: 'name',
field: {
max_length: 30,
},
html_name: 'name',
label: 'Nom',
value: '',
},
status: {
id_for_label: 'status',
html_name: 'status',
label: 'Statut',
value: {
value: 'draft',
name: 'Brouillon',
},
},
description: {
errors: [],
id_for_label: 'description',
html_name: 'description',
label: 'Description',
value: '',
},
geom: {
errors: [],
label: 'Localisation',
value: null,
},
},
};
},
computed: {
...mapGetters(['permissions']),
...mapGetters('feature_type', ['feature_type']),
...mapState(['user', 'USER_LEVEL_PROJECTS']),
...mapState('projects', ['project']),
...mapState('map', ['basemaps']),
...mapState('feature', [
'attachmentFormset',
'linkedFormset',
'features',
'extra_form',
'statusChoices',
]),
field_title() {
if (this.feature_type) {
if (this.feature_type.title_optional) {
return 'field';
}
}
return 'required field';
},
currentRouteName() {
return this.$route.name;
},
feature() {
return this.$store.state.feature.current_feature;
},
orderedCustomFields() {
return [...this.extra_form].sort((a, b) => a.position - b.position);
},
geoRefFileLabel() {
if (this.file) {
return this.file.name;
}
return 'Sélectionner une image ...';
},
selected_status: {
get() {
return this.form.status.value;
},
set(newValue) {
this.form.status.value = newValue;
this.updateStore();
},
},
allowedStatusChoices() {
if (this.project) {
const isModerate = this.project.moderation;
const userStatus = this.USER_LEVEL_PROJECTS[this.project.slug];
const isOwnFeature = this.feature
? this.feature.creator === this.user.id //* prevent undefined feature
: false; //* si le contributeur est l'auteur du signalement
if (
//* si admin, modérateur ou super contributeur, statuts toujours disponible: Brouillon, Publié, Archivé
userStatus === 'Modérateur' ||
userStatus === 'Administrateur projet' ||
(userStatus === 'Super Contributeur' && !isModerate)
) {
return this.statusChoices.filter((el) => el.value !== 'pending');
} else if (userStatus === 'Super Contributeur' && isModerate) {
return this.statusChoices.filter(
(el) => el.value === 'draft' || el.value === 'pending'
);
} else if (userStatus === 'Contributeur') {
//* cas particuliers du contributeur
if (
this.currentRouteName === 'ajouter-signalement' ||
!isOwnFeature
) {
//* même cas à l'ajout d'une feature ou si feature n'a pas été créé par le contributeur
return isModerate
? this.statusChoices.filter(
(el) => el.value === 'draft' || el.value === 'pending'
)
: this.statusChoices.filter(
(el) => el.value === 'draft' || el.value === 'published'
);
} else {
//* à l'édition d'une feature et si le contributeur est l'auteur de la feature
return isModerate
? this.statusChoices.filter(
(el) => el.value !== 'published' //* toutes sauf "Publié"
)
: this.statusChoices.filter(
(el) => el.value !== 'pending' //* toutes sauf "En cours de publication"
);
}
}
}
return [];
},
},
created() {
this.$store.commit(
'feature_type/SET_CURRENT_FEATURE_TYPE_SLUG',
this.$route.params.slug_type_signal
);
//* empty previous feature data, not emptying by itself since it doesn't update by itself anymore
if (this.currentRouteName === 'ajouter-signalement') {
this.$store.commit('feature/SET_CURRENT_FEATURE', []);
}
if (this.$route.params.slug_signal) {
this.getFeatureAttachments();
this.getLinkedFeatures();
}
},
mounted() {
let promises = [
this.$store.dispatch('projects/GET_PROJECT_INFO', this.$route.params.slug),
this.$store.dispatch('projects/GET_PROJECT', this.$route.params.slug),
];
if (this.$route.params.slug_signal) {
promises.push(
this.$store.dispatch('feature/GET_PROJECT_FEATURE', {
project_slug: this.$route.params.slug,
feature_id: this.$route.params.slug_signal,
})
);
}
Promise.all(promises).then(() => {
this.initForm();
this.initMap();
this.onFeatureTypeLoaded();
this.initExtraForms(this.feature);
setTimeout(
function () {
mapUtil.addGeocoders(this.$store.state.configuration);
}.bind(this),
1000
);
});
},
destroyed() {
//* be sure that previous Formset have been cleared for creation
this.$store.commit('feature/CLEAR_ATTACHMENT_FORM');
this.$store.commit('feature/CLEAR_LINKED_FORM');
this.$store.commit('feature/CLEAR_EXTRA_FORM');
},
methods: {
isOffline() {
return navigator.onLine == false;
},
initForm() {
if (this.currentRouteName === 'editer-signalement') {
for (let key in this.feature) {
if (key && this.form[key]) {
if (key === 'status') {
const value = this.feature[key];
this.form[key].value = this.statusChoices.find(
(key) => key.value === value
);
} else {
this.form[key].value = this.feature[key];
}
}
}
this.updateStore();
}
},
create_point_geoposition() {
function success(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
var layer = L.circleMarker([latitude, longitude]);
this.add_layer_call_back(layer);
this.map.setView([latitude, longitude]);
}
function error(err) {
this.erreurGeolocalisationMessage = err.message;
if (err.message === 'User denied geolocation prompt') {
this.erreurGeolocalisationMessage =
"La géolocalisation a été désactivée par l'utilisateur";
}
}
this.erreurGeolocalisationMessage = null;
if (!navigator.geolocation) {
this.erreurGeolocalisationMessage =
"La géolocalisation n'est pas supportée par votre navigateur.";
} else {
navigator.geolocation.getCurrentPosition(
success.bind(this),
error.bind(this)
);
}
},
toggleGeoRefModal() {
if (this.showGeoRef) {
//* when popup closes, empty form
this.erreurUploadMessage = '';
this.file = null;
}
this.showGeoRef = !this.showGeoRef;
},
handleFileUpload() {
this.erreurUploadMessage = '';
this.file = this.$refs.file.files[0];
},
georeferencement() {
const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}exif-geom-reader/`;
let formData = new FormData();
formData.append('image_file', this.file);
axios
.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then((response) => {
if (response.data.geom.indexOf('POINT') >= 0) {
let regexp = /POINT\s\((.*)\s(.*)\)/;
let arr = regexp.exec(response.data.geom);
let json = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [parseFloat(arr[1]), parseFloat(arr[2])],
},
properties: {},
};
this.updateMap(json);
this.updateGeomField(json);
// Set Attachment
this.addAttachment({
title: 'Localisation',
info: '',
id: 'loc',
attachment_file: this.file.name,
fileToImport: this.file,
});
}
})
.catch((error) => {
console.error({ error });
if (error && error.response && error.response) {
this.erreurUploadMessage = error.response.data.error;
} else {
this.erreurUploadMessage =
"Une erreur est survenue pendant l'import de l'image géoréférencée";
}
});
},
initExtraForms(feature) {
function findCurrentValue(label) {
const field = feature.feature_data.find((el) => el.label === label);
return field ? field.value : null;
}
let extraForm = this.feature_type.customfield_set.map((field) => {
return {
...field,
//* add value field to extra forms from feature_type and existing values if feature is defined
value:
feature && feature.feature_data
? findCurrentValue(field.label)
: null,
};
});
this.$store.commit('feature/SET_EXTRA_FORM', extraForm);
},
add_attachement_formset() {
this.$store.commit('feature/ADD_ATTACHMENT_FORM', {
dataKey: this.attachmentDataKey,
}); // * create an object with the counter in store
this.attachmentDataKey += 1; // * increment counter for key in v-for
},
addAttachment(attachment) {
this.$store.commit('feature/ADD_ATTACHMENT_FORM', {
dataKey: this.attachmentDataKey,
title: attachment.title,
attachment_file: attachment.attachment_file,
info: attachment.info,
fileToImport: attachment.fileToImport,
id: attachment.id,
});
this.attachmentDataKey += 1;
},
addExistingAttachementFormset(attachementFormset) {
for (const attachment of attachementFormset) {
this.addAttachment(attachment);
}
},
add_linked_formset() {
this.$store.commit('feature/ADD_LINKED_FORM', {
dataKey: this.linkedDataKey,
}); // * create an object with the counter in store
this.linkedDataKey += 1; // * increment counter for key in v-for
},
addExistingLinkedFormset(linkedFormset) {
for (const linked of linkedFormset) {
this.$store.commit('feature/ADD_LINKED_FORM', {
dataKey: this.linkedDataKey,
relation_type: linked.relation_type,
feature_to: linked.feature_to,
});
this.linkedDataKey += 1;
}
},
updateStore() {
this.$store.commit('feature/UPDATE_FORM', {
title: this.form.title.value,
status: this.form.status.value,
description: this.form.description,
geometry: this.form.geom.value,
feature_id: this.feature ? this.feature.feature_id : '',
});
},
checkFormTitle() {
if (this.form.title.value) {
this.form.title.errors = [];
return true;
} else if (
!this.form.title.errors.includes('Veuillez compléter ce champ.')
) {
this.form.title.errors.push('Veuillez compléter ce champ.');
document
.getElementById('errorlist-title')
.scrollIntoView({ block: 'end', inline: 'nearest' });
}
return false;
},
checkFormGeom() {
if (this.form.geom.value) {
this.form.geom.errors = [];
return true;
} else if (
!this.form.geom.errors.includes('Valeur géométrique non valide.')
) {
this.form.geom.errors.push('Valeur géométrique non valide.');
document
.getElementById('errorlist-geom')
.scrollIntoView({ block: 'end', inline: 'nearest' });
}
return false;
},
checkAddedForm() {
let isValid = true; //* fallback if all customForms returned true
if (this.$refs.attachementForm) {
for (const attachementForm of this.$refs.attachementForm) {
if (attachementForm.checkForm() === false) {
isValid = false;
}
}
}
if (this.$refs.linkedForm) {
for (const linkedForm of this.$refs.linkedForm) {
if (linkedForm.checkForm() === false) {
isValid = false;
}
}
}
return isValid;
},
postForm() {
let is_valid = true;
if (!this.feature_type.title_optional) {
is_valid =
this.checkFormTitle() &&
this.checkFormGeom() &&
this.checkAddedForm();
} else {
is_valid = this.checkFormGeom() && this.checkAddedForm();
}
if (is_valid) {
//* in a moderate project, at edition of a published feature by someone else than admin or moderator, switch published status to draft.
if (
this.project.moderation &&
this.currentRouteName === 'editer-signalement' &&
this.form.status.value.value === 'published' &&
!this.permissions.is_project_administrator &&
!this.permissions.is_project_moderator
) {
this.form.status.value = { name: 'Brouillon', value: 'draft' };
this.updateStore();
}
this.$store.dispatch('feature/SEND_FEATURE', this.currentRouteName);
}
},
//* ************* MAP *************** *//
onFeatureTypeLoaded() {
var geomLeaflet = {
point: 'circlemarker',
linestring: 'polyline',
polygon: 'polygon',
};
var geomType = this.feature_type.geom_type;
var drawConfig = {
polygon: false,
marker: false,
polyline: false,
rectangle: false,
circle: false,
circlemarker: false,
};
drawConfig[geomLeaflet[geomType]] = true;
L.drawLocal = {
draw: {
toolbar: {
actions: {
title: 'Annuler le dessin',
text: 'Annuler',
},
finish: {
title: 'Terminer le dessin',
text: 'Terminer',
},
undo: {
title: 'Supprimer le dernier point dessiné',
text: 'Supprimer le dernier point',
},
buttons: {
polyline: 'Dessiner une polyligne',
polygon: 'Dessiner un polygone',
rectangle: 'Dessiner un rectangle',
circle: 'Dessiner un cercle',
marker: 'Dessiner une balise',
circlemarker: 'Dessiner un point',
},
},
handlers: {
circle: {
tooltip: {
start: 'Cliquer et glisser pour dessiner le cercle.',
},
radius: 'Rayon',
},
circlemarker: {
tooltip: {
start: 'Cliquer sur la carte pour placer le point.',
},
},
marker: {
tooltip: {
start: 'Cliquer sur la carte pour placer la balise.',
},
},
polygon: {
tooltip: {
start: 'Cliquer pour commencer à dessiner.',
cont: 'Cliquer pour continuer à dessiner.',
end: 'Cliquer sur le premier point pour terminer le dessin.',
},
},
polyline: {
error: '<strong>Error:</strong> shape edges cannot cross!',
tooltip: {
start: 'Cliquer pour commencer à dessiner.',
cont: 'Cliquer pour continuer à dessiner.',
end: 'Cliquer sur le dernier point pour terminer le dessin.',
},
},
rectangle: {
tooltip: {
start: 'Cliquer et glisser pour dessiner le rectangle.',
},
},
simpleshape: {
tooltip: {
end: 'Relâcher la souris pour terminer de dessiner.',
},
},
},
},
edit: {
toolbar: {
actions: {
save: {
title: 'Sauver les modifications',
text: 'Sauver',
},
cancel: {
title:
'Annuler la modification, annule toutes les modifications',
text: 'Annuler',
},
clearAll: {
title: "Effacer l'objet",
text: 'Effacer',
},
},
buttons: {
edit: "Modifier l'objet",
editDisabled: 'Aucun objet à modifier',
remove: "Supprimer l'objet",
removeDisabled: 'Aucun objet à supprimer',
},
},
handlers: {
edit: {
tooltip: {
text: "Faites glisser les marqueurs ou les balises pour modifier l'élément.",
subtext: 'Cliquez sur Annuler pour annuler les modifications..',
},
},
remove: {
tooltip: {
text: 'Cliquez sur un élément pour le supprimer.',
},
},
},
},
};
this.drawnItems = new L.FeatureGroup();
this.map.addLayer(this.drawnItems);
this.drawControlFull = new L.Control.Draw({
position: 'topright',
edit: {
featureGroup: this.drawnItems,
},
draw: drawConfig,
});
this.drawControlEditOnly = new L.Control.Draw({
position: 'topright',
edit: {
featureGroup: this.drawnItems,
},
draw: false,
});
if (this.currentRouteName === 'editer-signalement') {
this.map.addControl(this.drawControlEditOnly);
} else {
this.map.addControl(this.drawControlFull);
}
this.changeMobileBtnOrder();
this.map.on(
'draw:created',
function (e) {
var layer = e.layer;
this.add_layer_call_back(layer);
}.bind(this)
);
//var wellknown;// TODO Remplacer par autre chose
this.map.on(
'draw:edited',
function (e) {
var layers = e.layers;
let self = this;
layers.eachLayer(function (layer) {
self.updateGeomField(layer.toGeoJSON());
});
}.bind(this)
);
this.map.on(
'draw:deleted',
function () {
this.drawControlEditOnly.remove(this.map);
this.drawControlFull.addTo(this.map);
this.updateGeomField('');
if (geomType === 'point') {
this.showGeoPositionBtn = true;
this.erreurGeolocalisationMessage = '';
}
}.bind(this)
);
},
updateMap(geomFeatureJSON) {
if (this.drawnItems) this.drawnItems.clearLayers();
var geomType = this.feature_type.geom_type;
if (geomFeatureJSON) {
var geomJSON = flip(geomFeatureJSON.geometry);
if (geomType === 'point') {
L.circleMarker(geomJSON.coordinates).addTo(this.drawnItems);
} else if (geomType === 'linestring') {
L.polyline(geomJSON.coordinates).addTo(this.drawnItems);
} else if (geomType === 'polygon') {
L.polygon(geomJSON.coordinates).addTo(this.drawnItems);
}
this.map.fitBounds(this.drawnItems.getBounds(), { padding: [25, 25] });
} else {
this.map.setView(
this.$store.state.configuration.DEFAULT_MAP_VIEW.center,
this.$store.state.configuration.DEFAULT_MAP_VIEW.zoom
);
}
},
updateGeomField(newGeom) {
this.form.geom.value = newGeom.geometry;
this.updateStore();
},
initMap() {
var mapDefaultViewCenter =
this.$store.state.configuration.DEFAULT_MAP_VIEW.center;
var mapDefaultViewZoom =
this.$store.state.configuration.DEFAULT_MAP_VIEW.zoom;
// Create the map, then init the layers and features
this.map = mapUtil.createMap(this.$refs.map, {
mapDefaultViewCenter,
mapDefaultViewZoom,
});
const currentFeatureId = this.$route.params.slug_signal;
setTimeout(() => {
let project_id = this.$route.params.slug.split('-')[0];
const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
mapUtil.addVectorTileLayer(
mvtUrl,
this.$route.params.slug,
this.$store.state.feature_type.feature_types
);
}, 1000);
const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?feature_type__slug=${this.$route.params.slug_type_signal}&output=geojson`;
axios
.get(url)
.then((response) => {
const features = response.data.features;
if (features) {
const allFeaturesExceptCurrent = features.filter(
(feat) => feat.id !== currentFeatureId
);
mapUtil.addFeatures(
allFeaturesExceptCurrent,
{},
true,
this.$store.state.feature_type.feature_types
);
if (this.currentRouteName === 'editer-signalement') {
const currentFeature = features.filter(
(feat) => feat.id === currentFeatureId
)[0];
this.updateMap(currentFeature);
}
}
})
.catch((error) => {
throw error;
});
document.addEventListener('change-layers-order', (event) => {
// 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
mapUtil.updateOrder(event.detail.layers.slice().reverse());
});
},
add_layer_call_back(layer) {
layer.addTo(this.drawnItems);
this.drawControlFull.remove(this.map);
this.drawControlEditOnly.addTo(this.map);
//var wellknown;// TODO Remplacer par autre chose
this.updateGeomField(layer.toGeoJSON());
if (this.feature_type.geomType === 'point') {
this.showGeoPositionBtn = false;
this.erreurGeolocalisationMessage = '';
}
},
changeMobileBtnOrder() { //* move large toolbar for polygon creation, cutting map in the middle
function changeDisplay() {
let buttons = document.querySelector('.leaflet-draw-actions.leaflet-draw-actions-top.leaflet-draw-actions-bottom');
if (buttons && buttons.style) {
buttons.style.display = 'flex';
buttons.style['flex-direction'] = 'column';
}
}
if (window.screen.availWidth < 767) { //* change button order all the time to keep homogeinity on mobile
let wrapper = document.querySelector('.leaflet-top.leaflet-right');
if (wrapper) wrapper.appendChild(wrapper.children[0]);
if (this.feature_type.geom_type === 'polygon') { //* if it's a polygon, change tools direction to vertical
let polygonBtn = document.querySelector('.leaflet-draw-draw-polygon'); //* since elements are generated
if (polygonBtn) polygonBtn.addEventListener('click', changeDisplay); //* it should be done at each click
}
}
},
getFeatureAttachments() {
featureAPI
.getFeatureAttachments(this.$route.params.slug_signal)
.then((data) => this.addExistingAttachementFormset(data));
},
getLinkedFeatures() {
featureAPI
.getFeatureLinks(this.$route.params.slug_signal)
.then((data) => this.addExistingLinkedFormset(data));
},
},
};
</script>
<style scoped>
#map {
height: 70vh;
width: 100%;
border: 1px solid grey;
}
#get-geom-from-image-file {
margin-bottom: 5px;
}
.georef-btn {
max-width: 400px;
}
@media only screen and (max-width: 767px) {
#map {
height: 80vh;
}
}
/* // ! missing style in semantic.min.css */
.ui.right.floated.button {
float: right;
margin-right: 0;
margin-left: 0.25em;
}
/* // ! margin écrasé par class last-child first-child */
.ui.segment {
margin: 1rem 0 !important;
}
/* override to display buttons under the dimmer of modal */
.leaflet-top,
.leaflet-bottom {
z-index: 800;
}
</style>