Custom Client Side Clustering Algorithm

In the latest release of Virtual Earth, version 6.2, client side clustering was included as a built in function for shape layers. You can assign a client side clustering algorithm to a shape layer so that it will automatically cluster the shapes when the map is zoomed. This is done by using the VEShapeLayer.SetClusteringConfiguration method. This method can take in two parameters. A clustering type and a VEClusteringOptions object. Currently there is only one built in clustering algorithm type available, grid clustering. Unfortunately there is limited flexibility in this algorithm in that the grid size can not be modified. This can be resolved by making a custom clustering algorithms. This post will show how to create a custom clustering algorithm that can be used with the VEShapeLayer.SetClusteringConfiguration method.

According to the documentation (http://msdn.microsoft.com/en-us/library/cc966716.aspx) a custom clustering algorithm must take in a VEShapeLayer and return an array of VEClusterSpecification objects.

A VEClusterSpecification objects has two properties. A Shapes property and a LatLong property. The Shapes object consists of an array shapes that are being clustered. The LatLong property is the coordinate used to place the clustered pushpin. (http://msdn.microsoft.com/en-us/library/cc966730.aspx)

The custom clustering algorithm that will be created in this blog post will implement a grid clustering algorithm and place the clustered pushpins in the mean average coordinate of the clustered pins. This algorithm is an improvement upon the clustering algorithm described here: http://msdn.microsoft.com/en-us/library/cc161072.aspx

Here is the custom clustering algorithm which grid clusters and places the clusters in the mean average coordinate of the clustered pins. You can modify the size of the grid used in the algorithm by changing the value of the gridSize variable.

function meanAverageGridCluster(baseLayer)
{
    var cluster = new Array();
    //the size of the grid in pixels   
    var gridSize = 30;                       

    //Calculate the size of the map in pixels
    var mapView = map.GetMapView();
    var bottomRight = map.LatLongToPixel(mapView.BottomRightLatLong);
    var mapWidth = parseInt(Math.ceil(bottomRight.x));
    var mapHeight = parseInt(Math.ceil(bottomRight.y));
    //break the map into a grid
    var numXCells = parseInt(Math.ceil(mapWidth / gridSize));
    var numYCells = parseInt(Math.ceil(mapHeight / gridSize));

    //create an array to store all the grid data.
    var gridCells = new Array(numXCells*numYCells);
    //Itirate through the shapes in the base layer
    for(var cnt = 0; cnt < baseLayer.GetShapeCount(); cnt++)
    {
        //convert the shapes latlong to a pixel location
        var shape = baseLayer.GetShapeByIndex(cnt);
        var latLong = (shape.GetPoints())[0];
        var pixel = map.LatLongToPixel(latLong);
        var xPixel = pixel.x;
        var yPixel = pixel.y;

        //check to see if the shape is within the bounds of the viewable map
        if(mapWidth >= xPixel && mapHeight >= yPixel && xPixel >= 0 && yPixel >= 0)
        {
            //calculate the grid position on the map of where the shape is located
            var i = Math.floor(xPixel/gridSize);
            var j = Math.floor(yPixel/gridSize);
            //calculates the grid location in the array
            var key = i+j*numXCells;

            if(gridCells[key]==null)
            {
                gridCells[key] = new VEClusterSpecification();
                gridCells[key].Shapes = new Array();
                gridCells[key].Shapes.push(shape);
            }
            else
            {
                gridCells[key].Shapes.push(shape);
            }
        }
    }
    //Iterate through the clustered data in the grid array
    for(var key = 0; key < gridCells.length; key++)
    {
        //calculate mean clustered coordinate
        if(gridCells[key] != null)
        {
            var size = gridCells[key].Shapes.length;
            var latSum = 0;
            var lonSum = 0;
            for(var i=0;i<size;i++)
            {
                var point = gridCells[key].Shapes[i].GetPoints()[0];
                latSum += point.Latitude;
                lonSum += point.Longitude;
            }
            gridCells[key].LatLong = new VELatLong(latSum/size,lonSum/size);
            cluster.push(gridCells[key]);
        }
    }
    return cluster;   
}

The following code can be used to implement this customer clustering algorithm.

var map = null;

function GetMap()
{
    map = new VEMap(‘myMap’);
    map.LoadMap();

    var clusterLayer = new VEShapeLayer();
    clusterLayer.SetClusteringConfiguration(meanAverageGridCluster);
    map.AddShapeLayer(clusterLayer);

    //Import data from source. In this case a GeoRSS is used. 
    var veLayerSpec = new VEShapeSourceSpecification(VEDataType.GeoRSS, "georsstest.xml", clusterLayer);
    map.ImportShapeLayerData(veLayerSpec);
}

 

Advertisements

2 thoughts on “Custom Client Side Clustering Algorithm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s