Cette carte affiche les 878 villes (et cedex) françaises commençant par la lettre 'H'. Le code présenté ici est déjà utilisé en production pour une application qui gère 200 000 données géolocalisables. Cette application fournie une recherche multi-critères sur les différents attributs des données et permet ensuite de visualiser le résultat de la recherche sur une telle carte. Les utilisateurs apprécient cette fonctionnalité, mais ils ne savent pas qu'elle est relativement aisée à implémenter. Vous, en revanche, vous allez vous en rendre compte très vite. Enfin, sachez que jusqu'à 10 000 markers, les différents navigateurs se comportent bien sur la plupart des machines. Ensuite, nous arrivons à afficher 90 000 markers sur une bonne machine et sous Chrome, mais cela prend un peu de temps. Place au code maintenant !
Ce tutoriel utilise deux librairies spécifiques.
Ici, la carte est divisée en carré de taille identique, la librairie compte le nombre de markers dans chaque carré et n'affiche finalement qu'un seul marker au centre du carré contenant le nombre de markers que ce cluster contient. Pour plus de lisibilité cette librairie a modifié l'icone qu'elle affiche (contraire à cette capture d'écran) et adopté un code couleur en fontion du nombre de markers contenus dans un cluster. Plus d'informations à cette adresse : Too Many Markers (en anglais).
La librairie peut être téléchargée (ou référencée) depuis cette URL : Marker Cluster (dans le dossier src)
En cliquant sur le marker, la librairie représente sous la forme d'une toile d'araignée (d'ou son nom) tous les markers qui sont situés au même endroit. Cela fonctionne même avec 200 markers, même si ce n'est pas très pratique. Plus d'informations à cette adresse : OverlappingMarkerSpiderfier (en anglais).
La librairie peut être téléchargée (ou référencée) depuis cette URL : oms.min.js
La construction de la map est identique aux précédents exemples. On rajoute l'instantiation de la librairie Spiderfier via la classe
OverlappingMarkerSpiderfier
qu'on associe à notre map.
var options = {
center: new google.maps.LatLng(47.90296, 1.90925),
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("cluster"), options);
var oms = new OverlappingMarkerSpiderfier(map);
La méthode getCities()
renvoit un tableau de tableaux de toutes les villes avec pour chaque entrée son nom, sa latitude et sa longitude
(regardez le code source si besoin). Nous itérons ensuite sur ce tableau pour construire nos markers. Dans les options du marker, nous ne définissons
pas la map associée comme nous l'avions fait précédement, cela sera fait par la suite. En revanche, un attribut city
a été ajouté auquel
on fourni le tableau correspondant à la ville pour laquelle on construit le marker. Il est possible d'ajouter n'importe quel attribut de cette façon.
Cela s'avère très utile par la suite lorsque l'on doit gérer l'évènement click sur ce marker. Le marker est ajouté à un tableau qui nous servira un
peu plus loin et également à l'objet oms
qu'on a instantié.
var cities = getCities();
var markers = [];
for (var i = 0; i < cities.length; i++) {
var markerOptions = {
position: new google.maps.LatLng(cities[i][1], cities[i][2]),
title: cities[i][0],
city: cities[i]
};
var marker = new google.maps.Marker(markerOptions);
markers.push(marker);
oms.addMarker(marker);
}
La création de nos cluster tient en une seule ligne. Il suffit d'instantier la classe MarkerClusterer
, de lui fournir notre map,
le tableau content tout nos markers (de type google.maps.Marker
) et enfin un dernier paramètre très important, une liste d'options
supplémentaires. Nous définissons uniquement l'option maxZoom
qui indique à la librairie à partir de quel niveau de zoom celle-ci
doit arrêter de faire des clusters. Prenons l'exemple de notre carte, si nous retirons ce paramètre alors en zoomant sur Honfleur nous verions
un cluster de 10 markers sans possibilité d'accéder à ces markers. En précisant le paramètre, quand on arrive à un zoom de 9, le cluster est remplacé
par un marker (ou par des markers dans d'autres situation). En cliquant dessus, la librarie Spiderfier prend le relais pour nous montrer les markers
qui sont tous localisés au même point. (Mise à jour de 2016) Il faut maintenant préciser l'emplacement des images correspondant aux clusters via
l'attribut imagePath
.
var markerClusterer = new MarkerClusterer(map, markers, {
maxZoom: 9, // maxZoom set when clustering will stop
imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'
});
Très pratique si vous utilisez ce tutoriel dans le cadre d'un outil de recherche, ce code permet de limiter le viewport (la zone visible de la map)
à l'ensemble des markers que votre recherche a ramené. Ici le résultat, assez naturellement, limite le viewport à la France. Pour cela nous créons
l'objet google.maps.LatLngBounds
composé de deux objets google.maps.LatLng
pour représenter un rectangle à partir des points
situés dans les coins Sud-Ouest et Nord-Est de celui-ci. Ensuite nous parcourons notre tableau de markers et demandons à notre objet bounds
de s'étendre afin de contenir la position de notre marker. Finalement, nous appelons la méthode fitBounds
de notre map pour redéfinir
correctement le viewport qui contiendra donc tous nos markers.
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; ++i) {
bounds.extend(this.markers[i].position);
}
map.fitBounds(bounds);
Dans l'exemple précédent nous avons vu comment afficher une fenêtre d'information lorsque l'on click sur un marker. Nous reprenons ici l'objet
google.maps.InfoWindow
qui représente notre fenêtre mais cette fois-ci nous ne définissons pas tout de suite son contenu. De plus,
plutôt que d'ajouter un listener sur notre marker nous ajoutons un listener global via notre objet oms
. Ainsi nous n'aurons qu'un
seul listener au lieu de 878, une petite économie de mémoire et de performance. La méthode addListener
prend un type d'évènement et une
fonction à exécuter dont le premier paramètre est le marker et le second l'évènement google à l'origine de l'action. Dans son traitement, nous
pouvons dynamiquement définir le contenu du marker (via l'objet city
que nous avions passé à sa construction) et l'afficher.
var infoWindow = new google.maps.InfoWindow();
oms.addListener('click', function(marker, event) {
infoWindow.setContent(marker.city[0] + ", " + marker.city[1] + ", " + marker.city[2]);
infoWindow.open(map, marker);
});
Sur la carte de ce tutoriel, zoomez légèrement avec la molette de votre souris vers Vannes. Juste au-dessus de Lorient il doit y avoir un cluster de 10 villes.
Si vous cliquez dessus vous vous rendez compte qu'il contient une petite ville nommée Hennebont et la liste de ses cedex. Tous les markers du cluster
sont donc exactement localisés au même point. Sans le code qui suit le zoom aurait donc été poussé à son maximum (21), c'est-à-dire à deux rues visibles
sur tout le viewport. Ce n'est pas forcément très beau. Ce code ajoute un listener sur notre objet markerClusterer
pour l'évènement
clusterclick
. Il définit une fonction qui calcule la taille du viewport du cluster puis si le zoom qui en résulte est supérieur à 14 redéfinit
le zoom de la map à 14. Ainsi on obtient le résultat visible ci-dessous au lieu de voir deux rues sans contexte véritable. Bien sûr cela
n'empèche pas l'utilisateur de zoomer lui-même avec sa molette au-delà du niveau 14.
google.maps.event.addListener(markerClusterer, 'clusterclick', function(cluster) {
map.fitBounds(cluster.getBounds());
if (map.getZoom() > 14) {
map.setZoom(14);
}
});
Voici le code complet qui vous permettra de tester cet exemple dans une page HTML. Notez qu'il vous faudra remplir le tableau cities par vous-même. Soit en le copiant depuis le code source de cette page, soit en utilisant vos propres données.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/src/markerclusterer.js"></script>
<script type="text/javascript" src="https://jawj.github.com/OverlappingMarkerSpiderfier/bin/oms.min.js"></script>
</head>
<body>
<div id="map" style="width:100%; height:100%"></div>
</body>
</html>
<script type="text/javascript">
var options = {
center: new google.maps.LatLng(47.90296, 1.90925),
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), options);
var oms = new OverlappingMarkerSpiderfier(map);
var cities = [];
var markers = [];
for (var i = 0; i < cities.length; i++) {
var markerOptions = {
position: new google.maps.LatLng(cities[i][1], cities[i][2]),
title: cities[i][0],
city: cities[i]
};
var marker = new google.maps.Marker(markerOptions);
markers.push(marker);
oms.addMarker(marker);
}
var markerClusterer = new MarkerClusterer(map, markers, {
maxZoom: 9, // maxZoom set when clustering will stop
imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'
});
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; ++i) {
bounds.extend(this.markers[i].position);
}
map.fitBounds(bounds);
var infoWindow = new google.maps.InfoWindow();
oms.addListener('click', function(marker, event) {
infoWindow.setContent(marker.city[0] + ", " + marker.city[1] + ", " + marker.city[2]);
infoWindow.open(map, marker);
});
google.maps.event.addListener(markerClusterer, 'clusterclick', function(cluster) {
map.fitBounds(cluster.getBounds());
if (map.getZoom() > 14) {
map.setZoom(14);
}
});
</script>
COMMENTAIRES
you tube
Merci beaucoup pour ce tuto,
a quel niveau se trouve votre fonction getCities() ? ou bien quel est la structure pour ajouter les données dans le tableau var cities = [];
Merci
ericfrigot
Bonjour,
Comme indiqué, il faut regarder dans le code source (par exemple sur Chrome on y accède en faisant bouton droit sur la page puis "Afficher le code source de la page"), vous trouverez la fonction getCities() de la ligne 335 à 1216. Un exemple de la structure avec deux villes :
function getCities() {
return [
['01110 - HAUTEVILLE-LOMPNES',45.966667,5.6],
['01110 - HOSTIAZ',45.9,5.533333]
];
}
Nerwan76
Merci beaucoup!! J'ai cherché pendant des heures comment faire ce que je voulais et j'y suis arrivé grâce à ce tuto.
Pour info, je m'en sers sur cette page: https://espace-ventes-prive...
ericfrigot
Avec plaisir ! J'ai regardé votre page et du coup la notice sous la carte n'est pas tout à fait exact, je ne sais pas si c'est important. Comme vous pouvez le voir le zoom définit après l'appel a fitBounds est à 1. Malheureusement il est trop grand alors que 2 serait trop petit et ne permettrait pas de voir tous les marqueurs. En revanche si je vais dans la console et que je fais map.setZoom(1.8); ça cadre mieux. Ce qui est dommage c'est que la librairie ne le fasse pas automatiquement.
Nerwan76
Oui c'est vrai que je me suis dit que le zoom n'était pas totalement parfait. Mais bon, ça me va très bien comme ça! (et puis les emplacements des destinations changent d'une semaine à l'autre donc ça s'ajustera en conséquence...).
Arnaud
Bonjour,
Merci pour ce tutoriel !! Je peux charger des milliers d'adresses presque instantanément, incroyable !
Par contre, je me heurte à un problème : changer d’icône pour chaque valeur. Si je rajoute un élément à var marker option et que dans ma boucle, je l'inclue après les coordonnées (j'ai précisé [3]). Ben plus aucun point ne se charge :-(.
Une idée ?
Encore merci !!
ericfrigot
Bonjour,
C'est à moi de vous remerciez, mon code ne fonctionnait plus. Google à déplacer la librairie MarkerClusterer, j'ai donc pu mettre à jour le code en conséquence. Par contre je ne comprend pas votre problématique, vous voulez changer l’icône des markers ou les icônes des clusters ? Dans le premier cas le premier tutoriel sur Google Maps API explique comment faire. Dans le second cas lorsque l'on construit le MarkerClusterer on peut passer un attribut imagePath, ici il finit par m et s'attend à trouver les images m1.png, m2.png, m3.png ... jusque 5 ou 6 il me semble. Eric