locked
Clustering pushpins in Silverlight Map Control

    Question

  • Is there a way to cluster pushpins that are close to each other in the Silverlight Map Control?
    Thursday, April 23, 2009 10:13 AM

Answers

  • Oh really?
    Well lets see how it looks in here....

    johnWeeGo[1] I was asked on the MSDN VE forum if the new Silverlight control had pushpins and indeed clustered pushpins. The answer is that it doesn’t have the concept of a pushpin but rather a much more flexible method of attaching any UIElement to the map.

    pinstreet

    In terms of clustering this is very possible but today I’d like to start by showing Powerlaw scaling from Lutz applied to the VE control.

    Powerlaw scaling applies a scale transform to the object based on the zoomlevel. It makes the object full size at street level and tiny at world level. It produces a more realistic effect as you zoom in and out where a static sized object appears to grow as you zoom out.

    pinworld

    The interesting part of this is it allows you to show much more information on the map without pins overlapping. It will reduce the situations where you need to cluster your data.

    The formula I use is:

    Math.Pow(0.05*(currentZoomLevel + 1), 2) + 0.01
    

    Adding a Pushpin

     

    Since the control has no built in Pushpin lets make our own, it will simply be a Silverlight control called pin.xaml.

    addcontrol

    For the XAML we will have Grid with the scale transform and an Image control:

    <UserControl x:Class="SoulSolutions.VESL.CustomPushPin.Pin"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Grid HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
            <Grid.RenderTransform>
                <ScaleTransform x:Name="PinScaleTransform" ScaleX="1" ScaleY="1" />
            </Grid.RenderTransform>
            <Image Width="250" Stretch="Uniform" x:Name="PinImage"></Image>
        </Grid>
    </UserControl>
    

    Note we gave the scale transform and the image a name so we can access these in the code behind:

    using System;
    using System.Windows.Media;
    using Microsoft.VirtualEarth.MapControl;
     
    namespace SoulSolutions.VESL.CustomPushPin
    {
        public partial class Pin
        {
            public Pin()
            {
                InitializeComponent();
            }
     
            private Map _map;
            public Map MapInstance {
                get
                {
                    return _map;
                } 
                set
                {
                    _map = value;
                    _map.ViewChangeOnFrame += MapViewChangeOnFrame;
                    ApplyPowerLawScaling(_map.ZoomLevel);
                }
            }
     
            public ImageSource ImageSource
            {
                get { return PinImage.Source; }
                set { PinImage.Source = value; }
            }
     
            void MapViewChangeOnFrame(object sender, MapEventArgs e)
            {
                ApplyPowerLawScaling(MapInstance.ZoomLevel);
            }
     
            private void ApplyPowerLawScaling(double currentZoomLevel)
            {
                double scale = Math.Pow(0.05*(currentZoomLevel + 1), 2) + 0.01;
                if (scale > 1) scale = 1;
                if (scale < 0.125) scale = 0.125;
                PinScaleTransform.ScaleX = scale;
                PinScaleTransform.ScaleY = scale;
            }
        }
    }
    

    Our class has two public properties, the MapInstance, set so we can access the map itself to get the zoomlevel and listen to the ViewChangeOnFrame event, and the ImageSource property to easily set the actual image for the pin.

    When the MapInstance is set we apply the initial scaling, same on every frame of ViewChange. The ApplyPowerLawScaling simply applies the formula with some min/max thresholds.

    To add the pin to the map we create a new layer and use the AddChild() methods like so:

    var layer = new MapLayer();
    map.AddChild(layer);
     
    //Sydney
    layer.AddChild(new Pin
    {
        ImageSource = new BitmapImage(new Uri("pin.png", UriKind.Relative)),
        MapInstance = map
    }, new Location(-33.86643, 151.2062), PositionMethod.Center);
    

    Pretty cool hey? See it in action here. Download the full source here (162KB).

    Next steps

    If you’re interested I can show you how to implement IDisposable to clean up events and stop an animation you may want to be running on your pins as well as extend the control to have a label and a infobox balloon. Leave me a comment if you’re interested.


    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/virtualearth for latest VE news.
    Wednesday, May 06, 2009 11:14 AM

All replies

  • as with the other thread, there are no specific "pushpin" controls or functionality to support them currently in the Silverlight control
    Brian @ Earthware - UK interactive mapping web developers http://www.earthware.co.uk/blog | http://www.twitter.com/earthware
    Friday, April 24, 2009 6:39 AM
  • Yes it is very early days but Silverlight is so much more powerful then javascript I don't think it will take long for something to be published to help you out.
    Are you looking to simple provide a better experience for overlapping pins or looking for performance increases?
    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/virtualearth for latest VE news.
    Wednesday, April 29, 2009 8:36 AM
  • Are you looking to simple provide a better experience for overlapping pins or looking for performance increases?

    Mainly to provide a better experience for overlapping pins.
    Wednesday, April 29, 2009 8:45 AM
  • My short term solution is to use Lutz powerlaw scaling to make the icons appear smaller as you zoomout, I can post a working sample of this in the VE control if this is useful to you?
    http://blogs.msdn.com/lutzg/archive/2008/11/03/powerlaw-scaling-of-synchronized-content-with-deep-zoom.aspx

    John.
    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/virtualearth for latest VE news.
    Wednesday, April 29, 2009 8:49 AM
  • That seems useful, yes. I suppose the scaling of the pins would work. An example would be great, thanks :)

    Wednesday, April 29, 2009 8:51 AM
  • I'll do a blog post a little later with a clean sample, my pushpin has labels and an infobox and stuff that will confuse everyone.

    In the constructor to my custom control that xaml look something like this:
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
    <Grid.RenderTransform>
    <ScaleTransform x:Name="PinScaleTransform" ScaleX="1" ScaleY="1" />
    </Grid.RenderTransform>
    <Image Width="100" x:Name="PinImage"></Image>
    </Grid>
    

    I pass in the map object so I can attach to the "ViewChangeOnFrame" event.

    In this event if the ZoomLevel has actually changed I call my method to scale the control, I also call this in the constructor to start it off correctly:

    private void ApplyPowerLawScaling(currentZoomLevel)
    {
            double scale = Math.Pow(0.05*(currentZoomLevel + 1), 2) + 0.01;
            if (scale > 1) scale = 1;
            if (scale < 0.125) scale = 0.125;
            PinScaleTransform.ScaleX = scale;
            PinScaleTransform.ScaleY = scale;
    }
    You may need to tweak the values to suit, I'll clean this up to have a configurable scale factor over the weekend.
    I hope this gets you off to good start.
    john.
    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/virtualearth for latest VE news.
    Wednesday, April 29, 2009 9:08 AM
  • Hi John

    Interesting approach, Im playing with exactly this concept today but for over 1,000 pushpins. Any idea how well you idea would work then? I am looking at adding a pushpin with a bounding rectange which then makes it a fixed size so as you zoom out it gets smaller and larger as you zoom in, Ive set my pushpin content to have a max width so it doesnt get massive when you are really zoomed in and it seems to work ok without having to hook up any events. I almost have it working with a few issues when pins are located at the edge of the map.

    I'll hopefully get a blog article on it asap once I have this project out the way in a few weeks, would be good to see an article on your approach and its scalability.
    Brian @ Earthware - UK interactive mapping web developers http://www.earthware.co.uk/blog | http://www.twitter.com/earthware
    Thursday, April 30, 2009 6:59 PM
  • Ok, a little late I know but I have posted up a working sample and full source:
    http://www.soulsolutions.com.au/Blog/tabid/73/EntryId/599/Powerlaw-scaling-Pushpin-in-the-Virtual-Earth-Silverlight-control.aspx

    I was very close with my example here but always easier to see a full solution. The trick is you have to use a centred image, so in my example I resized the pin image so the tip is in the centre.
    John.
    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/virtualearth for latest VE news.
    Tuesday, May 05, 2009 12:45 PM
  • Hi John,
    Thanks for the example. However, I get a runtime error when trying to view http://www.soulsolutions.com.au/Blog/tabid/73/EntryId/599/Powerlaw-scaling-Pushpin-in-the-Virtual-Earth-Silverlight-control.aspx

    Morten
    Wednesday, May 06, 2009 10:41 AM
  • Oh really?
    Well lets see how it looks in here....

    johnWeeGo[1] I was asked on the MSDN VE forum if the new Silverlight control had pushpins and indeed clustered pushpins. The answer is that it doesn’t have the concept of a pushpin but rather a much more flexible method of attaching any UIElement to the map.

    pinstreet

    In terms of clustering this is very possible but today I’d like to start by showing Powerlaw scaling from Lutz applied to the VE control.

    Powerlaw scaling applies a scale transform to the object based on the zoomlevel. It makes the object full size at street level and tiny at world level. It produces a more realistic effect as you zoom in and out where a static sized object appears to grow as you zoom out.

    pinworld

    The interesting part of this is it allows you to show much more information on the map without pins overlapping. It will reduce the situations where you need to cluster your data.

    The formula I use is:

    Math.Pow(0.05*(currentZoomLevel + 1), 2) + 0.01
    

    Adding a Pushpin

     

    Since the control has no built in Pushpin lets make our own, it will simply be a Silverlight control called pin.xaml.

    addcontrol

    For the XAML we will have Grid with the scale transform and an Image control:

    <UserControl x:Class="SoulSolutions.VESL.CustomPushPin.Pin"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Grid HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
            <Grid.RenderTransform>
                <ScaleTransform x:Name="PinScaleTransform" ScaleX="1" ScaleY="1" />
            </Grid.RenderTransform>
            <Image Width="250" Stretch="Uniform" x:Name="PinImage"></Image>
        </Grid>
    </UserControl>
    

    Note we gave the scale transform and the image a name so we can access these in the code behind:

    using System;
    using System.Windows.Media;
    using Microsoft.VirtualEarth.MapControl;
     
    namespace SoulSolutions.VESL.CustomPushPin
    {
        public partial class Pin
        {
            public Pin()
            {
                InitializeComponent();
            }
     
            private Map _map;
            public Map MapInstance {
                get
                {
                    return _map;
                } 
                set
                {
                    _map = value;
                    _map.ViewChangeOnFrame += MapViewChangeOnFrame;
                    ApplyPowerLawScaling(_map.ZoomLevel);
                }
            }
     
            public ImageSource ImageSource
            {
                get { return PinImage.Source; }
                set { PinImage.Source = value; }
            }
     
            void MapViewChangeOnFrame(object sender, MapEventArgs e)
            {
                ApplyPowerLawScaling(MapInstance.ZoomLevel);
            }
     
            private void ApplyPowerLawScaling(double currentZoomLevel)
            {
                double scale = Math.Pow(0.05*(currentZoomLevel + 1), 2) + 0.01;
                if (scale > 1) scale = 1;
                if (scale < 0.125) scale = 0.125;
                PinScaleTransform.ScaleX = scale;
                PinScaleTransform.ScaleY = scale;
            }
        }
    }
    

    Our class has two public properties, the MapInstance, set so we can access the map itself to get the zoomlevel and listen to the ViewChangeOnFrame event, and the ImageSource property to easily set the actual image for the pin.

    When the MapInstance is set we apply the initial scaling, same on every frame of ViewChange. The ApplyPowerLawScaling simply applies the formula with some min/max thresholds.

    To add the pin to the map we create a new layer and use the AddChild() methods like so:

    var layer = new MapLayer();
    map.AddChild(layer);
     
    //Sydney
    layer.AddChild(new Pin
    {
        ImageSource = new BitmapImage(new Uri("pin.png", UriKind.Relative)),
        MapInstance = map
    }, new Location(-33.86643, 151.2062), PositionMethod.Center);
    

    Pretty cool hey? See it in action here. Download the full source here (162KB).

    Next steps

    If you’re interested I can show you how to implement IDisposable to clean up events and stop an animation you may want to be running on your pins as well as extend the control to have a label and a infobox balloon. Leave me a comment if you’re interested.


    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/virtualearth for latest VE news.
    Wednesday, May 06, 2009 11:14 AM
  • This works well.  Thank you.  Can you provide an example of the next steps please?
    "If you’re interested I can show you how to implement IDisposable to clean up events and stop an animation you may want to be running on your pins as well as extend the control to have a label and a infobox balloon. Leave me a comment if you’re interested."
    Sunday, May 17, 2009 1:48 AM
  • Very very nice.
    How did you create the pushpin image? I need them in different colors to display different groups.
    Thursday, July 09, 2009 7:59 PM
  • That pushpin is from Lutz (Microsoft), I'm not sure what the copyright is sorry. I had one made for my commerical projects. If you have photoshop then you do this:
    Image->Adjustments->hue/saturation
    Then change the hue slider to the colour you desire.

    John.
    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/bingmapsdev for latest news.
    Thursday, July 09, 2009 11:51 PM
  • hey is it a must to use VE controls if i want to ad a "UIELEMENT" to my multiscaleimage control??? or a pushpin of sth????

    Friday, July 10, 2009 8:48 PM
  • There is a wrapper library of DeepZoom in silverlight.

    - Supports Push Pin with graphic asset
    - Custom Map
    - Pan & Zoom In/Out just like Google Map
    - Contains objects such as moving cloud and cars
    - Supports PathAnimation class that allow user move any object by curve

    Sample Page : http://hirihiri.com/project_ROH/silverlight/deepzoomcontainer/DeepZoomContainer.TestTestPage.html

    http://deepzoomcontainer.codeplex.com/



    Thursday, September 17, 2009 2:20 PM
  • Are there any plans for Microsoft to implement clustering?


    Eric Kaufman
    Thursday, April 01, 2010 7:41 PM
  • Here's an example of scaling the Pushpin class that's in the Bing Maps Silverlight v1.0 control instead of requiring you to create a custom image "pushpin". It's implemented with a little help from a custom IValueConverter, ScaleTransform and a little Data Binding.

    http://pietschsoft.com/post/2010/06/04/Resizing-and-Auto-Scaling-Pushpin-in-Bing-Maps-Silverlight.aspx


    Microsoft MVP - Windows Live Platform
    Blog: http://pietschsoft.com | Web.Maps.VE - ASP.NET AJAX Virtual Earth Server Control
    Friday, June 04, 2010 6:22 AM
  • My short term solution is to use Lutz powerlaw scaling to make the icons appear smaller as you zoomout, I can post a working sample of this in the VE control if this is useful to you?
    http://blogs.msdncom/lutzg/archive/2008/11/03/powerlaw-scaling-of-synchronized-content-with-deep-zoom.aspx

    John.
    Windows Live Developer MVP - www.soulsolutions.com.au - follow http://twitter.com/virtualearth for latest VE news.

    It is exactly what I need, Thanks for your help! The link you provided is very useful.
    Sunday, August 29, 2010 4:03 AM
  • @Eric Kaufman - In order to be get the most benefit from clustering, I find that it should be implemented as early as possible in your dataflow process. If you're retrieving the pushpin data for your map from a database, for example, then the clustering is best done in or as close to the database layer as possible, rather than at the client end in Silverlight (or javascript).

    If Microsoft were to include a clustering algorithm in the Silverlight Map Control itself, this might help reduce then number of elements that would have to be plotted on the map, but not the amount of data that would have to be sent to, or processed by the client, and this is a large part of what slows maps down when they have a lot of data. If you aggregate your data into clusters on the server side you'll generally get much better results.

    So, to answer your question, I wouldn't expect MS to have clustering as a high-priority item for the Silverlight Maps control.

     


    Beginning Spatial with SQL Server http://www.apress.com/book/view/1430218290
    Sunday, August 29, 2010 11:53 AM