Thursday, Aug 10, 2017
Google Maps Marker Clustering
In this article, you will learn how to use Google Maps marker clustering to show a lot of markers on a map. We assume that you are familiar with Google Maps and Google Maps Markers. If not, you can read more about it in Google Maps JavaScript API - Intro.
Forest or trees?
It is great to have the ability to show a bunch of markers (locations) in an area, but what if we have too many markers to display? If that’s the case, we’ll have a forest, and won’t be able to see a tree.
To avoid the forest issue, we’ll try to group all the markers in an area on the map and show a specific amount of them. As we zoom into cluster locations, we’ll be able to see the dispersion of markers and clusters around the area.
Default markers clustering
The simplest way to get a sense of how it works is to use basic locations format with lat
and lng
properties:
var locations = [
{lat: 40.733711, lng: -74.000924},
{lat: 40.7419449, lng: -73.9966639},
{lat: 40.80343810000001, lng: -73.9545847},
{lat: 40.76064179999999, lng: -73.9962596},
{lat: 40.7694349, lng: -73.9886707},
{lat: 40.7928421, lng: -73.96588950000002},
{lat: 40.7268495, lng: -73.978535},
{lat: 40.7994527, lng: -73.9688133}
]
After that, we can set up an initial map with default JavaScript library for the MarkerClusterer
from Google Maps repo on GitHub and Google Maps JavaScript API:
<div id="map"></div>
<script>
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: { lat: 40.779033, lng: -73.962297 }
});
}
</script>
<script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js"></script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
To add a few markers to the map, use:
var markers = locations.map(function(location, i) {
return new google.maps.Marker({
position: location
});
});
Now we are ready to create marker clusterer using the function MarkerClusterer
and the property imagePath
to define the cluster layout:
var markerCluster = new MarkerClusterer(map, markers,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
As a result you’ll get this:
You can get a finished code sample here.
Custom markers clustering
Since we weren’t satisfied with the default solution, we made some customization to the default setup.
First of all, the static locations didn’t suit us, so we found a better solution - the GeoJSON format. It supports a couple of geometry types, but we will use only the Point
format to show the markers on our map.
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [40.779033, -73.962297]
},
"properties": {
"name": "New York City"
}
}
The main reason why we chose the GeoJSON format was the fact that google.maps.Data
class has a built-in method loadGeoJson
, which can load GeoJSON from a URL - so we created a service on our side which will provide us with locations from our database.
{
"features": [
{
"geometry": {
"coordinates": [
-73.994265,
40.7618509
],
"type": "Point"
},
"properties": {
"address": "639 10th Avenue"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-73.967482,
40.68906
],
"type": "Point"
},
"properties": {
"address": "295 Clinton Avenue"
},
"type": "Feature"
},
...
],
"type": "FeatureCollection"
}
Our script to fill the map with markers from GeoJSON data will look like this:
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: { lat: 40.779033, lng: -73.962297 }
});
map.data.loadGeoJson('my-locations.json');
}
To create markers clustering with the data from my-locations.json
, we need to extend the loadGeoJson
method a little bit:
map.data.loadGeoJson('my-locations.json', null, function (features){
markers = features.map(function(feature) {
return new google.maps.Marker({
position: feature.getGeometry().get(0)
});
});
var markerCluster = new MarkerClusterer(map, markers,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
});
To avoid collision of the markers and clusters during the zoom in/out process, we need to remove unnecessary features from the map using:
map.data.setMap(null);
As a result, you’ll get this:
You can get a complete code sample here.
Homework - dynamic cluster size
Since we weren’t satisfied with the clusters layout, we found a way to change the default styling. Our goal was to have a dynamic cluster size - depending on the number of markers in a certain area and to ensure this behavior, you need to download the default markerclusterer.js
script and change ClusterIcon.prototype.createCss
function using our custom code:
ClusterIcon.prototype.createCss = function (pos) {
var size = 15;
if (this.cluster_.getMarkers().length < 10) { size = 15; }
if (this.cluster_.getMarkers().length > 10 && this.cluster_.getMarkers().length < 100) { size = 22; }
if (this.cluster_.getMarkers().length > 100 && this.cluster_.getMarkers().length < 1000) { size = 30; }
if (this.cluster_.getMarkers().length > 1000) { size = 37; }
style = ['border-radius : 50%',
'cursor : pointer',
'position : absolute',
'top : ' + pos.y + 'px',
'left : ' + pos.x + 'px',
'width : ' + size * 2 + 'px',
'height : ' + size * 2 + 'px',
'line-height : ' + (size * 2 + 1) + 'px',
'text-align : center',
'background-color: #51B3C3',
'color: #ffffff',
'font-size:14px'
];
return style.join(";") + ';';
};
For your homework, we encourage you to play with our marker’s length thresholds, cluster sizes, and cluster colors. Your result should be something like this: