|
|
- /**
-
- Contrôleur qui gère la carte (charge son contenu et contrôle l’interactivité)
-
- Voir la macro Twig `map` qui produit le HTML géré par ce contrôleur.
-
- Cf <https://leafletjs.com/reference.html>
-
- **/
- import { Controller } from '@hotwired/stimulus';
- import 'leaflet';
-
- export default class extends Controller {
- static values = {
- geojson: String,
- overpassResult: String,
- icon: String,
- popupUrl: String,
- }
-
- connect() {
- // Constitue une collection d’icones aux couleurs Bootstrap
- const iconHtml = `
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-geo-alt-fill" viewBox="0 0 16 16">
- <path fill="currentColor" d="M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10m0-7a3 3 0 1 1 0-6 3 3 0 0 1 0 6"/>
- </svg>
- `;
- const icons = {
- 'danger': L.divIcon({ html: iconHtml, className: 'svg-icon text-danger', iconSize: [16, 16], iconAnchor: [8, 16], }),
- 'warning': L.divIcon({ html: iconHtml, className: 'svg-icon text-warning', iconSize: [16, 16], iconAnchor: [8, 16], }),
- 'success': L.divIcon({ html: iconHtml, className: 'svg-icon text-success', iconSize: [16, 16], iconAnchor: [8, 16], }),
- 'info': L.divIcon({ html: iconHtml, className: 'svg-icon text-info', iconSize: [16, 16], iconAnchor: [8, 16], }),
- };
-
- var geojsons, _this = this, map = L.map(this.element.querySelector('#map'));
- this.mapInstance = map;
-
- // Commence par déclarer le fond de carte classique OSM par défaut
- L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
- maxZoom: 19,
- attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
- }).addTo(map);
-
- // Crée un ensemble de couches pour mieux les manipuler
- // individuellement
- var layer = L.featureGroup();
-
- // Crée la couche dédiée à Overpass
- var overpassLayer = L.featureGroup();
- if (this.overpassResultValue !== '') {
- geojsons = JSON.parse(this.overpassResultValue);
- if (geojsons.elements.length > 0) {
- // Ajoute chaque forme
- geojsons.elements.forEach(function (element) {
- // Cas d’un nœud
- if (element.type === 'node') {
- L.marker([ element.lat, element.lon ], {
- icon: icons['info'],
- }).addTo(overpassLayer).bindPopup(L.popup({
- overpassElement: element, // on transmet les données du geojson à la popup par là
- }).setContent('…')); // le contenu définitif de la popup sera chargé plus tard en ajax
- }
- // Cas d’autre chose qu’un nœud, sans distinction
- if (element.members) {
- element.members.forEach(function (member) {
- // TODO On est parti du principe que les features du
- // geojson sont toutes des polylines ce qui est un peu
- // réducteur, à terme il faudrait distinguer les géométries
- const polygon = L.polyline(member.geometry.map(function (coord) { return L.latLng(coord.lat, coord.lon)}), {
- color: '#0dcaf0', // bleu info bootstrap
- weight: 6, // l’idée c’est que ce soit plus gros (par défaut c’est 3) que le tracé des données des tâches parce que ce sera en dessous
- opacity: 0.8,
- }).addTo(overpassLayer).bindPopup(L.popup({
- overpassElement: element, // on transmet les données du geojson à la popup par là
- }).setContent('…')); // le contenu définitif de la popup sera chargé plus tard en ajax
- });
- }
- });
- // Intervient lors de l’ouverture de la popup associée à la forme
- overpassLayer.on('popupopen', function (event) {
- // Récupère le geojson de la forme
- var element = event.popup.options.overpassElement;
- // Enlève ce qui nous est inutile (les points, etc)
- delete element.members;
- // Ajoute ce qui peut-être utile (concernant la carte0
- element['map'] = {
- 'center': map.getCenter(),
- 'zoom': map.getZoom(),
- };
- // Effectue l’appel ajax pour récupérer le contenu de la popup
- fetch(_this.popupUrlValue + '?' + (new URLSearchParams({
- 'element': JSON.stringify(element),
- })))
- .then(function (response) {
- return response.text();
- })
- .then(function (text) {
- event.popup.setContent(text);
- });
- });
- overpassLayer.addTo(layer);
- }
- }
-
- // Créé la couche dédiée aux tâches
- var taskLayer = L.featureGroup();
- geojsons = JSON.parse(this.geojsonValue);
- if (geojsons.length > 0) {
- geojsons.forEach(function (geojson) {
- geojson.features.forEach(function (feature) {
- // Dessine la forme de la tâche avec la proprieté `name` qui
- // s’ffiche au survol et cliquable vers l’adresse web dans la
- // propriété `url`
- if (feature.geometry.type === 'Point') {
- L.marker([
- feature.geometry.coordinates[1],
- feature.geometry.coordinates[0],
- ], {
- icon: icons[feature.properties.color],
- title: feature.properties.name,
- clickUrl: feature.properties.url,
- }).addTo(taskLayer).on('click', function (event) {
- window.location.href = event.target.options.clickUrl;
- });
- } else {
- const polygon = L.geoJSON(feature, {
- style: function (feature) {
- // Par défaut c’est un bleu par défaut de leaflet mais
- // sinon on utilise les couleurs de Bootstrap
- var color = 'blue';
- switch (feature.properties.color) {
- case 'danger' : color = '#dc3545'; break
- case 'warning' : color = '#ffc107'; break
- case 'success' : color = '#198754'; break
- }
- if (feature.geometry.type === 'Polygon') {
- return {
- color: color,
- weight: 1,
- fillOpacity: 0.5,
- };
- } else {
- return {color: color};
- }
- }
- }).bindTooltip(feature.properties.name).addTo(taskLayer).on('click', function (event) {
- window.location.href = event.layer.feature.properties.url;
- });
- }
- });
- });
- taskLayer.addTo(layer);
- }
-
- layer.addTo(map);
-
- // Si la couche Overpass n’est pas vide, ajoute le sélecteur de couches
- // sur la carte
- if (this.overpassResultValue !== '') {
- L.control.layers({}, {
- 'Overpass': overpassLayer,
- 'Tâches': taskLayer,
- }).addTo(map);
- }
-
- // Zoome la carte pour que les données des tâches soient toutes
- // visibles
- map.fitBounds(taskLayer.getBounds());
- }
-
- openInOsm() {
- const url = "https://www.openstreetmap.org/#map="+this.mapInstance.getZoom()+"/"+this.mapInstance.getCenter().lat+"/"+this.mapInstance.getCenter().lng;
- window.open(url, '_blank');
- }
-
- openInPanoramax() {
- const url = "https://api.panoramax.xyz/#focus=map&map="+this.mapInstance.getZoom()+"/"+this.mapInstance.getCenter().lat+"/"+this.mapInstance.getCenter().lng;
- window.open(url, '_blank');
- }
-
- openInPifometre() {
- const self = this, url1 = "https://nominatim.openstreetmap.org/reverse?format=json&lat="+this.mapInstance.getCenter().lat+"&lon="+this.mapInstance.getCenter().lng+"&zoom="+this.mapInstance.getZoom()+"&extratags=1";
- fetch(url1).then(function (response) { return response.json(); }).then(function (json) {
- const hasInsee = ((typeof json.extratags === 'undefined') || (json.extratags.hasOwnProperty('ref:INSEE')));
- if (hasInsee) {
- const url2 = "https://bano.openstreetmap.fr/pifometre/?insee="+json.extratags['ref:INSEE'];
- window.open(url2, '_blank');
- } else {
- window.alert('Impossible de trouver le code INSEE de la commune…');
- }
- });
- }
-
- openInGeohack() {
- const url = "https://geohack.toolforge.org/geohack.php?params="+this.mapInstance.getCenter().lat+"_N_"+this.mapInstance.getCenter().lng+"_E";
- window.open(url, '_blank');
- }
-
- openInGeoportail() {
- const url = "https://www.geoportail.gouv.fr/carte?c="+this.mapInstance.getCenter().lng+","+this.mapInstance.getCenter().lat+"&z="+this.mapInstance.getZoom()+"&permalink=yes";
- window.open(url, '_blank');
- }
-
- openInMapillary() {
- const url = "https://www.mapillary.com/app/?lat="+this.mapInstance.getCenter().lat+"&lng="+this.mapInstance.getCenter().lng+"&z="+this.mapInstance.getZoom();
- window.open(url, '_blank');
- }
-
- openInGoogleMaps() {
- const url = "https://www.google.com/maps/@"+this.mapInstance.getCenter().lat+","+this.mapInstance.getCenter().lng+","+this.mapInstance.getZoom()+"z";
- window.open(url, '_blank');
- }
-
- openInBing() {
- const url = "https://www.bing.com/maps/?cp="+this.mapInstance.getCenter().lat+"%7E"+this.mapInstance.getCenter().lng+"&lvl="+this.mapInstance.getZoom();
- window.open(url, '_blank');
- }
-
- }
|