Something went wrong on our end
-
DESPRES Damien authoredDESPRES Damien authored
map-service.js 17.49 KiB
import TileWMS from 'ol/source/TileWMS';
import axios from '@/axios-client.js';
import { View, Map } from 'ol';
import { ScaleLine, Zoom, Attribution } from 'ol/control';
import TileLayer from 'ol/layer/Tile';
import { transform, transformExtent } from 'ol/proj.js';
import { defaults } from 'ol/interaction';
import XYZ from 'ol/source/XYZ';
import VectorTileLayer from 'ol/layer/VectorTile';
import VectorTileSource from 'ol/source/VectorTile';
import { MVT, GeoJSON } from 'ol/format';
import { boundingExtent } from 'ol/extent';
import Overlay from 'ol/Overlay';
import {
Fill, Stroke, Style, Circle //RegularShape, Circle as CircleStyle, Text,Icon
} from 'ol/style';
import { asArray } from 'ol/color';
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import { fromLonLat } from 'ol/proj.js';
import OverlayPositioning from 'ol/OverlayPositioning';
let dictLayersToLeaflet = {};
let statusList = [
{
name: 'Brouillon',
value: 'draft',
},
{
name: 'Publié',
value: 'published',
},
{
name: 'Archivé',
value: 'archived',
},
{
name: 'En attente de publication',
value: 'pending',
},
];
const mapService = {
layers: [],
mvtLayer: undefined,
content: {},
overlay: {},
map: undefined,
getMap() {
return this.map;
},
destroyMap() {
this.map=undefined;
},
createMap(el, options) {
const {
lat,
lng,
mapDefaultViewCenter,
mapDefaultViewZoom,
zoom,
zoomControl = true,
interactions = { doubleClickZoom: false, mouseWheelZoom: false, dragPan: true },
} = options;
console.log(options, zoomControl);
let map = new Map({
layers: [],
target: el,
controls: [
new Attribution(),
new ScaleLine({
units: 'metric',
})],
interactions: defaults(interactions),
view: new View({
center: transform([
!lng ? mapDefaultViewCenter[1] : lng,
!lat ? mapDefaultViewCenter[0] : lat,
], 'EPSG:4326', 'EPSG:3857'),
zoom: !zoom ? mapDefaultViewZoom : zoom
}),
});
this.map=map;
if (zoomControl) {
map.addControl(new Zoom({ zoomInTipLabel: 'Zoomer', zoomOutTipLabel: 'Dézoomer' }));
}
map.once('rendercomplete', () => {
map.updateSize();
});
const container = document.getElementById('popup');
this.content = document.getElementById('popup-content');
const closer = document.getElementById('popup-closer');
this.overlay = new Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 500,
},
});
let overlay = this.overlay;
closer.onclick = function () {
overlay.setPosition(undefined);
closer.blur();
return false;
};
map.addOverlay(this.overlay);
map.on('click', this.onMapClick.bind(this));
return map;
},
onMapClick(event) {
console.log(event);
let self = this;
const features = this.map.getFeaturesAtPixel(event.pixel, {
layerFilter(l) {
return l === self.mvtLayer;
},
});
console.log(features);
if (features && features.length > 0) {
const popupContent = this._createContentPopup(features[0],
self.mvtLayer.featureTypes, self.mvtLayer.project_slug);
this.content.innerHTML = popupContent;
this.overlay.setPosition(event.coordinate);
}
//const queryableLayerSelected = document.getElementById(`queryable-layers-selector-${this.wmsParams.basemapId}`).getElementsByClassName('selected')[0].textContent;
//console.log(queryableLayerSelected);
if (this.layers) {
console.log(this.layers.find(x => x.query));
let queryLayer = this.layers.find(x => x.query);
// pour compatibilité avec le proxy django
let proxyparams = [
'request',
'service',
'srs',
'version',
'bbox',
'height',
'width',
'layers',
'query_layers',
'info_format', 'x', 'y', 'i', 'j',
];
if (queryLayer) {
console.log('get feature infos');
let url = this.getFeatureInfoUrl(event, queryLayer);
console.log(url);
let urlInfos = url.split('?');
const urlParams = new URLSearchParams(urlInfos[1]);
let params = {};
Array.from(urlParams.keys()).forEach(param => {
if (proxyparams.indexOf(param.toLowerCase()) >= 0)
params[param.toLowerCase()] = urlParams.get(param);
});
params.url = urlInfos[0];
let self = 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) {
self.showGetFeatureInfo(err, event, data, queryLayer);
}
})
.catch(error => {
throw (error);
}
);
}
}
},
showGetFeatureInfo: function (err, event, data, layer) {
let content;
if (err) {
content = `
<h4>${layer.options.title}</h4>
<p>Données de la couche inaccessibles</p>
`;
this.content.innerHTML = content;
this.overlay.setPosition(event.coordinate);
} 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>${layer.options.title}</h4>`;
content = contentTitle.concat(contentLines.join(''));
this.content.innerHTML = content;
this.overlay.setPosition(event.coordinate);
}
}
},
getFeatureInfoUrl(event, layer) {
let olLayer = dictLayersToLeaflet[layer.id];
const source = olLayer.getSource();
const viewResolution = this.map.getView().getResolution();
let url;
let wmsOptions = { info_format: 'application/json', query_layers: layer.options.layers };
if (source && source.getFeatureInfoUrl) {
url = source.getFeatureInfoUrl(event.coordinate, viewResolution, 'EPSG:3857', wmsOptions);
}
return url;
},
fitBounds(bounds) {
console.log(bounds);
let ext = boundingExtent([[bounds[0][1], bounds[0][0]], [bounds[1][1], bounds[1][0]]]);
ext = transformExtent(ext, 'EPSG:4326', 'EPSG:3857');
this.map.getView().fit(ext, { padding: [25, 25, 25, 25] });
},
fitExtent(ext) {
//ext = transformExtent(ext, 'EPSG:4326', 'EPSG:3857');
this.map.getView().fit(ext, { padding: [25, 25, 25, 25] });
},
addLayers: function (layers, serviceMap, optionsMap, schemaType) {
console.log('addLayers');
this.layers = layers;
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') {
console.log(layer);
if (layer.queryable) {
options.title = layer.title;
dictLayersToLeaflet[layer.id] = this.addWMSLayer(layer.service, options);
} else {
dictLayersToLeaflet[layer.id] = this.addWMSLayer(layer.service, options);
}
} else if (layer.schema_type === 'tms') {
const layerTms = new TileLayer({
source: new XYZ({
attributions: options.attribution,
url: layer.service.replace('{s}', '{a-c}')
})
});
this.map.addLayer(layerTms);
dictLayersToLeaflet[layer.id] = layerTms;
}
}
}
});
} else { //* else when no basemaps defined
optionsMap.noWrap = true;
if (schemaType === 'wms') {
this.addWMSLayer(serviceMap, optionsMap);
} else {
const layer = new TileLayer({
source: new XYZ({
attributions: optionsMap.attribution,
url: serviceMap.replace('{s}', '{a-c}')
})
});
this.map.addLayer(layer);
}
}
},
addWMSLayer: function (url, options) {
options.VERSION = '1.1.1'; // pour compatibilité avec le proxy django
const source = new TileWMS({
attributions: options.attribution,
url: url,
crossOrigin: 'anonymous',
params: options
});
const layer = new TileLayer({
source: source
});
this.map.addLayer(layer);
return layer;
},
// Remove the base layers (not the features)
removeLayers: function () {
Object.values(dictLayersToLeaflet).forEach(element => {
this.map.removeLayer(element);
});
dictLayersToLeaflet = {};
},
updateOpacity(layerId, opacity) {
const layer = dictLayersToLeaflet[layerId];
layer.setOpacity(parseFloat(opacity));
},
updateOrder(layers) {
// First remove existing layers undefined
layers = layers.filter(function (x) {
return x !== undefined;
});
this.removeLayers();
// 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;
} else {
return featureType.color;
}
},
addVectorTileLayer: function (url, project_id, project_slug, featureTypes, form_filters) {
console.log(url, project_slug, featureTypes, form_filters);
let format_cfg = {/*featureClass: Feature*/ };
let mvt = new MVT(format_cfg);
let options = {
urls: [],
matrixSet: 'EPSG:3857'
};
options.format = mvt;
let layerSource = new VectorTileSource(options);
layerSource.setTileUrlFunction((p0) => {
return url+'/?tile=' + p0[0] + '/' + p0[1] + '/' + p0[2] + '&project_id=' + project_id;
});
const styleFunction = (feature) => this.getStyle(feature, featureTypes, form_filters);
this.mvtLayer = new VectorTileLayer({
style: styleFunction,
source: layerSource
});
this.mvtLayer.featureTypes = featureTypes;
this.mvtLayer.project_slug = project_slug;
this.map.addLayer(this.mvtLayer);
window.layerMVT = this.mvtLayer;
},
getStyle: function (feature, featureTypes, form_filters) {
//console.log(form_filters);
let properties = feature.getProperties();
let featureType;
// GeoJSON
if(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);
}
//console.log(featureType);
const color = this.retrieveFeatureColor(featureType, properties);
const colorValue =
color.value && color.value.length ?
color.value : typeof color === 'string' && color.length ?
color : '#000000';
let rgbaColor = asArray(colorValue);
rgbaColor[3] = 0.5;//opacity
const hiddenStyle = new Style();
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,
},
),
fill: new Fill(
{
color: rgbaColor,
},
),
},
);
// Filtre sur le feature type
if(form_filters){
if (form_filters.type && form_filters.type.selected) {
if (featureType.title !== form_filters.type.selected) {
return hiddenStyle;
}
}
// Filtre sur le statut
if (form_filters.status && form_filters.status.selected.value) {
if (properties.status !== form_filters.status.selected.value) {
return hiddenStyle;
}
}
// Filtre sur le titre
if (form_filters.title) {
if (!properties.title.toLowerCase().includes(form_filters.title.toLowerCase())) {
return hiddenStyle;
}
}
}
return defaultStyle;
},
addFeatures: function (features, filter, addToMap = true, featureTypes) {
console.log(features, filter, addToMap, featureTypes);
let drawSource = new VectorSource();
let retour;
// TODO verifier utilité de cette boucle et remplacer par readFeatures plutot
features.forEach((feature) => {
console.log(feature);
retour = new GeoJSON().readFeature(feature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' });
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);
});
const styleFunction = (feature) => this.getStyle(feature, featureTypes, filter);
const olLayer = new VectorLayer({
source: drawSource,
style: styleFunction,
});
this.map.addLayer(olLayer);
return drawSource;
},
addMapEventListener: function (eventName, callback) {
this.map.on(eventName, callback);
},
_createContentPopup: function (feature, featureTypes, project_slug) {
const formatDate = (current_datetime) => {
let formatted_date = current_datetime.getFullYear() + '-' + ('0' + (current_datetime.getMonth() + 1)).slice(-2) + '-' + ('0' + current_datetime.getDate()).slice(-2) + ' ' +
('0' + current_datetime.getHours()).slice(-2) + ':' + ('0' + current_datetime.getMinutes()).slice(-2);
return formatted_date;
};
let feature_type;
let status;
let date_maj;
let feature_type_url;
let feature_url;
if (feature.getProperties) {
status = feature.getProperties().status;
date_maj = feature.getProperties().updated_on;
feature_type_url = feature.getProperties().feature_type_url;
feature_url = feature.getProperties().feature_url;
} else {
status = feature.status;
date_maj = feature.updated_on;
feature_type_url = feature.feature_type_url;
feature_url = feature.feature_url;
}
if (featureTypes) { // => VectorTile
feature_type = featureTypes.find((x) => x.slug.split('-')[0] === '' + feature.getProperties().feature_type_id);
status = statusList.find((x) => x.value === feature.getProperties().status).name;
date_maj = formatDate(new Date(feature.getProperties().updated_on));
feature_type_url = '/geocontrib/projet/' + project_slug + '/type-signalement/' + feature_type.slug + '/';
feature_url = feature_type_url + 'signalement/' + feature.getProperties().feature_id + '/';
} else {
feature_type = feature.getProperties ? feature.getProperties().feature_type : feature.feature_type;
status = feature.getProperties ? feature.getProperties().status.label : feature.status.label;
}
//* adapt link url for shared-project restricted navigation
if (window.location.pathname.includes('projet-partage')) {
feature_url = feature_url.replace('projet', 'projet-partage');
feature_type_url = feature_type_url.replace('projet', 'projet-partage');
}
let author = '';
const creator = feature.getProperties ? feature.getProperties().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>` : '';
}
const title = feature.getProperties ? feature.getProperties().title : feature.title;
return `
<h4>
<a href="${feature_url}">${title}</a>
</h4>
<div>
Statut : ${status}
</div>
<div>
Type : <a href="${feature_type_url}"> ${feature_type.title} </a>
</div>
<div>
Dernière mise à jour : ${date_maj}
</div>
${author}
`;
},
zoomTo(location, zoomlevel, lon, lat) {
if (lon && lat) {
location = [+lon, +lat];
}
this.map.getView().setCenter(transform(location, 'EPSG:4326', 'EPSG:3857'));
this.map.getView().setZoom(zoomlevel);
},
addOverlay(loc) {
var pos = fromLonLat(loc);
console.log(loc);
var marker = new Overlay({
position: pos,
positioning: OverlayPositioning.CENTER_CENTER,
element: document.getElementById('marker'),
stopEvent: false
});
this.map.addOverlay(marker);
}
};
export default mapService;