Développement Web

Tutoriel : Créer des cartes interactives avec Leaflet.js

12 min de lecture

Prérequis

  • Niveau : bases JavaScript et HTML.
  • Outils : VS Code + Live Server, navigateur moderne. Aucune clé API requise.
  • Temps estimé : 1 h.

Pourquoi Leaflet plutôt que Google Maps ?

Leaflet est gratuit, sans clé, sans facturation. Pour 90 % des usages (afficher une adresse, montrer des points de vente, dessiner une zone), il suffit largement. Google Maps n’apporte un vrai plus que pour la navigation routière live, le Street View ou les autocomplétions d’adresses.

Leaflet.js : la bibliothèque de cartes la plus légère et simple

Leaflet.js est une bibliothèque JavaScript open source de 42 Ko pour créer des cartes interactives. Contrairement à Google Maps (payant au-delà de 28 500 chargements/mois), Leaflet utilise les tuiles gratuites d’OpenStreetMap. Parfait pour afficher la localisation de votre entreprise, vos zones de livraison, ou des données géographiques.

Installation et première carte

<!-- Ajouter dans le <head> -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>

<!-- Le conteneur de la carte -->
<div id="map" style="height: 400px; border-radius: 12px;"></div>
// Créer la carte centrée sur Dakar
const map = L.map('map').setView([14.6928, -17.4467], 13);

// Ajouter les tuiles OpenStreetMap (gratuit)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© OpenStreetMap contributors'
}).addTo(map);

// Ajouter un marqueur
L.marker([14.6928, -17.4467])
  .addTo(map)
  .bindPopup('<b>ITSkillsCenter</b><br>Dakar, Sénégal')
  .openPopup();

En 10 lignes, vous avez une carte interactive de Dakar avec un marqueur cliquable.

Personnaliser les marqueurs

// Icône personnalisée
const customIcon = L.icon({
  iconUrl: 'marker-icon.png',
  iconSize: [32, 42],
  iconAnchor: [16, 42],  // Point d'ancrage (bas du marqueur)
  popupAnchor: [0, -42]  // Position du popup par rapport au marqueur
});

L.marker([14.6928, -17.4467], { icon: customIcon }).addTo(map);

// Marqueur avec cercle coloré (sans image)
L.circleMarker([14.7167, -17.4677], {
  radius: 10,
  fillColor: '#e94560',
  color: '#fff',
  weight: 2,
  fillOpacity: 0.8
}).addTo(map).bindPopup('Zone de livraison');

Afficher plusieurs points (magasins, agences, etc.)

const lieux = [
  { nom: 'ITSkillsCenter Dakar', lat: 14.6928, lng: -17.4467, type: 'bureau' },
  { nom: 'Agence Thiès', lat: 14.7886, lng: -16.9260, type: 'agence' },
  { nom: 'Point Relais Mbour', lat: 14.4167, lng: -16.9667, type: 'relais' },
  { nom: 'Bureau Saint-Louis', lat: 16.0326, lng: -16.4897, type: 'bureau' }
];

lieux.forEach(lieu => {
  const colors = { bureau: '#4a90d9', agence: '#27ae60', relais: '#f39c12' };
  
  L.circleMarker([lieu.lat, lieu.lng], {
    radius: 8,
    fillColor: colors[lieu.type],
    color: '#fff',
    weight: 2,
    fillOpacity: 0.9
  })
  .addTo(map)
  .bindPopup('<strong>' + lieu.nom + '</strong><br>Type : ' + lieu.type);
});

// Zoomer pour voir tous les marqueurs
const bounds = L.latLngBounds(lieux.map(l => [l.lat, l.lng]));
map.fitBounds(bounds, { padding: [30, 30] });

Dessiner des zones (polygones)

// Zone de livraison autour de Dakar
const zoneLivraison = L.polygon([
  [14.78, -17.52],
  [14.78, -17.38],
  [14.62, -17.38],
  [14.62, -17.52]
], {
  color: '#4a90d9',
  fillColor: '#4a90d9',
  fillOpacity: 0.15,
  weight: 2
}).addTo(map);

zoneLivraison.bindPopup('Zone de livraison gratuite');

Géolocalisation : « Où suis-je ? »

document.getElementById('locateBtn').addEventListener('click', () => {
  map.locate({ setView: true, maxZoom: 16 });
});

map.on('locationfound', (e) => {
  L.marker(e.latlng)
    .addTo(map)
    .bindPopup('Vous êtes ici !')
    .openPopup();
  
  L.circle(e.latlng, { radius: e.accuracy / 2 }).addTo(map);
});

map.on('locationerror', () => {
  alert('Impossible de vous localiser. Vérifiez les permissions de géolocalisation.');
});

Événements : réagir aux clics sur la carte

// Afficher les coordonnées au clic
map.on('click', (e) => {
  const { lat, lng } = e.latlng;
  L.popup()
    .setLatLng(e.latlng)
    .setContent('Coordonnées : ' + lat.toFixed(4) + ', ' + lng.toFixed(4))
    .openOn(map);
});

Erreurs fréquentes

Carte qui s’affiche en gris

Cause : conteneur #map sans hauteur définie en CSS, ou map.invalidateSize() oublié après un changement de taille.
Solution : donnez explicitement height: 400px. Si la carte est dans un onglet caché qui devient visible, appelez map.invalidateSize().

Tuiles OSM qui ne se chargent pas

Cause : URL des tuiles incorrecte, ou usage abusif (rate limit) sur les serveurs OSM publics.
Solution : vérifiez l’URL https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png. En production lourde, hébergez vos tuiles ou utilisez un fournisseur (MapTiler, Stadia Maps, CartoDB).

Marqueurs personnalisés non centrés

Cause : mauvaises valeurs de iconAnchor et popupAnchor.
Solution : iconAnchor = pixel à coller sur la coordonnée GPS (souvent [width/2, height] pour une épingle dont la pointe est en bas).

Attribution OSM manquante

Cause : on retire l’attribution pour « faire propre ».
Solution : l’attribution est obligatoire par la licence ODbL. Forme correcte : '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'.

Exercice

  1. Créez une carte du Sénégal avec les 14 capitales régionales comme marqueurs
  2. Ajoutez un popup avec le nom et la population de chaque ville
  3. Dessinez une zone polygone autour de votre quartier
  4. Ajoutez un bouton de géolocalisation
  5. Bonus : chargez les marqueurs depuis un fichier JSON avec fetch()

Pour explorer plus loin

Pourquoi Leaflet plutot que Google Maps

Pour une PME a Dakar, Abidjan ou Yaounde qui veut afficher un magasin, un parcours touristique ou un reseau d’agences, Leaflet.js est une alternative gratuite et puissante a Google Maps. Pas de cle API obligatoire, pas de facture surprise au-dela de 28 000 chargements mensuels, et un poids minimal (39 Ko gzippes en version 1.9). Leaflet utilise par defaut les tuiles d’OpenStreetMap, libres de droits avec attribution.

Ce tutoriel pas-a-pas montre comment construire une carte interactive complete avec marqueurs, popups, clusters, GeoJSON et controles de zoom personnalises. Comptez 1 a 2 heures pour la premiere implementation. La version utilisee est Leaflet 1.9.4, compatible avec tous les navigateurs modernes y compris mobiles.

Etape 1 : Inclure Leaflet dans une page HTML

Creez un fichier index.html et incluez la feuille de style et le script de Leaflet via leur CDN officiel. Placez la feuille de style dans le head et le script juste avant la fermeture de body.

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
  integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
  integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>

Les attributs integrity et crossorigin garantissent que le fichier livre n’a pas ete altere. C’est la pratique recommandee par la documentation officielle de Leaflet pour la securite. Le signal de reussite : aucune erreur 403 ou 404 dans la console Network du navigateur.

Etape 2 : Creer le conteneur de carte et l’initialiser

Ajoutez un <div id= »map »></div> dans le body, avec une hauteur explicite en CSS (sans hauteur, la carte ne s’affiche pas).

<style>#map { height: 500px; width: 100%; }</style>
<div id="map"></div>
<script>
  const map = L.map('map').setView([14.6928, -17.4467], 12);
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: '© OpenStreetMap'
  }).addTo(map);
</script>

Les coordonnees [14.6928, -17.4467] correspondent au centre de Dakar. Le 12 est le niveau de zoom (0 = monde entier, 19 = niveau rue). Apres rechargement, la carte de Dakar apparait avec les controles de zoom en haut a gauche.

Etape 3 : Ajouter des marqueurs avec popups

Pour signaler vos points d’interet, ajoutez un marqueur par lieu et liez-y un popup HTML qui s’ouvre au clic.

const agence = L.marker([14.7167, -17.4677]).addTo(map);
agence.bindPopup('<b>Agence Plateau</b><br>Ouvert 8h-18h');

const siege = L.marker([14.6892, -17.4441]).addTo(map);
siege.bindPopup('<b>Siege Almadies</b><br>Tel : +221 33 800 00 00');

Le popup s’ouvre au clic. Pour qu’il s’ouvre par defaut, ajoutez .openPopup() en chaine. Les coordonnees sont au format [latitude, longitude] (et non l’inverse comme dans GeoJSON).

Etape 4 : Personnaliser les icones de marqueurs

L’icone par defaut est bleue. Pour un design coherent avec votre marque, definissez une icone personnalisee en pointant vers une image PNG ou SVG hebergee sur votre site.

const monIcone = L.icon({
  iconUrl: '/img/pin-orange.png',
  iconSize: [32, 40],
  iconAnchor: [16, 40],
  popupAnchor: [0, -35]
});
L.marker([14.7, -17.45], { icon: monIcone }).addTo(map);

iconAnchor indique le point de l’image qui colle precisement a la coordonnee (la pointe basse de l’epingle). popupAnchor decale le popup par rapport a ce point pour eviter le chevauchement.

Etape 5 : Grouper les marqueurs avec MarkerCluster

Si vous avez 50, 100 ou 1000 marqueurs, l’affichage devient illisible. Le plugin Leaflet.markercluster regroupe automatiquement les marqueurs proches en clusters numerotes qui se decomposent au zoom.

<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.Default.css">
<script src="https://unpkg.com/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
<script>
  const cluster = L.markerClusterGroup();
  donnees.forEach(p => {
    cluster.addLayer(L.marker([p.lat, p.lng]).bindPopup(p.nom));
  });
  map.addLayer(cluster);
</script>

Le tableau « donnees » contient vos points avec lat, lng et nom. Le plugin gere le zoom automatique et les animations de regroupement. Sortie attendue : un cercle bleu portant le nombre de marqueurs regroupes.

Etape 6 : Charger des donnees au format GeoJSON

GeoJSON est le format standard pour echanger des donnees geographiques. Hebergez un fichier .geojson sur votre serveur et chargez-le avec fetch.

fetch('/data/quartiers.geojson')
  .then(r => r.json())
  .then(data => {
    L.geoJSON(data, {
      style: { color: '#ff7800', weight: 2, fillOpacity: 0.3 },
      onEachFeature: (feature, layer) => {
        layer.bindPopup(feature.properties.nom);
      }
    }).addTo(map);
  });

Cette approche convient pour afficher des polygones (quartiers, regions) ou des lignes (itineraires). Le style applique une couleur orange semi-transparente et le popup affiche la propriete « nom » de chaque feature.

Etape 7 : Ajouter des controles et la geolocalisation

Ajoutez une echelle metrique en bas a gauche et un bouton qui centre sur la position de l’utilisateur (avec son consentement).

L.control.scale({ imperial: false }).addTo(map);

document.getElementById('btn-geo').addEventListener('click', () => {
  map.locate({ setView: true, maxZoom: 14 });
});

map.on('locationfound', (e) => {
  L.circle(e.latlng, { radius: 200 }).addTo(map);
});

map.locate utilise l’API Geolocation du navigateur. Le navigateur demande l’autorisation a l’utilisateur. En cas de refus, l’evenement locationerror est emis et vous pouvez afficher un message de fallback.

Optimiser les performances sur mobile

Sur les connexions 3G frequentes en Afrique de l’Ouest, le chargement des tuiles peut etre lent. Pour optimiser : limitez le maxZoom a 17 (au-dela, peu d’interet et plus lourd), pre-chargez uniquement la zone d’interet, et activez l’option preferCanvas: true sur la carte pour les rendus avec beaucoup d’elements vectoriels (gain de 20 a 40 % en fps).

Si votre site cible specifiquement le Senegal, vous pouvez limiter la zone navigable avec map.setMaxBounds([[12.3, -17.6], [16.7, -11.3]]) pour empecher l’utilisateur de se perdre hors du pays.

Hosting des tuiles : alternatives a OpenStreetMap public

Le serveur public d’OpenStreetMap impose des limites d’usage et n’est pas garanti pour la production. Pour un site a fort trafic, utilisez un service tiers comme Stadia Maps, Maptiler ou Mapbox (avec attribution conforme). Stadia Maps offre 200 000 chargements gratuits par mois, suffisant pour la plupart des PME. Voir aussi notre guide sur OpenStreetMap pour cartographie locale et JavaScript cartes web.

Avec ces 7 etapes, votre carte interactive est prete pour la production : marqueurs personnalises, clusters, GeoJSON, geolocalisation et performances mobile. Le code total tient en moins de 100 lignes.

Cas pratique : carte des agences d’une banque a Abidjan

Imaginons un cas reel : une banque en Cote d’Ivoire veut afficher ses 45 agences sur son site, avec horaires et numero de telephone. Le fichier de donnees est un JSON simple avec lat, lng, nom, adresse, horaires et tel pour chaque agence. Le code complet integre les 7 etapes precedentes : initialisation centree sur Abidjan ([5.347, -4.024], zoom 11), MarkerCluster active (45 marqueurs sur Abidjan deviennent illisibles sans clustering), icones personnalisees avec le logo de la banque, popups au format HTML avec toutes les infos, et un bouton « trouver l’agence la plus proche » qui combine geolocalisation et calcul de distance.

Le calcul de distance utilise la formule de Haversine appliquee aux coordonnees lat/lng : Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lng2 – lng1)) * 6371 (rayon terrestre en km). Le marqueur le plus proche est ouvert automatiquement avec openPopup().

Integration dans WordPress sans plugin

Pour integrer Leaflet dans une page WordPress sans plugin dedie, ajoutez un shortcode dans functions.php du theme enfant. Le shortcode injecte le div, le CSS et le JS necessaires uniquement sur les pages qui l’utilisent (evite de charger Leaflet partout). Vous appelez ensuite [carte_leaflet] dans l’editeur Gutenberg en mode HTML.

Pensez a enqueue les assets via wp_enqueue_script avec dependance jquery (par precaution) et a passer les donnees PHP vers JS via wp_localize_script. Cela permet de generer dynamiquement la liste des marqueurs depuis une custom post type « Agences » sans dupliquer les donnees.

Accessibilite et SEO de la carte

Une carte JS n’est pas indexable par Google. Pour le SEO, dupliquez les informations cles (adresses, horaires) en HTML standard sous la carte, dans un <ul> ou un <table>. Les robots indexent ces donnees, et les utilisateurs avec lecteurs d’ecran y accedent. Ajoutez aussi un attribut role= »application » et un aria-label clair sur le div #map pour informer les technologies d’assistance qu’il s’agit d’une carte interactive.

Avec ces ajouts, votre carte Leaflet est prete pour la production, accessible et compatible SEO. Pour des besoins plus avances (couches multiples, dessin par l’utilisateur, mesures), explorez les plugins Leaflet.draw et Leaflet.Heat.

Aller plus loin avec les plugins de la communaute Leaflet

L’ecosysteme Leaflet compte plus de 200 plugins maintenus. Les plus utiles : Leaflet.draw (dessin de polygones par l’utilisateur), Leaflet.Heat (cartes de chaleur), Leaflet.Routing.Machine (calcul d’itineraires via OSRM ou GraphHopper), Leaflet.fullscreen (bouton plein ecran), et Leaflet.Awesome-Markers (icones avec FontAwesome integre). Tous se chargent comme des scripts additionnels apres Leaflet.

Pour un site multilingue, le plugin Leaflet.PolyLineMeasure ajoute un outil de mesure de distance que les utilisateurs peuvent activer eux-memes. Ces ajouts coutent quelques kilo-octets supplementaires et enrichissent considerablement l’experience utilisateur.

Partager