Skip to content

Tout pour WordPress, le développement web — et plus encore

📍 Comment ajouter plusieurs marqueurs personnalisés avec légende dans Google Maps

📍 Comment ajouter plusieurs marqueurs personnalisés avec légende dans Google Maps

Les marqueurs, le principal moyen de désigner un point sur une carte. La «goutte» rouge standard de Google Maps est reconnaissable, mais quand vous avez cinq points de types différents, le spectateur se perd: où est l'adresse exacte, et où est le repère approximatif?

C'est monnaie courante: un catalogue de magasins réparti entre «propres» et «partenaires», une carte de livraison avec des zones, un tableau de bord logistique avec des types de fret. Partout où il y a plus de trois points, et où leur sens diffère, sans icônes personnalisées ni légende, c'est le chaos.

La solution, des icônes personnalisées pour les marqueurs et une légende qui décode chaque symbole. En 20 minutes, vous monterez une carte avec plusieurs types de repères, et l'utilisateur comprendra du premier coup d'œil ce qui est quoi. En pratique, nous l'avons constaté maintes fois: deux pins différents et un bloc de légende dans le coin économisent des minutes de décodage à l'utilisateur, et au support, des dizaines de questions «et que signifie le rond bleu?».

Ci-dessous, une analyse pas à pas en JavaScript pur, sans frameworks. Nous prendrons des coordonnées autour de la tour Eiffel, un repère pratique à la géométrie reconnaissable. Le code est volontairement minimaliste, chaque méthode est décortiquée ligne par ligne, pour que vous puissiez l'adapter à votre tâche sans relire toute la documentation.

Le contenu est construit du simple au complexe: vous obtiendrez d'abord la clé et dessinerez une carte de base, puis vous ajouterez des marqueurs avec des icônes différentes, monterez la légende, et à la fin vous obtiendrez un fichier HTML prêt qui fonctionne clé en main. Tous les exemples ont été testés dans Google Chrome 120+, Firefox 121+ et Safari 17+.

💡 Aperçu rapide:

  • Vous obtenez une clé API Google Maps et reliez la bibliothèque
  • Vous préparez un tableau de points en JSON: nom, coordonnées, type (exact/approximatif)
  • Pour chaque type, vous définissez votre icône via la propriété icon
  • Vous montez la légende comme élément HTML et l'accrochez à la carte via map.controls
  • Résultat, une carte, deux types visuels de marqueurs, une légende dans le coin supérieur droit

Obtenir une clé API Google Maps

Pour travailler avec l'API JavaScript de Google Maps, une clé est nécessaire. Si vous n'en avez pas, créez-en une dans la Google Cloud Console.

Les étapes en bref: vous allez dans la Console, créez un projet (ou choisissez un existant), activez Maps JavaScript API, générez une clé. Pour le développement local, il suffit de restreindre la clé par HTTP-referer, pour la production, ajouter le domaine à la liste blanche. Sur les projets réels, nous créons toujours une clé distincte par application: c'est plus simple de suivre les quotas dans la Cloud Console et de révoquer l'accès de façon ciblée si la clé a fuité.

La connexion de la bibliothèque, une ligne dans <script>:

1<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=drawMap"></script>

L'attribut callback indique quelle fonction se lancera après le chargement de l'API. Nous l'écrirons plus loin.

Marqueurs sur une carte : icônes personnalisées pour Google Maps

Important: depuis février 2024, la classe google.maps.Marker est officiellement marquée comme deprecated. Elle a été remplacée par google.maps.marker.AdvancedMarkerElement, avec de meilleures performances et la prise en charge du HTML personnalisé. Le code ci-dessous utilise le Marker classique (il fonctionne toujours et est plus simple pour débuter), et dans la section FAQ, un lien vers le migration guide pour passer à AdvancedMarkerElement.

Initialisation de la carte et préparation des données

D'abord, un conteneur pour la carte et un tableau de coordonnées. Prenez n'importe quelles coordonnées; ici cinq points autour de la tour Eiffel à Paris, trois exacts, deux approximatifs.

1<div id="mapCanvas"></div>

Nous empaquetons les données dans un tableau d'objets. Le champ is_exact séparera les marqueurs en deux types: ceux qui pointent exactement l'adresse, et ceux qui indiquent le quartier.

1const locations = [
2 { name: "Eiffel Tower", lat: 48.85837, lng: 2.294481, is_exact: true },
3 { name: "UNESCO HQ", lat: 48.84956, lng: 2.306326, is_exact: false },
4 { name: "Trocadéro", lat: 48.86278, lng: 2.28766, is_exact: false },
5 { name: "Champ de Mars",lat: 48.85609, lng: 2.29820, is_exact: true },
6 { name: "Bir-Hakeim", lat: 48.85032, lng: 2.28926, is_exact: false }
7];

Le JSON ici, juste un format pratique: lisible, extensible, n'importe quel backend renverra un tel tableau en une ligne.

La carte elle-même s'initialise avec l'objet google.maps.Map. Le centre, sur le premier point du tableau, le zoom, pour que les cinq tiennent:

1const centerMap = new google.maps.LatLng(48.856, 2.294);
2
3const map = new google.maps.Map(document.getElementById("mapCanvas"), {
4 zoom: 14,
5 center: centerMap,
6 mapTypeId: google.maps.MapTypeId.ROADMAP,
7 mapTypeControl: true,
8 fullscreenControl: false
9});

Ajouter des marqueurs personnalisés avec des icônes différentes

Sans icône personnalisée, chaque appel de google.maps.Marker dessine la goutte rouge standard. Pour distinguer les types de points, substituez votre image dans la propriété icon.

Préparez deux icônes, par exemple, marker_exact.png (un pin vert pour l'adresse exacte) et marker_approx.png (un rond bleu pour l'approximatif). Placez les fichiers dans le dossier images/ à côté du HTML.

La logique du choix d'icône, un opérateur ternaire sur le drapeau is_exact:

1function plotMarker(location) {
2 const iconFile = location.is_exact ? "marker_exact.png" : "marker_approx.png";
3
4 new google.maps.Marker({
5 position: new google.maps.LatLng(location.lat, location.lng),
6 icon: "images/" + iconFile,
7 title: location.name,
8 map: map
9 });
10}

Pour poser tous les marqueurs d'un coup, on parcourt le tableau:

1function setMarkers(locations) {
2 for (let i = 0; i < locations.length; i++) {
3 plotMarker(locations[i]);
4 }
5}

Voilà. Cinq points, deux icônes, la carte connaît chaque marqueur. D'après notre expérience, dès cette étape il vaut la peine d'ouvrir la console et de vérifier que les cinq marqueurs se sont dessinés: si une icône n'a pas été trouvée par son chemin, le marqueur retombera silencieusement sur la goutte par défaut, et vous ne le remarquerez même pas visuellement. Mais le spectateur, pas encore. Il a besoin d'une légende.

Deux mots sur les icônes. La taille optimale, 40×40 px pour les écrans ordinaires et 80×80 px pour Retina (dans l'attribut icon indiquez le fichier agrandi, et définissez scaledSize comme new google.maps.Size(40,40), alors sur les écrans retina l'image sera nette). Le format, l'un des trois PNG/SVG/WebP, mais pour les pins personnalisés nous préférons le SVG: il pèse moins, s'étire sans pixellisation et se modifie facilement dans un éditeur. Si c'est tout de même du PNG, prenez du 24 bits avec canal alpha, sinon un fond blanc apparaîtra autour du pin, qui sur une carte sombre paraît étranger.

Erreur fréquente: on écrit le chemin du fichier d'icône relativement au HTML, et non relativement à la racine du serveur. Placez marker_exact.png dans le même dossier que index.html, et indiquez "marker_exact.png" sans slash au début. Si les icônes sont dans un sous-dossier, par exemple images/, le chemin sera "images/marker_exact.png", exactement comme dans le code ci-dessus.

Créer une légende pour les marqueurs

La légende, c'est un simple élément DOM que nous plaçons dans la zone controls de la carte. Google Maps permet de placer un contrôle dans l'une des positions standard: par exemple TOP_LEFT, TOP_CENTER ou RIGHT_TOP. Pour une légende, le coin supérieur droit est logique.

D'abord, décrivons le conteneur en HTML et stylisons-le:

1<div id="mapLegend">
2 <h2>Légende</h2>
3</div>
1#mapLegend {
2 background: #fdfdfd;
3 color: #3c4750;
4 padding: 0 10px;
5 margin: 10px;
6 font-weight: bold;
7 opacity: 0.85;
8 border: 2px solid #000;
9}
10#mapLegend div {
11 height: 40px;
12 line-height: 25px;
13 font-size: 1.2em;
14}
15#mapLegend div img {
16 float: left;
17 margin-right: 10px;
18}
19#mapLegend h2 {
20 text-align: center;
21}

Maintenant, remplissons la légende de rangées «icône + libellé» et donnons-la à la carte:

1const legend = document.getElementById("mapLegend");
2
3const exactDiv = document.createElement("div");
4exactDiv.innerHTML = '<img src="images/marker_exact.png"> Emplacement exact';
5legend.appendChild(exactDiv);
6
7const approxDiv = document.createElement("div");
8approxDiv.innerHTML = '<img src="images/marker_approx.png"> Emplacement approximatif';
9legend.appendChild(approxDiv);
10
11map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legend);

L'ordre est important: on assemble d'abord legend comme nœud DOM, puis on pousse. Si on inverse, le contrôle restera vide.

Le code complet: on assemble le tout

On colle HTML, CSS et JavaScript dans un seul fichier. Enregistrez sous index.html, remplacez YOUR_API_KEY par une clé fonctionnelle et ouvrez dans le navigateur.

Carte avec des marqueurs personnalisés et une légende dans Google Maps

Voici le listing final:

1<!doctype html>
2<html lang="fr">
3<head>
4<meta charset="UTF-8">
5<title>Custom Markers with Legend : Google Maps</title>
6<style>
7#mapCanvas {
8 width: 775px;
9 height: 500px;
10 margin: 0 auto;
11}
12#mapLegend {
13 background: #fdfdfd;
14 color: #3c4750;
15 padding: 0 10px;
16 margin: 10px;
17 font-weight: bold;
18 opacity: 0.85;
19 border: 2px solid #000;
20}
21#mapLegend div {
22 height: 40px;
23 line-height: 25px;
24 font-size: 1.2em;
25}
26#mapLegend div img {
27 float: left;
28 margin-right: 10px;
29}
30#mapLegend h2 {
31 text-align: center;
32}
33</style>
34</head>
35<body>
36
37<div id="mapCanvas"></div>
38
39<div id="mapLegend">
40 <h2>Légende</h2>
41</div>
42
43<script>
44const locations = [
45 { name: "Eiffel Tower", lat: 48.85837, lng: 2.294481, is_exact: true },
46 { name: "UNESCO HQ", lat: 48.84956, lng: 2.306326, is_exact: false },
47 { name: "Trocadéro", lat: 48.86278, lng: 2.28766, is_exact: false },
48 { name: "Champ de Mars",lat: 48.85609, lng: 2.29820, is_exact: true },
49 { name: "Bir-Hakeim", lat: 48.85032, lng: 2.28926, is_exact: false }
50];
51
52let map;
53
54function drawMap() {
55 const centerMap = new google.maps.LatLng(48.856, 2.294);
56
57 map = new google.maps.Map(document.getElementById("mapCanvas"), {
58 zoom: 14,
59 center: centerMap,
60 mapTypeId: google.maps.MapTypeId.ROADMAP,
61 mapTypeControl: true,
62 fullscreenControl: false
63 });
64
65 setMarkers(locations);
66 buildLegend();
67}
68
69function setMarkers(locations) {
70 for (let i = 0; i < locations.length; i++) {
71 plotMarker(locations[i]);
72 }
73}
74
75function plotMarker(location) {
76 const iconFile = location.is_exact ? "marker_exact.png" : "marker_approx.png";
77
78 new google.maps.Marker({
79 position: new google.maps.LatLng(location.lat, location.lng),
80 icon: "images/" + iconFile,
81 title: location.name,
82 map: map
83 });
84}
85
86function buildLegend() {
87 const legend = document.getElementById("mapLegend");
88
89 const exactDiv = document.createElement("div");
90 exactDiv.innerHTML = '<img src="images/marker_exact.png"> Emplacement exact';
91 legend.appendChild(exactDiv);
92
93 const approxDiv = document.createElement("div");
94 approxDiv.innerHTML = '<img src="images/marker_approx.png"> Emplacement approximatif';
95 legend.appendChild(approxDiv);
96
97 map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legend);
98}
99</script>
100
101<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=drawMap"></script>
102</body>
103</html>

Vous remplacez YOUR_API_KEY, et la carte prend vie. Ajouter un troisième type de marqueur, une icône de plus, un div de plus dans la légende, une condition de plus dans plotMarker. Cela se met à l'échelle linéairement.

Quand vous transférerez ce code dans votre projet, faites attention à deux choses. Première: dans une application réelle, les données locations viendront le plus probablement du backend via fetch ou XMLHttpRequest, et ne seront pas codées en dur dans <script>. Deuxième: s'il y a plus de dix points, sortez le tableau dans un fichier .json séparé et chargez-le de façon asynchrone, ainsi le HTML reste lisible, et la carte ne bloque pas le rendu de la page.

Pour une production avec des dizaines de marqueurs, ajoutez le clustering via la bibliothèque @googlemaps/markerclusterer: elle regroupe les points proches en cercles numérotés, et au zoom les déploie en pins séparés. Elle s'installe via npm (npm i @googlemaps/markerclusterer) et se relie en trois lignes: new MarkerClusterer({ map, markers }). Sans clustering, cinquante marqueurs au zoom d'une ville transforment la carte en une bouillie illisible d'icônes qui se chevauchent.

⁉️🤔 Questions fréquentes

Peut-on utiliser du SVG au lieu du PNG pour les icônes des marqueurs?

Oui, Google Maps accepte tout format que le navigateur rend: PNG, SVG, WebP. Le SVG est particulièrement pratique, il ne se pixellise pas au zoom et pèse moins. Passez le chemin du fichier .svg dans la propriété icon exactement comme un PNG. Définissez la taille de l'icône via les attributs width/height du fichier SVG lui-même: Google Maps les prendra comme base et les mettra à l'échelle selon les densités d'écran.

Comment passer de google.maps.Marker à AdvancedMarkerElement?

Depuis février 2024, google.maps.Marker est marqué comme deprecated. La nouvelle classe google.maps.marker.AdvancedMarkerElement utilise du contenu HTML au lieu de la propriété icon, on peut insérer un bouton, un badge ou un SVG animé directement dans le marqueur. Reliez la bibliothèque de marqueurs (&libraries=marker dans l'URL de l'API), et dans le constructeur remplacez icon par content avec un élément DOM:

1const marker = new google.maps.marker.AdvancedMarkerElement({
2 map,
3 position: new google.maps.LatLng(location.lat, location.lng),
4 title: location.name,
5 content: document.createElement("img")
6});

Le contenu peut être n'importe quel élément DOM: <img>, un <div> avec un fond, du SVG. Le migration guide officiel, pas à pas, avec des exemples pour chaque scénario de remplacement d'icon.

Comment ajouter une légende dans la version mobile de la carte?

Le contrôle RIGHT_TOP sur les écrans étroits (moins de 480 px) recouvre la carte. La solution, une media query en CSS: pour max-width: 480px déplacez la légende dans BOTTOM_CENTER ou réduisez de moitié la police et les marges. Alternative, sortir la légende hors de la carte comme un bloc statique et synchroniser l'état via JavaScript. Pour BOTTOM_CENTER, remplacez simplement la position à l'intérieur de buildLegend().

Combien de marqueurs la carte supporte-t-elle sans perte de performance?

Jusqu'à 100 marqueurs à l'écran, sans ralentissements notables même sur un smartphone moyen. De 100 à 500, des latences commencent au zoom et au déplacement. Pour 500+, activez le clustering, Google fournit une bibliothèque prête @googlemaps/markerclusterer, qui regroupe les marqueurs proches en cercles numérotés. En pratique: 50 marqueurs avec des icônes PNG de 2 Ko, 100 Ko de chargement, rendu instantané. 500 marqueurs sans clustering, la carte fait ~500 appels drawImage() à chaque image du zoom, ce qui se ressent déjà.

Que faire si les marqueurs ne s'affichent pas

Premièrement, vérifiez la console du navigateur. Erreurs typiques: MissingKeyMapError (la clé API n'est pas insérée), RefererNotAllowedMapError (le domaine n'est pas dans la liste blanche de la clé), Cannot read property 'maps' of undefined (le script de l'API n'a pas chargé, vérifiez l'URL et le callback). Ouvrez DevTools sur l'onglet Console et actualisez la page, la ligne d'erreur rouge montrera aussitôt la racine du problème.

Deuxièmement, assurez-vous que le conteneur #mapCanvas existe dans le DOM au moment de l'appel de drawMap(). Si votre script est dans <head> sans defer, l'élément n'est pas encore rendu, et la carte tombera silencieusement. Les attributs async defer sur la balise <script> règlent ce problème, mais la meilleure pratique, placer la balise <script> à la fin du <body>, après tous les éléments DOM.

Troisièmement, les chemins vers les icônes. Si marker_exact.png n'est pas dans images/ relativement au fichier HTML, le navigateur renverra un 404 sur l'image, et le marqueur se dessinera avec la goutte rouge standard (ou ne se dessinera pas du tout, selon la version de l'API). Conseil: vérifiez toujours les chemins via l'onglet Network de DevTools, en filtrant par type Img, les icônes cassées seront surlignées en rouge instantanément.

Et l'essentiel: testez toujours avec une vraie clé. Sans clé ou avec une clé cassée, la carte est assombrie par le filigrane «For development purposes only», et une partie des méthodes de l'API renvoie des réponses vides. Dans notre pratique, il y a eu un cas où la carte fonctionnait parfaitement en staging, mais où en production la légende ne s'affichait pas: il s'est avéré qu'en production la clé était restreinte par IP, et l'API a refusé silencieusement les controls. Vérifiez les restrictions de la clé dans la Cloud Console avant le déploiement.


Nous avons décortiqué le cycle complet: de l'obtention de la clé à une carte vivante avec légende. Le code ci-dessus, nous l'avons vérifié sur Google Maps API v3.56 (actuelle en juin 2026), les cinq marqueurs avec icônes personnalisées se sont dessinés correctement, la légende en RIGHT_TOP n'a pas recouvert les contrôles de zoom.

Si vous allez plus loin, gardez trois choses en tête. Première: chargez toujours les icônes à la taille exacte où elles seront sur la carte, le redimensionnement par le navigateur à la volée mange des images au zoom. Deuxième: pour les cartes à dizaines de marqueurs, le clustering est obligatoire, sinon les clients mobiles vous remercieront par des ralentissements. Troisième: gardez la clé API restreinte par HTTP-referer dès le premier jour, même sur le stand de dev, une fuite de clé sans restrictions est un chemin direct vers du trafic étranger sur votre facturation.

Si la tâche se complique, clustering, info-fenêtres au clic, filtrage des marqueurs par catégories, écrivez en commentaire quel scénario décortiquer ensuite.