Something went wrong on our end
-
Florent Lavelle authoredFlorent Lavelle authored
map-util.js 18.81 KiB
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import flip from '@turf/flip';
import axios from '@/axios-client.js';
import 'leaflet.vectorgrid';
let map;
let dictLayersToLeaflet = {};
var layerMVT;
let statusList = [
{
name: 'Brouillon',
value: 'draft',
},
{
name: 'Publié',
value: 'published',
},
{
name: 'Archivé',
value: 'archived',
},
{
name: 'En attente de publication',
value: 'pending',
},
];
L.TileLayer.BetterWMS = L.TileLayer.WMS.extend({
onAdd: function (map) {
// Triggered when the layer is added to a map.
// Register a click listener, then do all the upstream WMS things
L.TileLayer.WMS.prototype.onAdd.call(this, map);
map.on('click', this.getFeatureInfo, this);
},
onRemove: function (map) {
// Triggered when the layer is removed from a map.
// Unregister a click listener, then do all the upstream WMS things
L.TileLayer.WMS.prototype.onRemove.call(this, map);
map.off('click', this.getFeatureInfo, this);
},
getFeatureInfo: function (evt) {
if (this.wmsParams.basemapId != undefined) {
const queryableLayerSelected = document.getElementById(`queryable-layers-selector-${this.wmsParams.basemapId}`).getElementsByClassName('selected')[0].textContent;
if (queryableLayerSelected.trim() === this.wmsParams.title.trim()) {
// Make an AJAX request to the server and hope for the best
var params = this.getFeatureInfoUrl(evt.latlng);
var showResults = L.Util.bind(this.showGetFeatureInfo, this);
axios.get(
window.proxy_url,
{
params: params,
}
).then(response => {
let data = response.data;
var err = typeof data === 'object' ? null : data;
if (data.features || err) {
showResults(err, evt.latlng, data);
}
})
.catch(error => {
throw (error);
}
);
}
}
},
getFeatureInfoUrl: function (latlng) {
// Construct a GetFeatureInfo request URL given a point
var point = this._map.latLngToContainerPoint(latlng, this._map.getZoom());
var size = this._map.getSize(),
params = {
url: this._url,
request: 'GetFeatureInfo',
service: 'WMS',
// srs: this.wmsParams.srs,
srs: 'EPSG:4326',
// styles: this.wmsParams.styles,
// transparent: this.wmsParams.transparent,
version: this.wmsParams.version,
// format: this.wmsParams.format,
bbox: this._map.getBounds().toBBoxString(),
height: size.y,
width: size.x,
layers: this.wmsParams.layers,
query_layers: this.wmsParams.layers,
info_format: 'application/json'
};
params[params.version === '1.3.0' ? 'i' : 'x'] = Math.floor(point.x);
params[params.version === '1.3.0' ? 'j' : 'y'] = Math.floor(point.y);
return params;
},
showGetFeatureInfo: function (err, latlng, data) {
let content;
if (err) {
content = `
<h4>${this.options.title}</h4>
<p>Données de la couche inaccessibles</p>
`;
L.popup({ maxWidth: 800 })
.setLatLng(latlng)
.setContent(content)
.openOn(this._map);
} else {
// Otherwise show the content in a popup
let contentLines = [];
let contentTitle;
if (data.features.length > 0) {
Object.entries(data.features[0].properties).forEach(entry => {
const [key, value] = entry;
if (key !== 'bbox') {
contentLines.push(`<div>${key}: ${value}</div>`);
}
});
contentTitle = `<h4>${this.options.title}</h4>`;
content = contentTitle.concat(contentLines.join(''));
L.popup({ maxWidth: 800 })
.setLatLng(latlng)
.setContent(content)
.openOn(this._map);
}
}
}
});
L.tileLayer.betterWms = function (url, options) {
return new L.TileLayer.BetterWMS(url, options);
};
const mapUtil = {
getMap: () => {
return map;
},
createMap: function (el, options) {
const {
lat,
lng,
mapDefaultViewCenter,
mapDefaultViewZoom,
zoom,
zoomControl = true,
} = options;
map = L.map(el, {
maxZoom: 18,
minZoom: 1,
zoomControl: false,
}).setView(
[
lat ? lat : mapDefaultViewCenter[0],
lng ? lng : mapDefaultViewCenter[1],
],
!zoom ? mapDefaultViewZoom : zoom
);
map.setMaxBounds( [[-90,-180], [90,180]] );
if (zoomControl) {
L.control
.zoom({
zoomInTitle: 'Zoomer',
zoomOutTitle: 'Dézoomer',
position: 'topright',
})
.addTo(map);
}
L.control.scale().addTo(map);
return map;
},
addGeocoders: function (configuration) {
let geocoder;
const geocoderLabel = configuration.SELECTED_GEOCODER.PROVIDER;
if (geocoderLabel && L.Control.Geocoder) {
const LIMIT_RESULTS = 5;
if (
geocoderLabel === configuration.GEOCODER_PROVIDERS.ADDOK
) {
geocoder = L.Control.Geocoder.addok({ limit: LIMIT_RESULTS });
} else if (
geocoderLabel === configuration.GEOCODER_PROVIDERS.PHOTON
) {
geocoder = L.Control.Geocoder.photon();
} else if (
geocoderLabel === configuration.GEOCODER_PROVIDERS.NOMINATIM
) {
geocoder = L.Control.Geocoder.nominatim();
}
L.Control.geocoder({
placeholder: 'Chercher une adresse...',
geocoder: geocoder,
}).addTo(map);
}
},
addLayers: function (layers, serviceMap, optionsMap, schemaType) {
if (layers) { //* if admin has defined basemaps for this project
layers.forEach((layer) => {
if (layer) {
let options = layer.options;
if (options) {
options.noWrap = true;
options.opacity = layer.opacity;
if (layer.schema_type === 'wms') {
let leafletLayer;
if (layer.queryable) {
options.title = layer.title;
leafletLayer = L.tileLayer
.betterWms(layer.service, options)
.addTo(map);
} else {
leafletLayer = L.tileLayer
.wms(layer.service, options)
.addTo(map);
}
dictLayersToLeaflet[layer.id] = leafletLayer._leaflet_id;
} else if (layer.schema_type === 'tms') {
const leafletLayer = L.tileLayer(layer.service, options).addTo(map);
dictLayersToLeaflet[layer.id] = leafletLayer._leaflet_id;
}
}
}
});
} else { //* else when no basemaps defined
optionsMap.noWrap = true;
if (schemaType === 'wms') {
L.tileLayer
.wms(serviceMap, optionsMap)
.addTo(map);
} else {
L.tileLayer(serviceMap, optionsMap).addTo(map);
}
}
},
// Remove the base layers (not the features)
removeLayers: function () {
map.eachLayer((leafLetlayer) => {
if (
Object.values(dictLayersToLeaflet).includes(leafLetlayer._leaflet_id)
) {
map.removeLayer(leafLetlayer);
}
});
dictLayersToLeaflet = {};
},
updateOpacity(layerId, opacity) {
const internalLeafletLayerId = dictLayersToLeaflet[layerId];
map.eachLayer((layer) => {
if (layer._leaflet_id === internalLeafletLayerId) {
layer.setOpacity(opacity);
}
});
},
updateOrder(layers) {
// First remove existing layers undefined
layers = layers.filter(function (x) {
return x !== undefined;
});
// First remove existing layers
map.eachLayer((leafLetlayer) => {
layers.forEach((layerOptions) => {
if (dictLayersToLeaflet[layerOptions.id] === leafLetlayer._leaflet_id) {
map.removeLayer(leafLetlayer);
}
});
});
dictLayersToLeaflet = {};
// Redraw the layers
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;
}
return featureType.color;
},
addVectorTileLayer: function (url, projectSlug, featureTypes, formFilters) {
layerMVT = L.vectorGrid.protobuf(url, {
noWrap:true,
vectorTileLayerStyles: {
default: (properties) => {
const featureType = featureTypes.find((x) => x.slug.split('-')[0] === '' + properties.feature_type_id);
const color = this.retrieveFeatureColor(featureType, properties);
const colorValue =
color.value && color.value.length ?
color.value : typeof color === 'string' && color.length ?
color : '#000000';
const hiddenStyle = ({
radius: 0,
fillOpacity: 0.5,
weight: 0,
fill: false,
color: featureType.color,
});
const defaultStyle = {
radius: 4,
fillOpacity: 0.5,
weight: 3,
fill: true,
color: colorValue,
};
// Filtre sur le feature type
if (formFilters && formFilters.type.selected) {
if (featureType.title !== formFilters.type.selected) {
return hiddenStyle;
}
}
// Filtre sur le statut
if (formFilters && formFilters.status.selected.value) {
if (properties.status !== formFilters.status.selected.value) {
return hiddenStyle;
}
}
// Filtre sur le titre
if (formFilters && formFilters.title) {
if (!properties.title.toLowerCase().includes(formFilters.title.toLowerCase())) {
return hiddenStyle;
}
}
return defaultStyle;
},
},
// subdomains: "0123",
// key: 'abcdefghi01234567890',
interactive: true,
maxNativeZoom: 18,
getFeatureId: function (f) {
return f.properties.id;
}
});
layerMVT.on('click', (e) => { // The .on method attaches an event handler
const popupContent = this._createContentPopup(e.layer, featureTypes, projectSlug);
L.popup()
.setContent(popupContent)
.setLatLng(e.latlng)
.openOn(map);
});
layerMVT.addTo(map);
window.layerMVT = layerMVT;
},
addFeatures: function (features, filter, addToMap = true, featureTypes, projectSlug) {
const featureGroup = new L.FeatureGroup();
features.forEach((feature) => {
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];
}
if (
!filter ||
!Object.values(filter).some(val => val) ||
Object.values(filter).some(val => val) &&
filters.length && filters.every(val => val !== false)
) {
const geomJSON = flip(feature.geometry || feature.geom);
const popupContent = this._createContentPopup(feature, featureTypes, projectSlug);
// Look for a custom field
let customField;
let customFieldOption;
if (
featureType.customfield_set &&
Object.keys(featureProperties).some(el => featureType.customfield_set.map(e => e.name).includes(el))
) {
customField = Object.keys(featureProperties)
.filter(el => featureType.customfield_set.map(e => e.name).includes(el));
customFieldOption = featureProperties[customField[0]];
}
let color = '#000000';
if (feature.overideColor) {
color = feature.overideColor;
} else {
color = this.retrieveFeatureColor(featureType, featureProperties) || featureProperties.color;
if (color.value && color.value.length) {
color = color.value;
}
}
if (geomJSON.type === 'Point') {
if (
customFieldOption &&
featureType.colors_style &&
featureType.colors_style.value &&
featureType.colors_style.value.icons &&
!!Object.keys(featureType.colors_style.value.icons).length
) {
if (
featureType.colors_style.value.icons[customFieldOption] &&
featureType.colors_style.value.icons[customFieldOption] !== 'circle'
) {
const iconHTML = `
<em
class="fas fa-${featureType.colors_style.value.icons[customFieldOption]} fa-lg"
style="color: ${color}"
></em>
`;
const customMapIcon = L.divIcon({
html: iconHTML,
iconSize: [20, 20],
className: 'myDivIcon',
});
L.marker(geomJSON.coordinates, {
icon: customMapIcon,
color: color,
zIndexOffset: 100
})
.bindPopup(popupContent)
.addTo(featureGroup);
} else {
L.circleMarker(geomJSON.coordinates, {
color: color,
radius: 4,
fillOpacity: 0.5,
weight: 3,
})
.bindPopup(popupContent)
.addTo(featureGroup);
}
} else {
if (featureType.icon && featureType.icon !== 'circle') {
const iconHTML = `
<em
class="fas fa-${featureType.icon} fa-lg"
style="color: ${color}"
></em>
`;
const customMapIcon = L.divIcon({
html: iconHTML,
iconSize: [20, 20],
className: 'myDivIcon',
});
L.marker(geomJSON.coordinates, {
icon: customMapIcon,
color: color,
zIndexOffset: 100
})
.bindPopup(popupContent)
.addTo(featureGroup);
} else {
L.circleMarker(geomJSON.coordinates, {
color: color,
radius: 4,
fillOpacity: 0.5,
weight: 3,
})
.bindPopup(popupContent)
.addTo(featureGroup);
}
}
} else if (geomJSON.type === 'LineString') {
L.polyline(geomJSON.coordinates, {
color: color,
weight: 3,
})
.bindPopup(popupContent)
.addTo(featureGroup);
} else if (geomJSON.type === 'Polygon') {
L.polygon(geomJSON.coordinates, {
color: color,
weight: 3,
fillOpacity: 0.5,
})
.bindPopup(popupContent)
.addTo(featureGroup);
}
}
});
if (map && addToMap) {
map.addLayer(featureGroup);
}
return featureGroup;
},
addMapEventListener: function (eventName, callback) {
map.on(eventName, callback);
},
_createContentPopup: function (feature, featureTypes, projectSlug) {
const formatDate = (currentDatetime) => {
let formattedDate = currentDatetime.getFullYear() + '-' + ('0' + (currentDatetime.getMonth() + 1)).slice(-2) + '-' + ('0' + currentDatetime.getDate()).slice(-2) + ' ' +
('0' + currentDatetime.getHours()).slice(-2) + ':' + ('0' + currentDatetime.getMinutes()).slice(-2);
return formattedDate;
};
let featureType = feature.properties ? feature.properties.feature_type : feature.feature_type;
let featureUrl = feature.feature_url;
let status = feature.status;
let featureTypeUrl = feature.feature_type_url;
let dateMaj = feature.updated_on;
if (feature.properties) {
status = feature.properties.status;
dateMaj = feature.properties.updated_on ? formatDate(new Date(feature.properties.updated_on)) : '<em>indisponible</em>';
featureTypeUrl = feature.properties.feature_type_url;
featureUrl = feature.properties.feature_url;
}
if (featureTypes && feature.properties) { // => VectorTile
featureType = feature.properties.feature_type_id ?
featureTypes.find((x) => x.slug.split('-')[0] === '' + feature.properties.feature_type_id) :
featureTypes.find((fType) => fType.slug === feature.properties.feature_type); //* geojson
status = statusList.find((x) => x.value === feature.properties.status.value).name;
if (featureType) featureTypeUrl = `/geocontrib/projet/${projectSlug}/type-signalement/${featureType.slug}/`;
featureUrl = `${featureTypeUrl}signalement/${feature.properties.feature_id}/`;
} else {
status = feature.properties ? feature.properties.status.label : feature.status.label;
}
//* adapt link url for shared-project restricted navigation
if (window.location.pathname.includes('projet-partage')) {
featureUrl = featureUrl.replace('projet', 'projet-partage');
featureTypeUrl = featureTypeUrl.replace('projet', 'projet-partage');
}
let author = '';
const creator = feature.properties ? feature.properties.creator : feature.creator;
if (creator) {
author = creator.full_name
? `<div>
Auteur : ${creator.first_name} ${creator.last_name}
</div>`
: creator.username ? `<div>Auteur: ${creator.username}</div>` : '';
}
let title = feature.properties ? feature.properties.title : feature.title;
if (featureUrl) {
title = `<a href="${featureUrl}">${title}</a>`;
} else {
title = `<span>${title|| '<em>indisponible</em>'}</span>`;
}
if (featureTypeUrl) {
`<a href="${featureTypeUrl}"> ${featureType.title || '<em>indisponible</em>'} </a>`;
}
return `
<h4>
<${featureUrl ? `a href="${featureUrl}"` : 'span'}>${title || '<em>indisponible</em>'}</${featureUrl ? 'a' : 'span'}>
</h4>
<div>
Statut : ${status || '<em>indisponible</em>'}
</div>
<div>
Type : <${featureTypeUrl ? `a href="${featureTypeUrl}"` : 'span'}> ${featureType.title || '<em>indisponible</em>'} </${featureTypeUrl ? 'a' : 'span'}>
</div>
<div>
Dernière mise à jour : ${dateMaj || '<em>indisponible</em>'}
</div>
${author}
`;
},
};
export { mapUtil };