Windows Phone 8 Development: Maps and Clusters

Windows Phone 8 Development: Maps and Clusters

This code example demonstrates how to dynamically group pushpins in the map control.
There is a lot of code for Windows Phone 7, then I merged all what I need to create a project for WP8.

First of all you need some namespace declaration: for map control and for pushpins from WP Toolkit.


You need also two templates: one for a standard pushpin and the other for the cluster.

    <DataTemplate x:Key="PushpinTemplate">
        <maptk:Pushpin GeoCoordinate="{Binding GeoCoordinate}" Content="{Binding}" />
    <DataTemplate x:Key="ClusterTemplate">
        <maptk:Pushpin GeoCoordinate="{Binding GeoCoordinate}" Content="{Binding Count}"/>

ClustersGenerator is the core of the project. It's a static class that accepts in input:
  • Map control
  • Pushpins collection
  • Cluster DataTemplate  
public ClustersGenerator(Map map, List<Pushpin> pushpins, DataTemplate clusterTemplate)
    _map = map;
    _pushpins = pushpins;
    this.ClusterTemplate = clusterTemplate;
    // maps event
    _map.ViewChanged += (s, e) => GeneratePushpins();
    _map.ZoomLevelChanged += (s, e) => GeneratePushpins();
    _map.CenterChanged += (s, e) => GeneratePushpins();
    // first generate

Every map event launches the pushpins elaboration, but first to explain GeneratePushpins method, let's introduce another class: PushpinGroup.
PushpinGroup represents a standard pushpin or a cluster, and exposes a GetElement method to return them. If the group is a cluster, it needs to get only the first pushpin GeoCoordinate and the content is a group of all pushpins.

public class PushpinsGroup
    private List<Pushpin> _pushpins = new List<Pushpin>();
    public Point MapLocation { get; set; }
    public PushpinsGroup(Pushpin pushpin, Point location)
        MapLocation = location;
    public FrameworkElement GetElement(DataTemplate clusterTemplate)
        if (_pushpins.Count == 1)
            return _pushpins[0];
        // more pushpins
        return new Pushpin()
            // just need the first coordinate
            GeoCoordinate = _pushpins.First().GeoCoordinate,
            Content = _pushpins.Select(p => p.DataContext).ToList(),
            ContentTemplate = clusterTemplate,
    public void IncludeGroup(PushpinsGroup group)
        foreach (var pin in group._pushpins)

The GeneratePushipins function creates clusters based on map ViewPort and a constant named MAXDISTANCE. An extension method convert pushpin GeoCoordinate to a ViewPort Point. That is used to get the distance from other points. If this distance is less then the MAXDISTANCE, the pushpin become a part of cluster.

private void GeneratePushpins()
    List<PushpinsGroup> pushpinsToAdd = new List<PushpinsGroup>();
    foreach (var pushpin in _pushpins)
        bool addGroup = true;
        var newGroup = new PushpinsGroup(pushpin, _map.ConvertGeoCoordinateToViewportPoint(pushpin.GeoCoordinate));
        foreach (var pushpinToAdd in pushpinsToAdd)
            double distance = pushpinToAdd.MapLocation.GetDistanceTo(newGroup.MapLocation);
            if (distance < MAXDISTANCE)
                addGroup = false;
        if (addGroup)
    _map.Dispatcher.BeginInvoke(() =>
        MapLayer layer = new MapLayer();
        foreach (var visibleGroup in pushpinsToAdd.Where(p => _map.IsVisiblePoint(p.MapLocation)))
            var cluster = visibleGroup.GetElement(this.ClusterTemplate) as Pushpin;
            if (cluster != null)
                layer.Add(new MapOverlay() { GeoCoordinate = cluster.GeoCoordinate, Content = cluster.Content, ContentTemplate = cluster.ContentTemplate});
        if (layer.Count > 0)

The extension method GetDistanceTo is the algorithm to calculate the distance between two points:

public static double GetDistanceTo(this Point p1, Point p2)
    return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));

Instead IsPointVisible returns true if the point is visible in the map, otherwise false:

public static bool IsVisiblePoint(this Map map, Point point)
    return point.X > 0 && point.X < map.ActualWidth && point.Y > 0 && point.Y < map.ActualHeight;

Now in your MainPage.xaml, you only need to pass all pushpins to the ClusterGenerator and it will do all work for you.

var clusterer = new ClustersGenerator(map, pushpins, this.Resources["ClusterTemplate"] as DataTemplate);

You can download all code here.

Leave a Comment
  • Please add 6 and 2 and type the answer here:
  • Post
Wiki - Revision Comment List(Revision Comment)
Sort by: Published Date | Most Recent | Most Useful
  • Naomi  N edited Revision 8. Comment: Minor edit

  • Ed Price - MSFT edited Revision 2. Comment: Title casing, needs tech in title, adding tags

Page 1 of 1 (2 items)
Wikis - Comment List
Sort by: Published Date | Most Recent | Most Useful
Posting comments is temporarily disabled until 10:00am PST on Saturday, December 14th. Thank you for your patience.
Page 1 of 1 (8 items)