Maps - Leaflet Map - Control

Utilisation de la classe L.control pour créer une carte interactive
Sources :

Introduction

Ce tutoriel présente les points d'intérêt de la ville d'Issy-Les-Moulineaux, un panneau de commande situé sur la droite permet de sélectionner les catégories que l'on souhaite afficher. Les différentes catégories et sous-catégories sont représentées par des marqueurs différents et un popup s'affiche lorsque l'on clique sur l'un d'entre-eux pour fournir des informations complémentaires. Le principal intérêt de ce tutoriel est d'introduire la classe L.Control de Leaflet.

Les données

Les données proviennent du site data.gouv.fr et datent de 2014. Nous avons retenu la version JSON mieux structurée que la version CSV. Voici un exemple de ces données. L'objet geometry est directement hérité du format geoJSON et permet de représenter un point sur une carte. L'objet fields est propre à notre jeu de données et contient toutes les informations que nous utilisons quand on clique sur un marker. Comme Leaflet ne permet pas de charger un fichier nous avons copié le contenu geoJSON dans un fichier javascript que nous importons sur la page : geojson-data.js. Ce fichier fournit la variable geojson que nous parcourons dans le paragraphe Préparation des données.

{
	"datasetid": "points-dinteret-de-la-ville-dissy-les-moulineaux",
	"recordid": "ef6036c22c658a7e9ec5417cbc87fb3f92dcbd42",
	"fields": {
		"ville": "Issy-les-Moulineaux",
		"wgs84": [48.822435, 2.249327],
		"description": "Mixte. Ouvert du mardi au samedi de 10h \u00e0 19h sauf le jeudi de 12h \u00e0 20h.",
		"adresse": "3, rue Pierre Poli",
		"telephone": "01 46 42 34 65",
		"longitude": 2.249327,
		"codepostal": 92130,
		"latitude": 48.822435,
		"titre": "Katia Coiffure",
		"categorie1": "Vie pratique",
		"categorie3": "Coiffeurs",
		"categorie2": "Commerces, consommation"
	},
	"geometry": {
		"type": "Point",
		"coordinates": [2.249327, 48.822435]
	},
	"record_timestamp": "2014-08-13T22:12:23+00:00"
}

Les icônes

Il nous fallait trouver un ensemble d'icônes cohérent et assez complet pour représenter les différents points d'intérêt d'une ville comme les commerces, les administrations, les transports, les ensembles sportifs... Nous avons finalement opté pour un jeu d'icônes dont la liste est présentée ci-dessous et qui provient du site templatic.com. Il est possible d'y télécharger un fichier ZIP contenant toutes les icônes au format PNG, ce qui est parfait pour gérer la transparence sur une carte.

Icones du template free-marker-icons

Préparation des données

Notre objectif ici est de trouver une structure de données facilitant les interactions sur la carte. Nous présentons une liste de catégories que l'on peut cocher ou décocher. A chacune de ces opérations nous ajoutons ou enlevons un ensemble de markers sur la carte. De cette description du besoin découle l'objet que nous allons construire, à savoir un tableau contenant pour chaque entrée un identifiant technique, un libellé pour les cases à cocher et un objet provenant de Leaflet L.geoJson. Cet objet dont la documentation peut être consultée sur le site de Leaflet représente un tableau d'objets GeoJSON. Pour chacune de nos catégories il contiendra tous les points d'intérêt associés. Le gros avantage c'est qu'il sera très facile d'ajouter ou d'enlever ces points par la suite car nous utilisons un objet que Leaflet connait. Voici donc le code de construction de notre objet cats.

var cats = [];
for (var i = 0; i < geojson.length; i++) {
	var cat = getCat(cats, geojson[i].properties.categorie2);
	if (cat === undefined) {
		cat = {
			"interestPoints" : createInterestPoints(),
			"id" : "cat" + i,
			"label" : geojson[i].properties.categorie2
		}
		cats.push(cat);
	}
	cat["interestPoints"].addData(geojson[i]);
}

La fonction createInterestPoints retourne l'objet L.geoJson qui sera utilisé par Leaflet. Nous implémentons la fonction pointToLayer afin de modifier l'image associé au marker en fonction de la catégorie (c'est la fonction getIcon qui en fonction de la catégorie, sous-catégorie et sous-sous-catégorie renvoit la bonne icone ). La fonction onEachFeature permet de définir en HTML le contenu de la popup de chaque marker. L'avantage de procéder ainsi c'est que le code ne sera appelé qu'en cas de besoin (si on coche une des cases à cocher ou si on clique sur un marker).

function createInterestPoints () {
	return new L.geoJson([], {
		pointToLayer: function(feature, latlng) {
			var smallIcon = L.icon({
				iconUrl: "/tutorials/maps/interest-point/" + /*icon-provider.js*/getIcon(feature.properties.categorie1, feature.properties.categorie2, feature.properties.categorie3),
				//shadowUrl: 'icon-shadow.png',
				iconSize:     [33, 44], // taille de l'icone
				//shadowSize:   [50, 64], // taille de l'ombre
				iconAnchor:   [16, 44], // point de l'icone qui correspondra à la position du marker
				//shadowAnchor: [32, 64],  // idem pour l'ombre
				popupAnchor:  [-3, -76] // point depuis lequel la popup doit s'ouvrir relativement à l'iconAnchor
			});
			return L.marker(latlng, {icon: smallIcon});
		},
		onEachFeature: function(feature, layer) {
			var html = '';
			if (feature.properties.titre) {
				html += '<b>' + feature.properties.titre + '</b></br>';
			}
			if (feature.properties.description) {
				html += 'Description :' + feature.properties.description + '</br>';
			}
			if (feature.properties.url) {
				html += '<a href="' + feature.properties.url + '" target="_blank">Site Internet</a></br>';
			}
			if (feature.properties.categorie1) {
				html += 'Catégorie 1 : ' + feature.properties.categorie1 + '</br>';
			}
			if (feature.properties.categorie2) {
				html += 'Catégorie 2 : ' + feature.properties.categorie2 + '</br>';
			}
			if (feature.properties.categorie3) {
				html += 'Catégorie 3 : ' + feature.properties.categorie3 + '</br>';
			}
			layer.bindPopup(html);
		}
	});
}

Gestion du panneau de contrôle

Maintenant que nous disposons de toutes nos données dans un format facilement utilisable il ne nous reste plus qu'à créer notre panneau de contrôle. Voici le code complet pour créer la carte et le panneau de contrôle en haut à droite. Pour chaque catégorie, donc chaque élément de notre tableau cats nous ajoutons au panneau une case à cocher.

var stamen = new L.StamenTileLayer("toner-lite");

var map = new L.Map("map", {
	center: new L.LatLng(48.825, 2.27),
	zoom: 15,
	layers: [stamen],
});

var command = L.control({position: 'topright'});
command.onAdd = function (map) {
	var div = L.DomUtil.create('div', 'command');
	div.innerHTML += '<div style="text-align:center;"><span style="font-size:18px;">Points d\'intérêt</span><br /><span style="color:grey;font-size:14px;">(ville d\'Issy-Les-Moulineaux)</span></div>';
	for (var i = 0; i < cats.length; i++) {
		div.innerHTML += '<form><input id="' + cats[i]["id"] + '" type="checkbox"/>' + cats[i]["label"] + '</form>';
	}
	return div;
};
command.addTo(map);

Nous utilisons directement les fonctions de Leaflet pour créer le DIV contenant les checkbox en utilisant L.DomUtil.create(). Le second paramètre correspond à la classe CSS que nous avons définit. Elle est nécessaire pour obtenir un panneau légèrement transparent avec un minimum de style. Nous avons également défini un titre et un sous-titre en HTML.

.command {
	padding: 4px 6px;
	background: white;
	font: 14px/16px Arial, Helvetica, sans-serif;
	background: rgba(255,255,255,0.8);
	box-shadow: 0 0 15px rgba(0,0,0,0.2);
	border-radius: 5px;
	min-width: 200px;
}

Pour gérer la sélection/désélection de catégories, il nous faut ajouter un évènement sur chaque checkbox et ajouter/supprimer les markers en fonction. Notez que cela revient simplement à appeler addTo ou removeLayer sur notre carte en passant l'objet interestPoints correspondant.

for (var i = 0; i < cats.length; i++) {
	document.getElementById(cats[i]["id"]).addEventListener("click", handleCommand, false);
}

function handleCommand() {
	var selectedCat;
	for (var i = 0; i < cats.length; i++) {
		if (cats[i]["id"] === this.id) {
			selectedCat = cats[i];
			break;
		}
	}
	if (this.checked) {
		selectedCat["interestPoints"].addTo(map);
	} else {
		map.removeLayer(selectedCat["interestPoints"]);
	}
}

Conclusion

Ce tutoriel permet de prendre connaissance des fonctionnalités de contrôle sur les cartes Leaflet, il est bien sûr possible d'aller beaucoup plus loin et d'avoir un panneau de contrôle plus complexe intégrant filtres, informations diverses et le tout avec un CSS précis.

COMMENTAIRES

Cat


Bonjour,

Au chargement d'un fichier GPX externe (via API HTML5 comme ci-dessous) si on ajoute "à la volée" une nouvelle case à cocher dans L.Control :

layerControl.addOverlay(e.layer, e.filename);

Comment créer et récupérer une variable polyline pour le trajet qui s'affiche afin d'appliquer une mesure de distance avec un plugin de ce type :
Leaflet.PolylineMeasuredDistance

Merci par avance.

Cat


Bonjour,

Les Layers affichées sur une carte peuvent être des marker WayPoint comme sur ce sujet.

En tant qu'overlayMap dans un L.Control.layers ils peuvent être une trace de chemin ou un simple marker.

Un plugin comme leaflet.filelayer.js ici permet via une API HTML5 de charger des fichiers présents localement (hors serveur) sur un PC tel des fichiers GPX.

Ces fichiers peuvent contenir, outre les points d'intérêts collectés (balises wpt), le tracé complet du chemin parcouru (balises trkpt) ou simplement des points de passage (balises rtept).

Dans les deux cas trkpt ou rtept vont afficher une ligne de point à point.

Comment faire pour neutraliser le chargement et l'affichage des points route (rtept) ?

Merci par avance,

Cat


EDIT du 11/09/2021

Partant du principe qu'une API HTML5 ne se suffit pas à elle seule, elle permet juste de sélectionner un fichier externe.

Constat fait que Leaflet.fileleayer.js est totalement dépendant d'un autre script : togeojson.js qui "parse" le contenu du fichier chargé…Dans son code source figure une boucle de chargement "universelle" d'un fichier XML de structure GPX.

Intervenir sur ce code est du domaine du possible, mais comment ajouter en amont une propriété - lors du chargement de "L.Control.FileLayerLoad.LABEL" dans le fichier HTML de l'utilisateur - excluant le chargement des balises rtept si des balises trkpt existent…

Si réalisable, cela dépasse mon domaine de compétence faute de localiser via console.log LA propriété concernée…

Portion de code vu dans togeojson.js
gpx: function(doc) {
var i,
tracks = get(doc, 'trk'),
routes = get(doc, 'rte'),
waypoints = get(doc, 'wpt'),
// a feature collection
gj = fc(),
feature;
for (i = 0; i < tracks.length; i++) {
feature = getTrack(tracks[i]);
if (feature) gj.features.push(feature);
}
for (i = 0; i < routes.length; i++) {
feature = getRoute(routes[i]);
if (feature) gj.features.push(feature);
}
for (i = 0; i < waypoints.length; i++) {
gj.features.push(getPoint(waypoints[i]));
}

ericfrigot


Bonjour, je tarde un peu à vous répondre mais je pense qu'il faudrait peut-être intervenir en aval, c'est-à-dire après chargement du fichier, grâce à l'évènement data:loaded de la librairie Leaflet.FileLayer.js. Après je ne suis pas sûr que ça fonctionne, est-ce que vous auriez un exemple de fichier qui correspondrait à votre cas ? Je n'ai pas trouvé d'exemple de fichier GPS qui inclue à la fois des balises rtept et trkpt. Eric

Cat


Bonjour,

Merci de l'intérêt que vous portez à cette question.

Faute d'un serveur, je vous adresse exceptionnellement via e-mail un fichier zip contenant un parcours (fichier.gpx) de la route des cols des Pyrénées faute de pouvoir simplifier le trace avec une parfaite visualisation du résultat.

https://uploads.disquscdn.c...

Il est issu d'une conception personnelle depuis le site Tomtom MyDrive qui permet de préparer un trajet puis de le partager sur le cloud ou sur PC puis le transférer sur un GPS.

Les points route (rtept) permettent de démarrer le trajet depuis le point le plus proche de la localisation du GPS, par exemple suite à des routes coupées sur le trajet prévu et donc de disposer d'un nouveau trajet pour relier le prochain point route de la trace initiale (trkpt).

Hormis modification du script togeojson.js ou intervention sur le fichier GPX originel, j'avoue ne pas avoir trouvé comment neutraliser en aval les points route en présence d'une trace, car ils sont lus avant avant cette trace...

PS : Ne perdez pas un temps fou sur cette problématique où ces deux solutions sont possible...

marc ndong


Bonjour , merci pour ce tuto
j'essaie de modifier un peu le projet pour ajouter une image grâce à un lien url avec Geojson dans le popup , quelque pourrez me donner quelque élément de reponses ?
Merci

BELPAIRE Olivier


Bonjour,
A mon tour de vous féliciter pour votre site et vos tutoriels très riches et didactiques.
J'ai adapté avec succès votre exemple d'Issy les Moulineaux avec la classe L.control pour mes besoins propres. Hélas pour aller plus loin je bute sur une difficulté : je dois permettre d'afficher des lignes (polylines) reliant certains marqueurs entre eux. J'ai tenté une fonction createInterestLines avec un "return L.polyLine(..)" dans la partie PointToLayer, qui ne me donne rien (pas d'erreur, mais pas de résultat non plus). Par contre comme certaines lignes utilisent parfois une propriété commune à celle des sites (un seul tableau cats pour tout le monde), quand je coche cette propriété les lignes apparaissent toutes seules (en bleu sans style) sans passer par cette fonction.
Je suis un peu paumé...

BELPAIRE Olivier


Bonjour Eric,
Je reviens sur mon sujet après quelques mois...
J'ai mis en ligne l'exemple que j'essaie de traiter sur la base de votre script original :

Il s'agit d'une carte où l'on veut visualiser des gares et des lignes de chemin de fer (à vol d'oiseau), de différentes catégories.
Les données sont dans le fichier json :

Comme on peut le voir j'ai 2 problèmes :
1) une catégorie fantôme "Gare undefined" que je voudrais supprimer
2) les lignes "voie rapide" et "voie standard" auxquelles j'ai essayé d'affecter un style apparaissent par défaut (en bleu sans fioritures).

Etant autodidacte en javascript, je procède par imitation et modifications mineures, donc là je suis coincé.
Je vous remercie par avance de l'aide que vous voudrez bien m'apporter, si possible.

Bien cordialement,
Olivier

ericfrigot


Bonjour et merci. Est-ce que votre travail est accessible en ligne ? Il est assez difficile pour moi de vous aider sans voir ce que vous avez mis en place. Eric

BELPAIRE Olivier


Je n'ai pas du comprendre comment on insérait un lien sur ce forum.
Je retente :
https://yetanothermap.000webhostapp.com/

BELPAIRE Olivier


ericfrigot


Bonjour, il ne manque pas grand chose à votre code pour corriger ces petits défauts.
Concernant la gare undefined, il faut filtrer les données du geojson après avoir récupéré la nature :
var nature = geojson[i].properties[type];
Ici il faut ajouter un filtre qui va poursuivre sur la prochaine entrée si ce qu'on lit dans le geojson ne correspond pas à une gare
if (nature === undefined) {
continue;
}

Vous avez le même soucis dans la secondes boucle sur les lignes.
La correction de ces deux problèmes pourrait suffire à corriger le soucis que vous avez avec le style de vos lignes.
Eric

BELPAIRE Olivier


Merci beaucoup pour votre aide.
Effectivement, le filtre ajouté résout le problème des "undefined" dans le menu. Ca c'est fait.
Cependant, l'autre problème persiste : les styles ne sont pas appliqués aux lignes. J'ai modifié les noms des propriétés qui ne correspondaient pas à ceux du geojson ("TGV_ligne" , "ITC_ligne") mais rien n'y fait.
Voir ici : https://yetanothermap.000webhostapp.com/

Je me demande si je n'ai pas un problème de fond, en voulant utiliser pointToLayer pour tracer des polylines à la place de marqueurs...
Au passage je ne parviens pas à comprendre d'où sortent les paramètres feature, latlong et layer qui surgissent dans le code sans avoir été définis nulle part : il m'est difficile de débugger ce que je ne comprends pas :(

Merci pour votre patience
Olivier

ericfrigot


Bonjour,
J'ai pris le temps de debugger vos soucis et ça m'a pris un peu de temps mais j'ai résolu vos problèmes et je tiens l'explication.
Premièrement, vous ne pouvez effectivement pas utiliser pointToLayer puisque vous souhaitez ajouter des lignes, du coup la fonction n'est pas utilisée pour les lignes.
Les variables que vous mentionnez (feature, latlong et layer) sont fournies par Leaflet :
- feature correspond simplement au contenu de vos données geojson, c'est donc l'objet que vous voulez ajouter
- layer correspond au layer que vous avez construit, c'est la couche dans laquelle vous voulez ajouter votre objet
- latlng correspond aux coordonnées de votre Point quand vous voulez l'ajouter, c'est bien une propriété de feature.

Maintenant pour la correction remplacez votre méthode createInterestLinks par ça :
function createInterestLinks (link,nature) {
return new L.geoJson([], {
style: function (feature) {
if (feature.properties["TGV_Ligne"] === "rapide") {
return {opacity: 1,weight: 5,color: '#FF6400'};
}
if (feature.properties["TGV_Ligne"] === "standard") {
return {opacity: 1,weight: 2,color: '#006400'};
}
if (feature.properties["ITC_ligne"] === "standard") {
return {opacity: 1,weight: 1,color: '#006400'};
}
}
});

Ca devrait mieux fonctionner, vous laissez Leaflet construire les lignes et vous vous chargez juste de spécifier le style en fonction des propriétés de la ligne.

BELPAIRE Olivier


Bonjour Eric,
Modifié, testé, approuvé....

Merci beaucoup !
Olivier

jjacques mivielle


Une petite erreur concernant la variable cp qui est numérique.
Si la valeur est inexistante, il se produit une erreur.
Et je ne suis pas sur que si le CP = 01240 le résultat donne bien 01240.
je vais voir en plaçant le cp dans une vairable alphanumerique si l'erreur se produit encore. (ref= "codepostal": '.$cp.',)

jjacques mivielle


Bonjour
Effectivement cette variable exprimée en alphanumérique fonctionne mieux.
Une question?
Comment sélectionner toutes les catégories à l'ouverture du script?
Et comment les déselectionner d'une seule opération sans devoir les déslectionner manuellement?
Merci
Cdlmt

ericfrigot


Bonjour, je tarde un peu à vous répondre.
Merci pour le retour sur le code postal et vous avez raison, ça pose soucis avec les codes postaux commençant par 0.
Pour tout sélectionner/déselectionner, le plus simple serait d'ajouter une catégorie "Tout sélectionner" positionner au début de la liste des checkbox. Ensuite on renseigne le cat["interestPoints"] en itérant sur toutes les catégories qu'on a précédemment créé. Le code se positionne donc à la fin de la préparation des données. Pour être propre il faudrait lorsqu'on clique sur cette catégorie spéciale sélectionner/déselectionner toutes les autres checkbox. Ce n'est pas si évident à gérer car cela risque de déclencher l'évènement associé à chaque checkbox.
N'hésitez pas à revenir vers moi en fonction de vos avancées. Eric

jjacques mivielle


Bonjour
Merci pour votre partage. Javascript est moins évident que le PHP mais avec un exemple et vos explications j'y suis arrivé.
J'ai supprimer la reference à la fonction function getIcon(categorie1) car trop longue à adapter.
Par la même occasion j'ai rajouté une trace gpx pour visiter les <ayponts cdlmt="" jj="">

lekerhore


Bonjour,
je tenais simplement à vous remercier pour ce tutoriel que j'ai adapté à celui sur le markersCluster pour répondre parfaitement à mes besoins.
Bonne continuation

ericfrigot


Bonjour, merci pour votre commentaire. Ca fait toujours plaisir. Eric

Pet


Rebonjour,
Je me demandais comment faire pour que quand on "coche vie pratique" tous les "vie pratique" apparaissent et que si je coche "vie pratique" et "éducation" n'apparaissent que ceux qui ont "vie pratique"ET"education" ? Parceque la dans notre exemple toute les occurences ont "vie pratique" en catégorie 1 mais quand on coche vie pratique il n'y a que les aires de jeux qui s'affichent. et quand on coche "vie pratique" et "éducation" on a tous les vie pratique et tous les éducation mais pas ceux qui font les deux ?

ericfrigot


Bonjour,
Les données d'origine possèdent 1188 entrées. Les catégories sont sur trois niveaux et pour information la liste des cases à cocher est basé sur le niveau 2 car effectivement la plupart des données possèdent "Vie pratique" pour catégorie 1 mais ce n'est pas le cas des "Entreprise TIC + 50 salariés" qui possèdent la catégorie 1 "Entreprises". Du coup quand on coche "Vie pratique" c'est seulement les données ayant pour catégorie 2 "Vie pratique" qui apparaissent. Ca peut prêter à confusion effectivement. Vous pouvez très bien ajouter un autre bloc de cases à cocher pour les données "Vie pratique" de catégorie 1 mais ça fera apparaitre vraiment beaucoup de markers. Si vous le faite il faudra dupliquer le code pour prendre geojson[i].properties.categorie1 quand on réparti les geojson par catégorie. Du coup ça répond probablement à votre deuxième question. En espérant que cela puisse vous aider.
Eric

Pet


Merci Encore pour vos réponses rapides. Mais je ne pense pas avoir tout compris. Si je duplique :

var cats = [];
for (var i = 0; i < geojson.length; i++) {
var cat = getCat(cats, geojson[i].properties.categorie1);
if (cat === undefined) {
cat = {
"interestPoints" : createInterestPoints(),
"id" : "cat" + i,
"label" : geojson[i].properties.categorie1
}
cats.push(cat);
}
cat["interestPoints"].addData(geojson[i]);
}


var cats = [];
for (var i = 0; i < geojson.length; i++) {
var cat = getCat(cats, geojson[i].properties.categorie2);
if (cat === undefined) {
cat = {
"interestPoints" : createInterestPoints(),
"id" : "cat" + i,
"label" : geojson[i].properties.categorie2
}
cats.push(cat);
}
cat["interestPoints"].addData(geojson[i]);
}

Si je mets le geojson[i].properties.categorie1 avant le geojson[i].properties.categorie2 rien ne change.
Si je fais l'Inverse : je n'ai plus que "vie pratique" et "Entreprise" qui s'affichent.

Et je me demande même pourquoi ça fonctionne car du coup j'ai crée deux fois la variable cats ?
Après ça vous allez prier pour que je ne fasse pas les autres tuto ... https://media2.giphy.com/me...

ericfrigot


Bonjour,
Pas d'inquiétudes je suis content si je peux vous apporter un peu d'aide.
C'est logique que vous ayez ce comportement car vous avez effectivement la même variable donc vous écrasez son contenu. Il faut par exemple nommer la première cats1 et la deuxième cats2. Ensuite vous devez enrichir le code d'ajout des cases à cocher. Ca donne quelque chose comme ça :

command.onAdd = function (map) {
var div = L.DomUtil.create('div', 'command');
div.innerHTML += '<div style="text-align:center;">Points d\'intérêt
(ville d\'Issy-Les-Moulineaux)</div>';
for (var i = 0; i < cats2.length; i++) {
div.innerHTML += '<form><input id="' + cats2[i][" id"]="" +="" '"="" type="checkbox"/>' + cats2[i]["label"] + '</form>';
}
// Ici vous commencer l'ajout des catégories niveau 1
div.innerHTML += '
Points d\'intérêt niveau 1';
for (var i = 0; i < cats1.length; i++) {
div.innerHTML += '<form><input id="' + cats1[i][" id"]="" +="" '"="" type="checkbox"/>' + cats1[i]["label"] + '</form>';
}
return div;
};

Après il faut aussi gérer les évènements, le plus simple est de réunir les deux tableaux cats1 et cats2 en un seul car ils possèdent les mêmes propriétés.

let cats = cats2.concat(cats1);

Ensuite vous pouvez laisser le reste du code tel quel.
Je n'ai pas testé donc il y a peut être des ajustements à faire.

PS : Le code est modifié par le moteur de commentaire, il manque les balises HTML, quand on ajoute les catégories de niveau j'avais mis une balise BR pour mettre un espace et j'avais aussi ajouté un SPAN indiquant "Catégories de niveau 1".

ericfrigot


Une autre façon de faire plus simple consiste à tout mettre dans le tableau cats mais on modifie le label pour identifier que c'est de niveau 1 :

var cats = [];
for (var i = 0; i < geojson.length; i++) {
var cat = getCat(cats, geojson[i].properties.categorie1);
if (cat === undefined) {
cat = {
"interestPoints" : createInterestPoints(),
"id" : "cat" + i,
"label" : geojson[i].properties.categorie1 + "(niveau 1)"
}
cats.push(cat);
}
cat["interestPoints"].addData(geojson[i]);
}


for (var i = 0; i < geojson.length; i++) {
var cat = getCat(cats, geojson[i].properties.categorie2);
if (cat === undefined) {
cat = {
"interestPoints" : createInterestPoints(),
"id" : "cat" + i,
"label" : geojson[i].properties.categorie2
}
cats.push(cat);
}
cat["interestPoints"].addData(geojson[i]);
}

Pet


Bonjour,
Merci pour ces tuto, je suis au troisième tuto Leaflet de votre site depuis ce matin. Je ne comprend pas encore tout mais sur ce troisième tuto tout fonctionne sauf que les icones ne s'affichent pas, à la place il y a des petite images.

Est-ce ici que ça se joue ? :
var smallIcon = L.icon({
iconUrl: "working/interest-point/" + /*icon-provider.js*/getIcon(feature.properties.categorie1, feature.properties.categorie2, feature.properties.categorie3),
//shadowUrl: 'icon-shadow.png',
iconSize: [33, 44], // taille de l'icone
//shadowSize: [50, 64], // taille de l'ombre
iconAnchor: [16, 44], // point de l'icone qui correspondra à la position du marker
//shadowAnchor: [32, 64], // idem pour l'ombre
popupAnchor: [-3, -76] // point depuis lequel la popup doit s'ouvrir relativement à l'iconAnchor
});
return L.marker(latlng, {icon: smallIcon});

Désolée de vous embêter et merci de prendre autant de temps.

https://uploads.disquscdn.c...

ericfrigot


Bonjour,
C'est effectivement bien ici que le problème doit se situer.
D'abord vous devez avoir téléchargé le pack d'icones, ensuite vous devez placer ce pack d'icones décompressé quelque part ou il est accessible et enfin il faut vous assurez que le chemin est correct en fonction de votre configuration. Si vous utilisez un serveur le chemin se fera depuis la racine du projet sinon il sera relatif à l'endroit ou vous vous situez.
https://uploads.disquscdn.c...
Pour ajuster le chemin en fonction de votre contexte regardez dans l'onglet Network de l'outils de debbugage de Chrome (par exemple) et vous verez les images qu'il n'arrive pas à télécharger. Il vous suffira ensuite d'ajuster ce chemin. Il y a d'ailleurs une petite erreur dans le tutoriel puisque je commence le chemin par working (qui correspond à mon dossier de projets en préparation) alors que sur le site c'est bien maps.
J'espère que ça marchera. Eric

Pet


Merci mille fois pour votre réponse hyper rapide. Le soucis venait du fait que dans le fichier icon-provider j'avais remis le chemin complet... Je vais maintenant essayer d'associer ce tuto avec le second pour faire des cluster... On verra bien si j'ai tout compris.

idrizza


Bonjour !

Merci pour cet excellent tutoriel, que j'ai presque réussi à adapter à mes données ! J'ai un problème avec la fonction handleCommand, que j'ai pourtant copié telle quelle, et voici l'erreur qui ressort quand je coche ou décoche une checkbox : "selectedCat is undefined".

Comme si selectedCat ne prenait jamais la valeur de cats[i] dans la boucle if...

Voici mon code :

function handleCommand() {
var selectedCat;
for (var i = 0; i < cats.length; i++) {
console.log(cats[i])
console.log(this.id)
console.log(cats[i]["id"])
if (cats[i]["id"] === this.id) {
selectedCat = cats[i];
console.log(selectedCat)
break;
}
}
if (this.checked) {
selectedCat["interestPoints"].addTo(mymap);
} else {
mymap.removeLayer(selectedCat["interestPoints"]);
}
};

Tous les console.log renvoient les valeurs attendues, sauf le dernier, juste après selectedCat = cats[i]
Auriez-vous une idée ?

ericfrigot


Bonjour, si vous utilisez Chrome vous pouvez poser des breakpoint dans le code pendant son exécution, c'est parfois plus pratique que les console.log. Si selectedCat reste undefined c'est que vous ne rentrez pas dans le if. Si vous ne rentrez pas dans le if c'est que les conditions ne sont pas remplies. D'après votre commentaire je dirais que vous n'avez pas défini l'id à la construction des cases à cocher. Est-ce que vous avez fait quelque chose comme ça ?
for (var i = 0; i < cats.length; i++) {
div.innerHTML += '<form><input id="' + cats[i][" id"]="" +="" '"="" type="checkbox"/>' + cats[i]["label"] + '</form>';
}

C'est la définition de l'attribut id sur le tag input qui est importante pour le handleCommand

idrizza


Bonjour !

Merci pour le conseil des breakpoint.
J'ai bien utilisé le bout de code que vous mettez, dans le cadre de la définition de command.onAdd :

command.onAdd = function (map) {
var div = L.DomUtil.create('div', 'command');
div.innerHTML += '<div style="text-align:center;">Mon Lyon

Filtrer les lieux :</div>';
for (var i = 0; i < cats.length; i++) {
div.innerHTML += '<form><input id="' + cats[i][" id"]="" +="" '"="" type="checkbox"/>' + cats[i]["label"] + '</form>';
}
return div;
};

ericfrigot


Si vous avez copié/collé votre code, il y a un espace en trop dans la construction du innerHTML : cats[i][" id"], juste avant l'id.
Edit : c'était aussi le cas dans le code que je vous ai donné...

idrizza


Ah, ç'aurait été bien que ce soit ça, mais après vérification aucun espace dans mon code, il a dû s'ajouter au collage dans l'espace commentaires...
Finalement j'ai trouvé un moyen en créant manuellement une couche pour chaque catégorie de données, mais c'est moins élégant que votre solution avec les boucles !

mafoudji kande


Bonjour,

Je reçois une erreur

Uncaught ReferenceError: getCat is not defined

.
Je pense qu'il ne reconnais la fonction getCat :
var cat = getCat(cats, geojson[i].properties.categorie2);
Comment puis-je remédier à cette erreur?

Merci d'avance.

ericfrigot


Bonjour, tout le code source n'est pas présenté dans ce tutoriel. Il faut regarder le code source de la page et chercher la fonction getCat. Je ne l'ai pas mise dans le tutoriel car c'est une fonction utilitaire liée à mes données mais la voici :
function getCat(cats, cat) {
for (var i = 0; i < cats.length; i++) {
if (cats[i]["label"] === cat) {
return cats[i];
}
}
return ;
}
En espérant que vous pourrez poursuivre.
Eric

mafoudji kande


Bien reçu.
Merci vraiment.

Cat


Bonjour,

A moins d'être hors sujet, comme vous me proposez de ne pas hésiter à explorer certains sujets par ici, - sauf erreur il n'existe pas un forum générique - je souhaiterai savoir comment stopper la propagation de double clic, ou clics rapide qui font que l'écran se teinte en bleu si un mouvement de la souris suit y compris les panneaux de contrôles.

C'est particulièrement flagrant lors de l'utilisation de L.circle([Lat, Lng]).

Merci par avance !

PS: Je viens de comprendre ce qui se passe, ce n'est pas visible sur la copie écran jointe, mais le curseur de la souris reste en l'état après un clic (doigt levé au lieu d'une main) de fait lorsqu'on sort de la zone cerclée cela sélectionne fond de carte et / ou contrôles présents. Je cherche la solution pour qu'au relâchement du clic il y ait retour à la normale…. (return false à placer dans le code ?)

Voici une routine test concernée :
var MyCircle = "";

function TracerCercle(C_Lat, C_Lng, C_Km) {

// La fonction attend latitude, Longitude, et une valeur de départ qui aura valeur de Kilometres
// Clic gauche agrandie la zone, Clic droit la réduit jusqu'à retirer le canvas
// preventDefault() ou stopPropagation(e) sans effet si clic rapide ou double clic gauche !
// Par contre L.DomEvent.stopPropagation(e); limite bien l'action sur cette unique function.

if (C_Km > 0) {
MyCircle = L.circle([C_Lat, C_Lng], {
bubblingMouseEvents: false, // code trouvé dans la doc Leaflet mais qui semble sans effet
color: "red",
weight: 2,
fillColor: "blue",
fillOpacity: 0.1,
radius: (C_Km * 1000)
});
MyCircle.addTo(map).on('click', function (e) {
MyCircle.remove(map);
TracerCercle(C_Lat, C_Lng, C_Km + 25)
e.preventDefault();
})
.on('contextmenu', function (e) {
MyCircle.remove(map);
if (C_Km - 25 > 0) {
TracerCercle(C_Lat, C_Lng, C_Km - 25)
}
L.DomEvent.stopPropagation(e);
});
}

Les codes L.DomEvent.stopPropagation(e); et L.DomEvent.preventDefault(e); sont a acceptés mais sans effet.

Le résultat en image :
https://uploads.disquscdn.c...

ericfrigot


Bonjour, pas de soucis pour poster ce genre de questions ici.
Votre exemple est intéressant mais je ne comprend pas très bien le problème en fait.
Par défaut quand on ajoute un cercle avec Leaflet et que l'on passe la souris dessus le cursor va se changer en doigt levé.
J'ai repris votre code sur cette page : https://www.datavis.fr/maps...
Je ne vois pas de problème particulier de comportement même avec des clics rapides.

Cat


Bonjour, Pour information test fait depuis l'url de votre post, sous Windows 10 et le navigateur Edge et le dysfonctionnement constaté est bien reproductible de manière aléatoire.
Il apparait lors de clics droits successifs pour agrandir le cercle puis que l'on déplace ensuite le curseur de la souris hors de la zone du cercle.
Le cursor reste en doigt levé et provoque une sélection des contrôles présents, boutons de zoom et même mention de la provenance code et fond de carte, voire même de toute la page HTML !

Placer un délai d'une seconde ou plus avant appel de la function ne change rien…ce n'est pas la rapidité des clics qui semble en cause.

Suite à une recherche trouvée à ce lien : https://stackoverflow.com/q...
j'ai adapté ce code pour vérifier s'il y a propagation en l'insérant après les lignes d'appel de la function TracerCercle … et ce ne semble pas le cas !

if (window.event.target === e) {
console.log("Propagation : OUI");
} else {
console.log("Propagation : NON");
}

Copie écran jointe : https://uploads.disquscdn.c...

Cat


Bonjour,
Ne plus chercher, car étonnement cette anomalie apparaissait uniquement lors de l'utilisation de Edge avec une souris sans fil ET pile faible !!!

Une mise à jour Windows Update de ce jour corrige ce dysfonctionnement qui était présent uniquement avec Edge !

ericfrigot


Voilà un mystère de résolu alors :)
Très sympa en tout cas l'idée d'utiliser les deux événements pour faire zoom et dézoom

Cat


Bonjour,
Malheureusement le mystère fantôme est réapparu !

J'ai tenté sur l'event click ce code… mais visiblement il ne sert à rien !

L.DomEvent.disableClickPropagation(map);
L.DomEvent.preventDefault(map);

Ce qui est le plus gênant pour moi est de ne constater aucune anomalie en mode débogage d'Edge… Savoir comment faire serait pouvoir.

Autre piste : Ces fonctionnalités seraient-elles à placer sur un autre élément à un autre niveau de la page html ?

PS : Le code mis sur votre forum est bien entendu librement à disposition de tous !

ericfrigot


Bonjour,
J'ai retesté ma page sur le navigateur Edge sous Windows 10 et je n'ai pas réussi à reproduire le problème.
S'il n'y a pas de logs dans la console de débogage d'Edge, c'est probablement parce que le comportement est attendu ainsi.
Est-ce que vous observer le problème sur ma page ou alors uniquement sur votre page à vous ?
Si c'est uniquement sur votre page, il doit y avoir autre chose qui rentre en conflit avec le comportement attendu.
Vous dites aussi que cela provoque une sélection des boutons de zoom et des mentions légales, c'est-à-dire un comportement similaire à ce qu'on obtiendrait avec un CRTL+a pour sélectionner tout le texte de la page mais ça n'est pas relié à un événement de clic à priori.
Avez-vous une souris configurée d'une manière particulière (c'est possible sous Windows) ? Avez-vous essayé sur d'autres navigateurs et sur d'autres postes informatiques ?
Ce sont quelques pistes, bon courage à vous.

Cat


Bonjour,
1- Que doit contenir le fichier icon-provider.js ?
2- Comment récupérer l'élément sélectionné sur un clic sur une case cocher du Control.Layers en javascript ?

var baseLayers = {
"IGN": ign,
"OpenStreetMap": osm
};

var overlays = {
"Wpt": marker,
"Trk": route
};

L.control.layers(baseLayers, overlays).addTo(map);

je n'arrive pas à trouver quel objet ou élément surveiller pour placer un event indiquant la case à cocher cliquée.

Merci par avance.

ericfrigot


Bonjour,
Le fichier icon-provider.js contient uniquement la fonction getIcon qui fournit le chemin de l'icone à afficher en fonction des catégories de niveau 1, 2 et 3. Vous pouvez le voir ici : https://www.datavis.fr/maps.... La gestion des cases à cocher correspond au dernier bloc de code de ce tutoriel. Il faut ajouter un événement sur chaque case à cocher (addEventListener) et y associer une fonction de traitement, dans ce tutoriel c'est handleCommand. Pour préciser les choses, quand nous construisons le panneau de contrôle, on ajoute un id HTML sur l'élément checkbox (cats[i]["id"]), c'est ce qui permet ensuite de placer un événement sur chacun d'entre eux. Eric

Cat


1000 mercis pour cette réponse rapide et bravo pour ce tutoriel en français.
Une découverte pour moi de la possibilité de chargement sélectif des icônes du fichier icon-provider via la fonction onEachFeature !
Effectivement avec votre code exemple, vous créez de toute pièce le Control et ajoutez un id HTML sur l'élément checkbox (cats[i]["id"]), qui permet ensuite de placer un événement sur chacun d'entre eux…
Mais le code Leaflet dispose d'une fonction toute faite : L.control.layers pour cela :
https://leafletjs.com/refer..."
Avec ce control, y-a-t'il impossibilité de détecter un clic sur une case à cocher, pour surveiller le mode coché ou non, voire le chargement ou déchargement des overlays associés sur la carte, comme pour un changement de fond de carte ?
objet.on('click', function (e) { ...; });
Je ne trouve aucune réponse sur ce point dans la documentation sur le nom de l'objet à mettre dans le code … pourtant en debug on trouve ceci :<input class="leaflet-control-layers-selector" type="checkbox">

ericfrigot


Bonjour, pouvez-vous me dire ce que vous voulez "écouter" comme événement sur l'exemple de Leaflet : https://leafletjs.com/examp... ?

Cat


Bonjour, Merci de l'intérêt que vous portez à mon interrogation et du temps consacré.
Dans l'exemple de Leaflet, je souhaite écouter la sélection des cases à cocher overlayMaps
var overlayMaps = {
"Cities": cities
};

Pour ne pas surcharger le control.layer, dans cette partie de code je place :
1- Une liste de trajets - L.Polyline (2020-04-10 2020-04-11 etc..)
2- A la fin, la globalité des points d'intérêts (POIs) de tous les trajets - markersArray(WayPoints, IconWpt)
Sélectionner POIs affiche l'intégralité des WayPoints.
Si POIs coché, pouvoir détecter la sélection d'un ou plusieurs trajets me permettrait de ne laisser affiché que ceux correspondants aux seuls trajets sélectionnés en actualisant la liste des WayPoints

ericfrigot


Bonjour, j'ai finalement compris ou vous vouliez en venir et merci à vous d'avoir insisté sur le sujet ! Je vais mettre à jour le tutoriel pour utiliser directement la fonctionnalité d'overlayMaps. Dans l'utilisation de Leaflet nous n'avons pas la main sur l'événement de click, c'est Leaflet qui gère tout il me semble.Je vais essayer de regarder ça dans la semaine.

Cat


Bonjour,
Pour information j'ai trouvée une solution en simple javascript qui mérite d'être affinée pour surveiller un clic non pas sur tout l'objet document, mais uniquement dans le control.layer présent. En l'état cela fonctionne, mais la solution me semble "tordue" !

Je teste si affichage ou non des overlayMaps via hasLayer :

// Création du Control.Layers avec les tableaux
var overlayMaps = {
// Remplir selon variables ci-dessous
};

overlayMaps["" + DateGPX[0] + ""] = TraceGPX[0];
overlayMaps["" + DateGPX[1] + ""] = TraceGPX[1];
overlayMaps["" + DateGPX[2] + ""] = TraceGPX[2];
overlayMaps["POIs Intéressants"] = WayPoints;

// Ajout Control.Layers Leaflet
map.addControl(new L.Control.Layers(baseMaps, overlayMaps, { collapsed: false }));

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Routine de détection des Layers visible
// pour pouvoir ensuite adapter la liste Waypoints - si sélectionné - uniquement pour les circuits (Traces) affichés
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

document.onclick = function () {
// surveiller liste des overlayMaps visible ou non
for (i = 0; i < TraceGPX.length; i++) {
// Si layer affiché récupérer le numéro (i) de la case cochée
if (map.hasLayer(TraceGPX[i])) {
console.log("Case " + i + " cochée" + map.hasLayer(TraceGPX[i]));
// en profiter pour un Zoom sur affichage de la sélection cliquée
var bounds = TraceGPX[i].getBounds();
map.fitBounds(bounds);
}
}
console.log("WayPoints " + map.hasLayer(WayPoints));
}

En fait mon interrogation est comment limiter la surveillance d'un clic uniquement sur le control.layer via elementByName input ce nom s'avère inconnu ou inaccessible, idem via un byClassName("leaflet-control-layers-selector[i]) ?

Bon usage de cette information

ericfrigot


Bonjour, j'ai trouvé un moyen plus précis de faire ce que vous voulez. map.on("overlayadd", function(e) {console.log(e.name);}); Voir la documentation associée : https://leafletjs.com/refer.... Le code affiche dans la console le libellé de la case qui a été cochée, vous avez la même chose avec "overlayremove" quand la case est décochée. L'événement contient aussi l'objet Layer qui est ajouté ou supprimé.

Cat


Bonjour, C'est l'event, objet de mes convoitises depuis plusieurs mois de recherche et qui répond parfaitement à ma problématique ! Difficile de faire plus précis et simple…

Chapeau bas et Respect à vous pour votre pédagogie et votre aide au travers votre pépite de site en français, qui fourmille d'exemples de codage particulièrement clairs et bien expliqués ! Votre site fait désormais l'objet de ma bible de favoris.

Vous avez l'intelligence du Savoir et le Savoir de l'intelligence. Malgré mes 35 années de codage dans des langages différents cela reste toujours pour moi la science de la modestie. C'est notre partage, la découverte d'autres logiques de code qui m'ont toujours fait progresser. Ne changer rien !

Pour ma part c'est l'obligation d'une clé pour Google qui ma permis de découvrir Leaflet. Malheureusement mes limites dans l'Anglais "non technique" font qu'à la lecture de leur documentation, le mot "Fired" ne m'a pas été "parlant" car traduit en français comme "Tirer" ou "Mis à la porte". Un exemple de code est plus évident à comprendre.

Vous pouvez considérer ce sujet comme clos.

Encore 1000 Mercis !

ericfrigot


Bonjour, merci pour votre message et content d'avoir pu vous aider. J'ai un peu le même parcours concernant la découverte de Leaflet, c'est bien dommage que Geoportail, malgré les millions investis ne soit pas plus facile à utiliser et mieux maintenu. Vous m'avez fait réfléchir, j'ai maintenant 20 ans de codage derrière mois et quand j'ai ouvert ce site je voulais effectivement écrire des tutoriels en français qui soient à jour et complets. Quand je regarde les statistiques je suis très content de voir qu'après la France c'est du continent Africain que vienne la plupart des visiteurs ! Bon courage à vous pour la suite et n'hésitez pas si vous voulez explorer certains sujets à en parler par ici !

Loic


Bonjour,

Très bon tuto que j'ai réussi à adapter à mon usage. Je sèche cependant sur un point : l'espace entre les catégories dans le panneau de contrôle. Sur l'exemple chaque catégorie est bien serré. Sur ma version c'est très espacé en hauteur. Comment je pourrais régler ça ? J'ai regardé du côté de classe CSS command mais sans succès... Par avance merci

ericfrigot


Bonjour et merci ! Concernant l'espacement entre les catégories, effectivement je n'ai pas ajouté de CSS particulier. Il est fort probable que votre version s'insère dans une page qui hérite de propriétés CSS spécifiques, qui viennent d'un framework particulier ou d'un CSS plus gobal.

Christophe Raverdy


Bonjour.

Merci beaucoup pour le tutoriel que j'ai réussi à adpater avec un peu d'efforts. J'ai donc généré un fichier geojson qui me permet d'afficher entre 1000 et 10000 items avec filtrage par cases à cocher. J'ai par contre un problème lorsque plusieurs items partagent les mêmes coordonnées gps : Un seul d'entre eux apparait sauf si je choisis uniquement un seul type d'item. Est-ce normal ? Une piste ? par avance, merci

Christophe Raverdy


Je me permets de préciser ; Je ne vois pas comment on peut associer la gestion par catégories (afficher/masquer des groupes) et l'extension markercluster

ericfrigot


Bonjour Christophe et merci pour votre réponse à Yvon.
Votre problème est moins technique que fonctionnel. Il est normal qu'un seul d'entre eux apparaisse et j'imagine qu'il s'agit du dernier ajouté. Ce n'est pas évident à résoudre par contre ! La meilleure solution serait : s'il y a deux points d'intérêts au même endroit je met une icone dédié qui cumule les deux icônes associées aux deux points d'intérêts mais ce n'est pas faisable. Vous pourriez peut-être avoir une icône dédiée et quand on clique dessus vous utilisez la librairie spiderfy pour "ouvrir" le marker et montrer les icônes sous-jacentes (https://github.com/jawj/Ove... ). Ca vous impose de parcourir vos données à l'avance pour savoir si vous aller rencontrer un tel cas suivant les cases qui sont cochés.

Christophe Raverdy


Bonjour.

En me rappelant de mes cours d'algorithmique, j'ai commencé il y a deux jours à revoir l'approche prépaparatoire (ok depuis 1h15 cette nuit) : un 1er script en php compte le nombre d'occurrence, le second collecte les éléments essentiels et prépare une <ol> synthétique associée à une propriété "Autres Equipements". Solution relativement propre à l'arrivée. Merci encore pour le débrouss https://uploads.disquscdn.c... aillage sur L.Control.

Yvon Yves Noel OVONO BENG


Bonsoir, je suis encore dans la galère avec ce code.
Je peux avoir le code de ton fichier ?

Christophe Raverdy


Bonjour.

Normalement, j'ai retrouvé la version déposée à l'époque. Archive disponible pour une semaine via wetransfer

Cdt

Yvon Yves Noel OVONO BENG


Bonjour Christophe et Merci pour le code.
Un autre soucis s'il vous plaît.
Pour récupérer les données dans la BD avec php je n'ai pas de problème. Ce pendant le fichier .js que l'application utilise est statique. je voudrais insérer le résultat de ma requête dans le fichier json pour afficher dans la carte.


require('db.php');
$res = $db->query('SELECT idEcole, nomEcole, lat, lng, CONCAT(nomNature," " ,nomSousSysteme) AS type, tel
FROM ecole e
INNER JOIN nature n ON (e.idNature=n.idNature)
INNER JOIN soussysteme s ON (e.idSousSysteme=s.idSousSysteme)
WHERE idArrond = 12');


$data = array();


while ( $row = $res->fetchAll(PDO::FETCH_ASSOC)) {


$data[] = $row;
//Afficher le tableau au format JSON
$EquipementsData = json_encode($data) ;


?>

<script>
var EquipementsData = [{ "type": "Point",
"coordinates": [2.3410459180344, 48.860080644116],
"proprietes":


{


"nomEcole": "DFPE",
"C_AR": "1",
"C_QUINSEE": "7510101",
"L_QU": "Saint-Germain-l'Auxerrois",
"TypeEquipement": "16312",
"type": "Centre de protection maternelle et infantile",
"CodeEquipement": "308",
"Designation": "Locaux des puéricultrices 1",
"Source": "SANTE",
"DesignationCourt": "Locaux PMI 1",
"ServiceGest": "DFPE",
"Statut": "en service",
"Collectivite": "Commune",
"Proxi": "NON",
"Longitude": "2.3410459180344",
"Latitude": "48.860080644116",
"Cluster": "<ol><li>ACTION-SOCIALE - Centre d'action sociale : 8559 - CASVP d'arrondissement 75001 (Commune - CASVP - DPSP)</li>
<li>CULTURE-ET-LOISIR - Bibliothèque : 3123 - BIBLIO. MUN. DU LOUVRE (Commune - DAC - DAC)</li>
<li>ESPACE-VERT-BOIS - Terre-plein, jardinière, plate-bande : 7106 - EV DECORATION MAIRIE 1ER ARRDT (Commune - DEVE - MAIRIE DU 1ER)</li>
<li>MAIRIE-SERVICE-MUNICIPAL - Mairie d'arrondissement : 4038 - MAIRIE DU 1ER ARRONDISSEMENT (Commune - DDCT - MAIRIE DU 1ER)</li>
<li>SANTE - Centre de protection maternelle et infantile : 308 - Locaux des puéricultrices 1 (Commune - DFPE - DFPE)</li>
<li>SERVICE-REGALIEN-NATIONAL-et-INTERNATIONAL - Tribunal d'instance : 4169 - Tribunal D INSTANCE 1ER ARRONDISSEMENT (ETAT - DCPA - DDCT)</li></ol>"


}
}
];


</script>


CloseCursor();
?>

Cordialement

ericfrigot


Bonjour, Avec plaisir ! Et votre solution (fonctionnelle du coup) semble bien adaptée à votre contexte.

Christophe Raverdy


Bonjour.

Extension aujourd'hui en exploitant les distances entre 2 coordonnées gps et trier en fonction de l'éloignement : Cette logique mise en place, mes utilisateurs auront, en fonction de leurs centres d'intérêt une vue immédiate sur leur environnement. J'exploite un fond de carte ainsi que des couches pour les limites d'arrondissements et de quartiers administratifs mais pour les utilisateurs l'essentiel réside dans le panneau de contrôle. https://uploads.disquscdn.c...

Yvon Yves Noel OVONO BENG


Bonsoir,tu as fais comment pour afficher ta carte ? Je suis coincé là.
comment puisez depuis la base de données ?
Je peux avoir tes fichiers et ta base de données pour comprendre comment tu as fait ?

Christophe Raverdy


Bonjour.

En fait, j'ai repris le tutoriel pour Issy-les-Moulineaux en personnalisant petit à petit.

En amont : il y a du code en php pour gérer une base de données et produire un fichier au format geojson
Le code html comprend 3 parties : L'intégration des différents fichiers de données, la div qui intègre la carte html et le code javascript qui va relier les propriétés leaflet.

Via https://we.tl/t-jZ6321yn2z jusqu'au 25-04-2020 : html et fichiers dépendants pour visualiser la carte, code php et jeu de données + base de données mysql. Par contre je précise qu'il s'agit d'un travail (sur leaflet) commencé à l'été 2018 : Beaucoup d'heures de travail pour comprendre, personnaliser et progresser. J'espère que mon code sera utile mais je recommande d'imaginer un projet personnel proche de ce que je présente pour adapter petit à petit. Egalement, ne pas hésiter à commenter le code et à sauvegarder avec des dates de version.

J-N NAL


Bonjour, je tombe sur cette intéressante discussion désirant mettre en ligne une carte des parcelles cadastrales "IGN".

Mais en tant que novice dans l'utilisation de Leaflet, j'avoue que je suis un peu perdu dans ces éléments de code. Ta proposition de mise en ligne de ton travail me serait très utile, afin d'avoir une vision d'ensemble de la question.
Arrivant après la date "butoir", pourrais-tu refaire un téléchargement?
D'avance Merci.

PS: Je suis conscient de la nécessité d'un travail d'appropriation des techniques, mais là mes capacités sont un peu dépassées, soyez indulgents.

Christophe Raverdy


Lien actualisé à l'instant : https://we.tl/t-3BL7ON7skH

A noter : Pour mon travail, je devrais moi aussi exploiter le cadastre (en lien avec la publicité foncière) mais l'association avec une adresse (BANO) n'est pas évidente et devrait nécessiter pas mal de calculs en amont.

J-N NAL


Merci pour ta réactivité!
Je télécharge immédiatement!
:)

Christophe Raverdy


Je reste disponible s'il faut commenter le code php. Concernant l'utilisation du code javascript il ne devrait pas y avoir trop de difficultés en se basant sur l'exécution en local et sur la documentation du code par l'auteur du tutoriel.

Bruno


Bonjour,
Désolé de détérrer un peu ce post.
Je cherche à remplacer mon code PHP (affichage avec API Google Maps de positionnement de maisons en lien avec des données cadastrales) par du code PHP utilisant Leaflet.
D'après vos échanges, Christophe et J-N, je serais preneur de tout code pouvant m'aider à démarrer avec ces éléments.
Si c'est possible de pouvoir disposer d'un lien de téléchargement à jour ?
Ce serait cool.
Merci !

Christophe Raverdy


Le lien de téléchargement actualisé pour une semaine : https://we.tl/t-djMXN9M2yE : Codes html+css fonctionnels, tableaux au format csv, base de données (format sql) et code php. Il s'agit d'un travail personnel mais je n'y serais pas parvenu sans le tutotiel appliqué à Issy-les-Moulineaux.

Bruno


Merci Christophe.
C'est cool !
Excellent boulot !!
Je vais voir ça de plus près asap !

J-N NAL


Tu as fait un travail impressionnant!
Je vais essayer de le comprendre...
Merci pour ton obligeance.

Yvon Yves Noel OVONO BENG


Pourquoi est-ce que tu mystifie la chose ?
C'est quoi ce mystère ?
Me donner juste le contenu du fichier et adapter à mon projet te dépasse ?
Ça ne fait pas avancer la communauté.

Cordialement.

Christophe Raverdy


Bonjour.

J'ai juste répondu à la demande "Je peux avoir tes fichiers et ta base de données pour comprendre comment tu as fait ?"

Après, la programmation dynamique dans la carte affichée se fait via javascript et ses différentes bibliothèques (leaflet, jquery...) et je
maintiens qu'il y a un travail forcément conséquent d'appropriation et de débogage via la console web.

J'ai pensé qu'avoir l'ensemble y compris le jeu de données de base, les scripts php qui font les transformations et produisent le fichier de données permettait de s'en servir de bac à sable avec du code (html, javascript) opérationnel.

En ce sens, je pense (sous toutes réserves) que j'aide davantage la communauté qu'en faisant un travail pour un client qui veut juste voir le résultat mais ne s'intéresse pas au "making-of".

Si procéder ainsi n'est pas adapté j'en suis désolé, mais je ne vois pas comment faire différemment.

Cdt.

Yvon Yves Noel OVONO BENG


Effectivement j'ai juste 5 catégories à gérer :
- Écoles Maternelles Anglophones
- Écoles Maternelles Francophines
- Écoles Primaires Anglophones
- Écoles Primaires Francophones
- Écoles Bilingues

Et je veux faire la même chose que ce que vous avez fait là.

Cordialement.

Christophe Raverdy


Qu'il s'agisse du code pour Issy-les-Moulineaux ou du mien la génération des catégories est dynamique à partir du parcours de l'ensemble des équipements (il m'a fallu du temps pour comprendre cette subtilité)

Yvon Yves Noel OVONO BENG


C'est de la faisabilité dont j'ai besoin.

Yvon Yves Noel OVONO BENG


Bonsoir, très bon cours. Mais je ne comprends pas comment tu puises les données dans la base de données.
Merci

ericfrigot


Bonjour Yvon,
Christophe t'a déjà répondu de manière satisfaisante il me semble. Plutôt que de charger le fichier JSON directement avec d3js j'ai préféré copier son contenu dans un fichier javascript (https://www.datavis.fr/maps.... Ce fichier est importé au début de ma page, ainsi les données sont directement accessibles avec la variable geojson.

Yvon Yves Noel OVONO BENG


Bonsoir. parait-il que vous ne comprenez pas ma préoccupation.
au cas où mes sont dans ma BD local par exemple. Comment appeler mes données ?

ericfrigot


Bonjour Yvon,
Pour accéder à vos données il faut effectivement suivre les conseils de Christophe.
Déjà votre base de données c'est du MySQL, du PostgreSQL ou un autre type de language ? Ensuite vous voulez accéder à vos données, mais vous utilisez quel langage pour votre page ? Il vous faut du PHP au minimum pour interroger une base de données. Pour du PHP et une base MySQL vous pouvez par exemple regardé ce tutoriel : https://openclassrooms.com/...
Eric

Yvon Yves Noel OVONO BENG


Je n'ai pas de problèmes pour afficher les données de la base de données sur page Web avec php.
Dans le tuto je ne vois pas là où on se connecte à la base avec PDO ou autres.

ericfrigot


Bonjour Yvon, dans mes tutoriels, les données ne proviennent jamais d'une base de données mais toujours d'un fichier situé sur le serveur. J'ai mis à jour la section sur les données pour que cela soit plus clair.

Yvon Yves Noel OVONO BENG


Bonjour, OK. Maintenant si un utilisateur lambda(non informaticien) veut modifier les coordonnées de son école à partir d'une page Web, comment le fera-t-il ?

Christophe Raverdy


A la base, les données sont accessibles via https://www.data.gouv.fr/fr... (fichier JSON). La lecture du code source renvoie vers un "maps/leaflet-control/geojson-data.js" qui commence par "geojson =" et se prolonge par le fichier JSON précité.

Александр Косько


Comment activer un drapeau au début, où et quoi entrer?

ericfrigot


Bonjour, vous devriez consulter les précédents tutoriels pour savoir comment ajouter un marker and if you want to speak english you can.

gilles goetzmann


Bonjour et merci pour ce tuto.
J'ai simplement un peu de mal à voir comment est structuré le fichier....est-il possible d'avoir un code source pour comprendre ?

Merci d'avance

ericfrigot


Bonjour, je ne suis pas sûr de savoir de quel fichier vous parlez. Mais s'il s'agit du code source de la page (bouton droit sous Google Chrome "Afficher le code source de la page"), il faut regarder vers la ligne 263 ou débute la balise <script type="text/javascript">. Tout le code qui suit correspond à ce tutoriel jusque la fin de la balise. N'hésitez pas si ce n'est pas ce que vous cherchiez.

PSGvore


Bonjour, Très bon tuto. J'aurai néanmoins une question : est-il possible de rajouter un titre ou des sous-titres au panneau de contrôle ?
Résolu pour le titre en mettant cette ligne :
div.innerHTML = '<div style="font-size:17px;font-weight:bold;margin-bottom:10px;">Points d\'intérêts :</div>';
avant la boucle for du panneau de contrôle.

ericfrigot


Bonjour, merci pour votre commentaire ! Je viens de modifier le tutoriel pour ajouter un titre et un sous-titre en reprenant votre solution.

jhon barera


Bonjour. J'aimerais d'aide sur l'affichage des markers récupérer depuis la base de données et mis sous format Json

ericfrigot


Bonjour, il va me falloir un peu plus d'informations. Comment sont vos données ? Est-ce que vous avez bien une latitude et une longitude ? Ou est-ce que vous êtes bloqué ? Finally, do you prefer if we speak english ?

Erwan Withyou


Bonjour, tout d'abord merci pour cet excellent tutoriel.

J'ai un projet similaire à réaliser, à la différence que je dois afficher tous les points d'intérets au niveau national et pas juste à une ville, savez-vous si cela est possible ?

Merci d'avance

ericfrigot


Bonjour,
C'est tout à fait possible de réaliser un tel projet. Qu'est-ce qui vous pose problème ? Si vous avez les coordonnées géographiques de vos points/markers c'est gagné. Il faudra tenir compte du nombre de markers que vous voulez afficher et peut-être passer par une technique de regroupement si vous en avez beaucoup. Eric

Erwan Withyou


Merci pour votre retour, alors en fait j'ai créé un fichier avec les points d'intérets en France à partir du site https://public.opendatasoft..., or je n'arrive pas à l'utiliser et à faire apparaitre les différents marqueurs sur la carte, avec le code du tutoriel, je n'arrive pas à saisir d'où vient l'erreur sur le fichier, je vous montre ma première ligne de donnée, peut-être verrez vous l'erreur,

geojson = [{
"datasetid": "points-dinterets-openstreetmap-en-france",
"recordid": "6d68015f29ce94b33344b0b2e6e1a284428466e8",
"fields": {
"amenity": "school",
"other_tags": "{\"school:FR\": \"coll\\u00e8ge\", \"name\": \"Jean Ladignac\", \"ref:UAI\": \"0240065R \"}",
"timestamp": "2013-03-26T18:15:23+00:00",
"geo_point_2d": [44.8678454, 1.0366251],
"user": "CCJesus",
"geo_shape": {"type": "Point", "coordinates": [1.0366251, 44.8678454]}, "id": "1535651807"},
"geometry": {"type": "Point", "coordinates": [1.0366251, 44.8678454]},
"record_timestamp": "1970-01-01T00:00:00+00:00"
}];

Encore merci, en espérant que vous pourrez m'aider.

ericfrigot


Bonjour, votre geojson est compatible avec la librairie Leaflet, pas de soucis. En revanche il y a des modifications à apporter à mon code, est-ce que vous les avez faites ? Par exemple dans mon code j'accède à feature.properties.categorie1 que vous n'avez pas dans votre fichier. Autre question, est-ce que vous avez ouvert la console de votre navigateur pour voir s'il y avait des questions erreurs de javascript ? Si vous voulez que je jette un oeil à votre code il faudrait le mettre à disposition sur Internet pour que j'y accède.

Marceau


Bonjour,

Tout d'abord je trouve votre travail très bien réalisé et plus qu'utile.

J'ai souhaité ajouter à votre exemple une (plusieurs) fonction permettant de n'afficher que les points présents sur l'écran de l'utilisateur (bbox). Le problème étant que le panneau de contrôle se créé à chaque fois que l'utilisateur déplace la carte. Auriez-vous une idée pour, soit

- supprimer le panneau de contrôle à chaque fois que l'utilisateur déplace la carte, puis le recréer (j'ai essayer de plusieurs façon les méthodes removeFrom() et remove(), sans succès;
- soit faire en sorte que le panneaux de contrôle ne se créer qu'une seule fois (j'ai essayé de mettre en place en booléen, qui, une fois le panneau créer reste sur false (sans succès);
En tout cas, encore merci pour votre travail.

Marceau


Bonjour, si cela intéresse quelqu'un, j'ai résolu mon problème assez simplement.






var command = L.control({position: 'topright'});


...


if (commande) {
commande.removeFrom(map);
}
commande = command.addTo(map);




Voila, et encore merci !

ericfrigot


Bonjour et merci pour vos commentaires.
Content d'apprendre que vous avez trouvé une solution à votre problème, j'avais tout juste commencé à regarder.
Eric

algal


Bonjour,

Serait il possible d'avoir le code complet et opérationnel de votre exemple pour pouvoir travailler dessus ?

ericfrigot


Bonjour,
Les tutoriaux de ce site contiennent à la fois le texte du tutoriel et le code nécessaire à leur fonctionnement.
Néanmoins il est assez facile d'isoler le code du tutoriel. Je vous donne quelques indications et vous me direz si vous vous en sortez :
- Il faut commencer par afficher le code source de la page actuelle
- Lignes 53 à 56 vous trouverez l'import du CSS de leaflet, du JS de leaflet, du fichier de données geojson-data.js ainsi que la classe fournissant les icônes.
- Lignes 58 à 75 se trouve le CSS utiliser pour la carte et le panneau de commande
- Ligne 84 on déclare le DIV utilisé pour la carte
- Enfin lignes 261 à 377 se trouve le code JS complet pour faire fonctionner le tout
Il vous faudra par ailleurs récupérer l'ensemble des icônes et surement mettre à jour quelques chemins relativement à votre projet.

algal


Bonjour, Excellent tutorial, je suis arrivé à mes fins, merci beaucoup !

Loraine Grand


Bonjour, j'aimerais appliquer votre script à un autre ensemble de données mais je suis bloquée au niveau de la création des icônes (je ne souhaite pas utiliser d’icônes personnalisées). Pourriez vous mettre à disposition les codes html/css/js sans l'utilisation des icônes?
Merci !!

ericfrigot


Bonjour, si c'est la seule modification que vous voulez apporter c'est assez simple. Dans la fonction createInterestPoints, il faut changer pointToLayer avec le code suivant :
pointToLayer: function(feature, latlng) {
return L.marker(latlng);
}
Vous aurez ainsi les icônes standards de Leaflet.

Didier Willart


Bonjour,
Votre script est très intéressant.
Je cherche à faire quelque chose de ressemblant, mais avec des données provenant de fichiers de type GeoJSON.
Je souhaite afficher un fond de carte avec un menu comme le vôtre mais dans lequel chaque checkbox correspond à un fichier externe.
Pensez-vous que cela est possible ?
Merci.
Didier

ericfrigot


Bonjour Didier,
Oui c'est bien sûr possible mais pour connaître la bonne façon de faire je vais avoir besoin de plus d'informations. De prime abord j'aurais chargé tous vos fichiers geoJSON afin de les mettre dans un tableau (cela évite de devoir les recharger à chaque fois qu'on coche ou décoche une checkbox). Mais cela dépend du nombre de fichiers que vous avez, de leur taille et de ce qu'ils contiennent. Est-ce que vos fichiers contiennent bien des points ou est-ce un autre type d'information ? Si vous avez du code ou des exemples de fichiers à me montrer, ça pourrait aider aussi.
Eric

Didier Willart


Bonjour,
Merci pour votre réponse.
Mais à force de recherches et de ténacité (je ne suis pas programmeur mais bidouilleur), j'ai fini par trouver.
Et ça fonctionne !
En opérant de la manière ci-dessous.
Merci. Vous m'avez fait avancer.
Didier.

/* Définition des deux fonds de carte */
var satellite = L.tileLayer('https://wxs.ign.fr/{API Key}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET=PM&FORMAT={format}&LAYER=ORTHOIMAGERY.ORTHOPHOTOS&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}', {
attribution: 'Geoportail France',
bounds: [[-75, -180], [81, 180]],
minZoom: 2,
maxZoom: 19,
apikey: 'choisirgeoportail',
format: 'image/jpeg',
style: 'normal'
}),
ign = L.tileLayer('https://wxs.ign.fr/{API Key}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET=PM&FORMAT={format}&LAYER=GEOGRAPHICALGRIDSYSTEMS.MAPS&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}', {
attribution: 'Geoportail France',
bounds: [[-75, -180], [81, 180]],
minZoom: 2,
maxZoom: 18,
apikey: 'choisirgeoportail',
format: 'image/jpeg',
style: 'normal'
}),
osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
});

/* Construction de la carte */
var newMap = L.map('map', {
center: [43.550414,4.992599],
zoom: 9,
layers: [osm]
});

/* Construction du Contrôle des cartes */
var baseLayers = {
"OSM": osm,
"Satellite": satellite,
"IGN Topo": ign
};

/* Construction du Contrôle des couches */
var cantons = new L.layerGroup();
var communes = new L.layerGroup();

/* Récupération de la couche des Cantons */
var cantons_style = {
"color": "#14F032",
"weight": 2,
"opacity": 0.65,
"fillOpacity": 0.00
};

$.getJSON("../zones/cantons.geojson",function(data){
L.geoJson(data, {style: cantons_style, onEachFeature: function(feature, featureLayer) {featureLayer.bindPopup(feature.properties.Name);}}).addTo(cantons);
});

/* Récupération de la couche des Communes */
var communes_style = {
"color": "#FFFFFF",
"weight": 2,
"opacity": 0.65,
"fillOpacity": 0.00
};

$.getJSON("../zones/communes.geojson",function(data){
L.geoJson(data, {style: communes_style, onEachFeature: function(feature, featureLayer) {featureLayer.bindPopup(feature.properties.Name);}}).addTo(communes);
});

/* Construction du pavé de contrôle */
var overlays = {
"Cantons": cantons,
"Communes": communes
};

/* Affichage du Pavé de Contrôle */
L.control.layers(baseLayers, overlays).addTo(newMap);

ericfrigot


Bonjour Didier,
Content que vous ayez pu résoudre vos problèmes, pour un bidouilleur vous avez plutôt de bonnes pratiques de programmation (nommage cohérent, commentaires et aération du code). Je vois maintenant ce que fait votre code.
Bonne continuation,
Eric