Pushpin Clustering in Bing Maps V8

Last month we announced the preview release of the Bing Maps V8 Web control. This new HTML5 power map control brings a lot of performance improvements and many new features. Today we would like to highlight the new Clustering module and how it can be used to make your data easier to navigate.

What is Pushpin Clustering?

With the performance improvements in the V8 map control it may be tempting to simply load all your data onto the map. However, if you add enough pushpins, eventually they will cover most of the map and make the map cluttered and difficult to navigate. For example, here is a map with 1,000 pushpins on it:

Map covered by a lot of pushpins.

Pushpin clustering makes it easier to navigate when there are a lot of pushpins on the map by taking closely positioned pushpins and grouping them into clusters. These clusters can then be displayed differently to indicate that they are a cluster of pushpins, and not a single pushpin. By default, the Clustering module represents clusters by using the default pushpin and simply adds the number of pushpins in a cluster as text on it. As you zoom into the map, the clusters will break apart into their individual pushpins. Here is an example of 1,000 pushpins rendered using the clustering module. As it zooms in the clusters break apart into their individual pushpins.

Animated image of a map being zoomed in to show clustered pushpins breaking apart.

Adding a cluster layer to the map

Now that we know what a clustering is, let’s see how you can add a clustering layer to your map. The following code example loads the Clustering module and then generates 1,000 random pushpins that are within the current map view using the TestDataGenerator class, which is built into the V8 map control. It then creates an instance of the ClusterLayer class and passes in the pushpins to be clustered and inserts it into the map.

<!DOCTYPE html>

<html>

<head>

    <title></title>

    <meta charset="utf-8" />

    <script type='text/javascript'

            src='http://www.bing.com/api/maps/mapcontrol?callback=GetMap'

            async defer></script>

    <script type="text/javascript">

        function GetMap() {

        var map = new Microsoft.Maps.Map('#myMap', {

            credentials: ‘Your Bing Maps Key’

        });

 

        //Load the Clustering module.

        Microsoft.Maps.loadModule("Microsoft.Maps.Clustering", function () {

            //Generate 1,000 random pushpins in the map view.

            var pins = Microsoft.Maps.TestDataGenerator.getPushpins(1000, map.getBounds());

 

            //Create a ClusterLayer and add it to the map.

            var clusterLayer = new Microsoft.Maps.ClusterLayer(pins);

            map.layers.insert(clusterLayer);

        });

    }

    </script>

</head>

<body>

    <div id="myMap" style="position:relative;width:600px;height:400px;"></div>

</body>

</html>

Running this code in a browser will display a map with a bunch of pushpins on it. Those that have numbers on them are clusters of pushpins. As you zoom the map towards a cluster you will see the cluster break apart into its individual pushpins.

Map with a lot of pushpins grouped into clusters

Try it here

There are a number of different ways to calculate clusters. Common algorithms that are used for clustering pushpins include grid base, point based, and k-means. Grid based clustering is one of the fastest and can handle the most data, this is what the Clustering module uses. Grid based clustering breaks the map into a grid, and if any two pushpins are in the same grid cell they are clustered together. Once all of the pushpins that are within a grid cell are known, it can then be positioned using a couple of different mechanisms. The most natural way of representing a cluster would be to position it at the average location of all the pushpins it represents. However, by doing this it is possible that if two grid cells have a number of pushpins along a shared edge, clustered pushpins may end up overlapping. A second method would be to simply use the location of the first pushpin in the cluster. This would require no calculation and would align with at least one pushpin in the cluster, but would also have the potential of overlapping of clustered pushpins. By default, the Clustering module uses the mean average position to positon a cluster, however you can change this using the clusterPlacementType option on the layer.

Customizing Clustered Pushpins

By default, the clustering layer uses the default pushpin and sets the text option to the number of pushpins that are in the cluster. Customizing the clustered pushpins can be done by passing a callback function into the clusteredPinCallback option of the cluster layer. This callback will receive a reference to a ClusterPushpin object which is basically a pushpin that has a couple of extra properties on it. You can customize the ClusterPushpin the same way you would a standard pushpin, using the setOptions function. For example:

var clusterLayer = new Microsoft.Maps.ClusterLayer(pins, {

    clusteredPinCallback: function (cluster) {

        //Customize clustered pushpin.

        cluster.setOptions({

            icon: '[custom pushpin info]'

        });

    }

});

There are many different ways to customize a pushpin in Bing Maps such as using a URL to an image or SVG file, using inline SVG or a Base64 image string. Documentation on how to create these different types of custom pushpins here.

In this code example, instead of simply using a custom image to represent a clustered pushpin, we will see how to dynamically create an inline SVG icon based on the number of pushpins that are in the cluster. Clusters will be represented using a circle. The more pushpins in the cluster the larger the circle will become, using a logarithmic scale. Since the size of the circles will grow, we will increase the grid size used by the clustering layer to accommodate this. Additionally, if a cluster has less than 10 pushpins, the circle will be green, less than 100 pushpins, yellow, more than 100, red.

<!DOCTYPE html>

<html>

<head>

    <title></title>

    <meta charset="utf-8" />

    <script type='text/javascript'

            src='http://www.bing.com/api/maps/mapcontrol?callback=GetMap' async defer></script>

    <script type="text/javascript">

    var map, clusterLayer;

 

        function GetMap() {

            map = new Microsoft.Maps.Map('#myMap',{

                credentials: 'Your Bing Maps Key',

               zoom: 3

            });

 

        Microsoft.Maps.loadModule("Microsoft.Maps.Clustering", function () {

            //Generate 3000 random pushpins in the map view.

            var pins = Microsoft.Maps.TestDataGenerator.getPushpins(3000, map.getBounds());

 

            //Create a ClusterLayer with options and add it to the map.

            clusterLayer = new Microsoft.Maps.ClusterLayer(pins, {

                clusteredPinCallback: createCustomClusteredPin,

                gridSize: 80

            });

            map.layers.insert(clusterLayer);

        });

        }

 

        function createCustomClusteredPin(cluster) {

            //Define variables for minimum cluster radius, and how wide the outline area of the circle should be.

            var minRadius = 12;

            var outlineWidth = 7;

 

        //Get the number of pushpins in the cluster

            var clusterSize = cluster.containedPushpins.length;

 

        //Calculate the radius of the cluster based on the number of pushpins in the cluster, using a logarithmic scale.

            var radius = Math.log(clusterSize) / Math.log(10) * 5 + minRadius;

 

        //Default cluster color is red.

            var fillColor = 'rgba(255, 40, 40, 0.5)';

 

            if (clusterSize < 10) {

                //Make the cluster green if there are less than 10 pushpins in it.

                fillColor = 'rgba(20, 180, 20, 0.5)';           

            } else if (clusterSize < 100) {

                //Make the cluster yellow if there are 10 to 99 pushpins in it.

                fillColor = 'rgba(255, 210, 40, 0.5)';

            }

 

            //Create an SVG string of two circles, one on top of the other, with the specified radius and color.

            var svg = ['<svg xmlns="http://www.w3.org/2000/svg" width="', (radius * 2), '" height="', (radius * 2), '">',

            '<circle cx="', radius, '" cy="', radius, '" r="', radius, '" fill="', fillColor, '"/>',

            '<circle cx="', radius, '" cy="', radius, '" r="', radius - outlineWidth, '" fill="', fillColor, '"/>',

            '</svg>'];

 

            //Customize the clustered pushpin using the generated SVG and anchor on its center.

            cluster.setOptions({

                icon: svg.join(''),

                anchor: new Microsoft.Maps.Point(radius, radius),

                textOffset: new Microsoft.Maps.Point(0, radius - 8) //Subtract 8 to compensate for height of text.

            });

        }

    </script>

</head>

<body>

    <div id="myMap" style="position:relative;width:600px;height:400px;"></div>

</body>

</html>

Running this code in a browser will cluster 3,000 random pushpins and display them as scaled and colored circles.

Map of clustered pushpins, where the clusters are colored and scaled circles.

Try it now

Conclusion

By using the Clustering module users can easily navigate large sets of pushpins while having a good user experience and performance.

- Ricky Brundritt, Bing Maps Senior Program Manager

Additional Resources