Makina Blog
Infrastructure web mapping minimaliste (partie 3) - Leaflet
Continuant notre série d'article sur l'application web mapping du CAUE24, nous présentons le code Leaflet produit.
Note : l'intégralité du code présenté dans cet article est consultable sur le dépot Github du projet ou directement sur le site.
Afficher une carte
Afficher une carte avec Leaflet est très simple, l'API étant vraiment facile à utiliser. Avant d'indiquer les 2 lignes à utiliser, nous devons préciser que comme nous restons sur la même page web, tout en changeant "virtuellement" de page, nous devons régulièrement réinitialiser notre carte. Cela nous permet également de factoriser une partie de code de création de carte :
// Variables globales. var CaueViews = {}; // Contiendra nos fonctions. var map; // Contiendra notre carte. CaueViews.initMap = function(category) { // Supprime l'éventuelle instance existante. if (map instanceof L.Map) { map.remove(); } // Initialise la nouvelle instance map = L.map('map'); // Ajoute les réglages communs à toutes nos cartes, comme la gestion de l'échelle. L.control.scale({imperial: false}).addTo(map); }
Grâce à l'utilisation de la variable globale "map", nous pouvons y accéder de tout notre scope et réaliser parfois des traitements complexes impossibles autrement. Maintenant que nous disposons d'une fonction réutilisable, pour afficher une carte en utilisant l'API Leaflet, nous n'avons besoin que des quelques lignes suivantes :
CaueViews.displayHomePage = function() { // On initialise la carte. CaueViews.initMap(); // On la centre. map.setView([45.10, 1.57], 9); // On ajoute le fond de carte. var caueUrl = 'http://{s}.livembtiles.makina-corpus.net/caue24/CAUE/{z}/{x}/{y}.png'; var caueAttrib = 'Données cartographiques fournies par le CAUE24'; L.tileLayer(caueUrl, {minZoom: 8, maxZoom: 11, attribution: caueAttrib, subDomains: 'abcdefgh'}).addTo(map); }
Les lignes précédentes suffisent à afficher notre fond de carte, le reste du code sera explicité ensuite. Le fond de carte est servi par notre serveur "livembtiles.makina-corpus.net", qui dispose des 8 sous-domaines a.livembtiles, b.livembtiles, etc. pour permettre le requêtage simultané de plus de tuiles, et ainsi améliorer les performances.
Changer de fond de carte selon le zoom
Une des demandes du client était de changer la couche de fond de la carte à certains niveaux de zoom, pour utiliser le fond orthophoto disponible. En utilisant le plugin Leaflet MultiTileLayer, cela se fait très facilement : il suffit de remplacer la ligne "L.TileLayer" citée précédemment par :
L.TileLayer.multi({ 14: { url: caueUrl, subdomains: 'abcdefgh' }, 15: { url: 'http://{s}.livembtiles.makina-corpus.net/caue24/orthophoto24/{z}/{x}/{y}.png', subdomains: 'abcdefgh' } }, { minZoom: 10, maxZoom: 15, attribution: caueAttrib }).addTo(map);
Cette syntaxe signifie : jusqu'au niveau de zoom 14, sert le fond CAUE classique, à partir du 15, utilise l'orthophoto. Nous pensions initialement directement utiliser le WMS du fond orthophoto, mais le plugin MultiTileLayer ne fonctionne pas avec une couche WMS, nous avons donc simplement converti cette couche en MBTiles pour la servir avec notre propre serveur de tuiles.
Chargement des couches de données
Chargement dynamique des GeoJSONs
Nous n'avons aucune connaissance à l'avance du nombre de couches de données dynamiques que le client souhaite ajouter sur l'écran. Nous avons donc du fixer une nomenclature pour les fichiers : "data/geojson/[territoire]_[categorie]_[nombre].geosjson", "nombre" étant un simple compteur entier. Voici le code qui charge dynamiquement les fichiers :
CaueViews.addGeoJSONs = function(community, category) { // Initialisation du sélecteur de couches. var layers = L.control.layers(null, null, {collapsed: false}); function loadUrl(baseUrl, n) { // Charge dynamiquement les GeoJSONs $.ajax({ url: baseUrl + "_" + n + ".geojson", dataType: 'json', }).done(function(data) { if (n == 1) { // Au moins 1 geojson => On ajoute le sélecteur de couches à la carte. layers.addTo(map); } // On ajoute le GeoJSON (cette fonction sera explicitée au prochain paragraphe). // Le nom addGeoJSONLegend ne se contente pas d'ajouter la légende, mais ajoute // Egalement la couche de GEOJSON complète (cela mériterait une réécriture). CaueViews.addGeoJSONLegend(layers, community, category, data, n); // On essaie de charger le suivant. loadUrl(baseUrl, n + 1); }).fail(function(jqXHR, textStatus, errorThrown) { // Rien à faire en cas d'erreur, à part stocker la boucle. }); } // Chargement des GeoJSON var url = "data/geojson/" + community + "_" + category; loadUrl(url, 1); };
En fait, une simple boucle récursive qui charge les GeoJSON, si ils existent, par une requête ajax.
Contrôle du rendu par le client
En plus de choisir le nombre de couches dynamiques chargées, le client souhaitait partiellement contrôler le rendu des couches. Pour cela, nous avons ajouté aux données géographiques quelques propriétés dans le GeoJSON, sur lequel le client peut agir pour choisir : la couleur de la couche, le nom et la description associée pour le composant de légende de la carte, si la couche est active par défaut au chargement, et si elle permet de déclencher des évènements (popups) lors du clic. C'est finalement assez simple :
CaueViews.addGeoJSONLegend = function(layers, community, category, data, n) { // Nom de la couche (pour la légende). var name = data.name || 'Sans nom'; // Description de la couche (pour la légende). var description = data.description || ''; // Couleur de la couche // On récupère éventuellement la couleur du style courant en cas d'absence. var color = data.color || CaueViews.getColorFromFeature(category, n); // Statut initial de la couche. var active = data.active || "false"; // La couche est-elle interactive ? var interactive = data.interactive || "true"; // Texte d'information de la couche (utilisation future) var displayText = data.displayText; // On initialise le style de la couche. var style = { "color": color, "weight": 1, "fillOpacity": 0.4, "opacity": 0.8, }; // On prépare l'interactivité. onEachFeature = function (feature, layer) { if (interactive != "false") { layer.on('click', function(e) { // Cette fonction sera décrite au paragraphe suivant. CaueViews.clickLayer(e.target, community, category); }); } } // Nous créons la couche. // Le pointToLayer, non décrit ici, remplace les marqueurs standards Leaflet par des marqueurs SVG, stylables. var geojsonLayer = L.geoJson(data, {pointToLayer: CaueViews.pointToLayer, onEachFeature: onEachFeature, style: style}); // Nous l'ajoutons à la carte si elle est active par défaut. if (active == "true") { geojsonLayer.addTo(map); } // Dans tous les cas, nous l'ajoutons au sélecteur. layers.addOverlay(geojsonLayer, name); // Et enfin, au composant légende (avec balisage "Bootstrap" pour le tooltip)). // L'initialisation de cette légende n'est pas commentée dans cet article, // reportez-vous au code correspondant sur le dépôt. $('.info.legend').append('[HTML Markup]'); // Et actionnons les tooltips sur la légende. $('.info.legend div').tooltip({container:'body'}); }
Il s'avère que notre système "manuel" est similaire à celui de MapBox qui pourrait devenir une norme pour ce type de besoin : https://github.com/mapbox/simplestyle-spec. A garder en tête pour le prochain projet.
Conclusion
Grâce à l'utilisation de Leaflet disposant d'une API très simple, il est possible de réaliser des applications de web mapping à forte valeur ajoutée fonctionnelle en réduisant le code JavaScript nécessaire à sa plus simple expression (vous constatez dans cet article que chaque fonctionnalité nécessite moins d'une dizaine de lignes de code).
Si cette technologie vous intéresse, venez suivre notre formation Leaflet.
Formations associées
Formations Front end
Formation Angular
À distance (FOAD) Du 25 au 29 novembre 2024
Voir la formationFormations SIG / Cartographie
Formation QGIS
À distance (FOAD) Du 2 au 6 décembre 2024
Voir la formationActualités en lien
Infrastructure web mapping minimaliste (partie 4) - Javascript
Dans ce dernier article sur l'application web mapping du CAUE24, nous traitons de quelques autres points Javascript ne concernant pas directement Leaflet.
Infrastructure web mapping minimaliste (partie 2) - TileMill
Dans le cadre de notre architecture web mapping minimaliste, nous présentons quelques astuces pour styler une carte avec TileMill.
Infrastructure web mapping minimaliste (partie 1)
Cette série d'articles présente par l'intermédiaire d'une réalisation une architecture de web mapping minimaliste, basée sur Leaflet et Jekyll.