Leaflet - Utilisation de la classe L.Control

Points d'intérêt de la ville d'Issy-Les-Moulineaux

Dernière mise à jour le 23/06/2018
leaflet1.3.1

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.

{
	"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.

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: "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});
		},
		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');
	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.

.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.

comments powered by Disqus