Votre carte Leaflet affiche un fond et des données. Voici dix outils à lui ajouter pour en faire une vraie application : chacun est autonome, avec le code complet à copier dans votre app.js (on suppose que la variable map existe déjà). Les plugins sont chargés depuis un CDN, en version figée.
1. Ma position (géolocalisation)
Intégré à Leaflet, aucun plugin nécessaire. Le navigateur demande l'autorisation, et la page doit être servie en https (ou localhost).
let maPosition = null;
function localiser() { map.locate({ setView: true, maxZoom: 17, enableHighAccuracy: true }); }
map.on('locationfound', (e) => {
if (maPosition) map.removeLayer(maPosition);
maPosition = L.layerGroup([
L.circle(e.latlng, { radius: e.accuracy, weight: 1, fillOpacity: 0.1 }), // précision
L.circleMarker(e.latlng, { radius: 7, color: '#fff', weight: 3, fillColor: '#1e88e5', fillOpacity: 1 })
]).addTo(map);
});
map.on('locationerror', () => alert('Position indisponible ou refusée'));
2. Recherche d'adresse (API Adresse)
La Base Adresse Nationale est interrogeable gratuitement, sans clé, via l'API Adresse de l'État. Un champ de recherche dans un contrôle Leaflet :
const Recherche = L.Control.extend({
onAdd() {
const box = L.DomUtil.create('div', 'leaflet-bar');
box.innerHTML = '<input type="search" placeholder="Rechercher une adresse…" style="width:240px;padding:6px 8px;border:0">';
L.DomEvent.disableClickPropagation(box);
const input = box.querySelector('input');
input.addEventListener('keydown', async (e) => {
if (e.key !== 'Enter' || !input.value.trim()) return;
const url = 'https://api-adresse.data.gouv.fr/search/?limit=1&q=' + encodeURIComponent(input.value);
const res = await (await fetch(url)).json();
if (!res.features.length) return alert('Adresse introuvable');
const [lon, lat] = res.features[0].geometry.coordinates; // GeoJSON : [lon, lat]
map.setView([lat, lon], 18);
L.marker([lat, lon]).addTo(map).bindPopup(res.features[0].properties.label).openPopup();
});
return box;
}
});
new Recherche({ position: 'topleft' }).addTo(map);
Pour une liste de suggestions pendant la frappe, appelez la même URL avec autocomplete=1&limit=5 à chaque saisie (en attendant ~250 ms entre deux requêtes).
3. Coordonnées sous la souris
const Coords = L.Control.extend({
onAdd() { this._div = L.DomUtil.create('div', 'coords'); return this._div; }
});
const coords = new Coords({ position: 'bottomleft' }).addTo(map);
map.on('mousemove', (e) => {
coords._div.textContent = `${e.latlng.lat.toFixed(5)}, ${e.latlng.lng.toFixed(5)}`;
});
.coords { background: rgba(255,255,255,.85); padding: 2px 8px; border-radius: 6px; font: 12px monospace; }
4. Mesurer une distance
Leaflet sait calculer la distance entre deux points (map.distance, en mètres). On cumule les segments d'une ligne tracée au clic ; double-clic pour recommencer.
map.doubleClickZoom.disable();
let points = [];
const trace = L.polyline([], { color: '#8b6cff', dashArray: '6 6' }).addTo(map);
map.on('click', (e) => {
points.push(e.latlng);
trace.setLatLngs(points);
let total = 0;
for (let i = 1; i < points.length; i++) total += map.distance(points[i - 1], points[i]);
trace.bindTooltip(total < 1000 ? `${total.toFixed(1)} m` : `${(total / 1000).toFixed(3)} km`, { permanent: true }).openTooltip(e.latlng);
});
map.on('dblclick', () => { points = []; trace.setLatLngs([]); trace.unbindTooltip(); });
Pour les surfaces, ajoutez Turf.js et utilisez turf.area(polygone) (résultat en m²). L'outil Mesure distance & surface de CartoForge fait les deux.
5. Dessiner et exporter (Leaflet-Geoman)
Barre d'outils complète pour dessiner, modifier, déplacer et supprimer des points, lignes et polygones.
<link rel="stylesheet" href="https://unpkg.com/@geoman-io/leaflet-geoman-free@2.17.0/dist/leaflet-geoman.css">
<script src="https://unpkg.com/@geoman-io/leaflet-geoman-free@2.17.0/dist/leaflet-geoman.js"></script>
map.pm.setLang('fr');
map.pm.addControls({ position: 'topleft', drawCircle: false, drawCircleMarker: false, drawText: false });
// Export de tout ce qui a été dessiné, en GeoJSON
function exporter() {
const geojson = map.pm.getGeomanLayers(true).toGeoJSON();
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([JSON.stringify(geojson)], { type: 'application/geo+json' }));
a.download = 'dessin.geojson';
a.click();
}
6. Regrouper les marqueurs (Leaflet.markercluster)
Au-delà de quelques centaines de marqueurs, la carte devient illisible et lente. Les clusters les regroupent par proximité et affichent leur nombre.
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css">
<script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script>
fetch('arbres.geojson').then((r) => r.json()).then((data) => {
const clusters = L.markerClusterGroup({ showCoverageOnHover: false, maxClusterRadius: 50 });
clusters.addLayer(L.geoJSON(data, {
onEachFeature: (f, layer) => layer.bindPopup(f.properties.espece || 'Arbre')
}));
map.addLayer(clusters);
});
7. Carte de chaleur (Leaflet.heat)
<script src="https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js"></script>
fetch('accidents.geojson').then((r) => r.json()).then((data) => {
// [latitude, longitude, intensité]
const pts = data.features.map((f) => [f.geometry.coordinates[1], f.geometry.coordinates[0], 1]);
L.heatLayer(pts, { radius: 22, blur: 18, maxZoom: 15 }).addTo(map);
});
8. Ouvrir un fichier par glisser-déposer
L'utilisateur dépose un GeoJSON sur la carte : il s'affiche et la carte se cadre dessus. Rien n'est envoyé sur un serveur.
const zone = map.getContainer();
zone.addEventListener('dragover', (e) => e.preventDefault());
zone.addEventListener('drop', async (e) => {
e.preventDefault();
for (const file of e.dataTransfer.files) {
const data = JSON.parse(await file.text());
const couche = L.geoJSON(data, {
onEachFeature: (f, layer) => layer.bindPopup(
Object.entries(f.properties || {}).map(([k, v]) => `<b>${k}</b> : ${v}`).join('<br>'))
}).addTo(map);
map.fitBounds(couche.getBounds());
}
});
Pour lire aussi Shapefile, GeoPackage, KML ou DXF, la visionneuse SIG montre la marche à suivre (bibliothèques JSZip, sql.js, dxf-parser).
9. Légende
const Legende = L.Control.extend({
onAdd() {
const div = L.DomUtil.create('div', 'legende');
const classes = [['Maison', '#e0553d'], ['Garage', '#3d8bfd'], ['Jardin', '#2fb673']];
div.innerHTML = '<strong>Nature</strong>' + classes.map(([nom, c]) =>
`<div><i style="background:${c}"></i> ${nom}</div>`).join('');
return div;
}
});
new Legende({ position: 'bottomright' }).addTo(map);
.legende { background: #fff; padding: 8px 12px; border-radius: 10px; font: 13px sans-serif; line-height: 1.6; }
.legende i { display: inline-block; width: 12px; height: 12px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
10. Plein écran
L'API Fullscreen des navigateurs suffit, inutile d'ajouter un plugin :
const PleinEcran = L.Control.extend({
onAdd() {
const a = L.DomUtil.create('a', 'leaflet-bar');
a.href = '#'; a.title = 'Plein écran'; a.textContent = '⛶';
a.style.cssText = 'display:grid;place-items:center;width:34px;height:34px;background:#fff;font-size:18px';
L.DomEvent.on(a, 'click', (e) => {
L.DomEvent.preventDefault(e);
document.fullscreenElement ? document.exitFullscreen() : map.getContainer().requestFullscreen();
});
return a;
}
});
new PleinEcran({ position: 'topleft' }).addTo(map);
document.addEventListener('fullscreenchange', () => map.invalidateSize()); // recalcule la taille
Et ensuite ?
- Ajoutez les photos aériennes de l'IGN et le cadastre : Photos aériennes IGN et cadastre dans Leaflet.
- Besoin de faire tourner la carte, de 3D ou de beaucoup d'objets ? Passez à MapLibre, avec ses 10 outils à intégrer.
Démonstration : les 10 outils réunis
Tous les outils de ce tutoriel sur une même carte (les 600 arbres sont fictifs) :
- ◎ ma position · recherche en haut à droite (tapez une adresse puis Entrée) · coordonnées en bas à gauche ;
- 📏 mesure : clics successifs, double-clic pour recommencer ;
- barre Geoman (à gauche) pour dessiner, puis 💾 pour télécharger le dessin en GeoJSON ;
- clusters d'arbres (cliquez pour zoomer) et carte de chaleur à cocher en bas à droite, avec la légende ;
- déposez un fichier .geojson sur la carte, et ⛶ pour le plein écran.