spacer1
spacer2 1_1 1_2
2_1
 Subscribe
 The MP2K Update!
 
 
 
 Magazine
Front Cover
What's New
Articles
News
Sample Data
Gallery
Advertise
About
 Features
MapPoint 2013
Press Releases
MapPoint Forums
Companies
Link to MP2Kmag
Wish List
MapPoint Trial
Authors
 Earlier Content
Past News Items
Past What's New Announcements
 Sponsors
 Order

MapPoint 2013

Programming MapPoint in .NET

MapPoint Book

  Spatial Community
SVG Tutorials
MapPoint

Map Visitors

  ARTICLES  


Handling Pushpin Collisions

Wilfried Mestdagh shares a solution for reducing the number of pushpins that must be displayed on a map

The question often comes up where thousands of pushpins must be displayed. However, nobody wants thousands of pushpins on the screen. So if you get that task then first rephrase the problem, that is: I have thousands pushpins, how do I make a comfortable GUI ?

Displaying thousands of pushpins take time. To avoid this you can only display the pushpins that are in view. Another point is the collision. You really don't want to have an ugly view of lots of pushpins overlapping each other.

In this article we describe the most simple thing to avoid collision, that is with sequentially positions as a route from a vehicle. In a future article we describe other possible approach needed for non sequentially positions.

You really don't want to display an ugly screen like the one above. Instead you calculate the distance between each pushpin, get the distance of 1 pixel, and leave preferable 20 pixels between each pushpin. This must be recalculated at every pan or zoom of the map.

This is the result you get, the more you zoom in you get more detail and more pushpins will be plotted. Only pushpins that are in view are plotted. This picture is the same data but with the code described and with a minimum distance of 20 pixels between the center of the pushpins.

You eventually can indicate to the user that there is more detail available when zooming in by setting a custom symbol for each pushpin if there are some not plotted because of collision, but these things are only limited to your own imagination.

Let's explain shortly what we going to do. First of all we calculate the geocoordinates of the 4 corners of the visible map. We need this to only plot pushpins that are in the visible area. So we calculate the top and bottom latitude and the left and right longitude to do that.

Location topLeftLoc = MP.ActiveMap.XYToLocation(0, 0);
Location bottomRightLoc = MP.ActiveMap.XYToLocation(MP.ActiveMap.Width, MP.ActiveMap.Height);
double top = topLeftLoc.Latitude;
double left = topLeftLoc.Longitude;
double bottom = bottomRightLoc.Latitude;
double right = bottomRightLoc.Longitude;

Then we have to specify the minimum distance we want between each pushpin. We can do that to ask MapPoint the distance of 1 pixel. We want at least 20 pixels between the center of our pushpins so we multiply with 20.

double minimumDistance = MP.ActiveMap.PixelSize * 20;

We do the calculation for each record. In this example dispLat and dispLon is the last displayed pushpin where we want a minimum distance from. You can put it in AfterViewChange then it is recalculated at every pan or zoom of the map.

double lat = positions.DataSource[positions.Index].Lat;
double lon = positions.DataSource[positions.Index].Lon;
if (lat <= top && lat >= bottom && lon >= left && lon <= right &&
    getDistance(dispLat, dispLon, lat, lon) >= minimumDistance) {
    dispLat = lat;
    dispLon = lon;
    Location loc = MP.ActiveMap.GetLocation(lat, lon, 1);
    MP.ActiveMap.AddPushpin(loc, positions.DataSource[positions.Index].Name);
}

We only need to explain the getDistance function. The example is for km. The cosine calculation is for correction of the distance in longitude because this is only equal to latitude if you are on the equator. In this example the distances are very short so this is only needed once and not in each call so you can speed it up. The 60 * 1.852 is because 1 arc minute is exact 1 nautical mile and the division by 1.609 is if your geo units are in miles instead of in kilometres.

private double getDistance(double lat1, double lon1, double lat2, double lon2)
{
    double lat = lat2 - lat1;
    double lon = (lon2 - lon1) * Math.Cos(Deg2Rad((lat1 + lat2) / 2));
    double dist = Math.Sqrt(Math.Pow(lat, 2) + Math.Pow(lon, 2));
    return dist * 60 * 1.852;
    //return dist * 60 * 1.852 / 1.609;
}

This is the CSV file from where I have got the data in this example and here is the complete code inclusive the reader and object data table I have used so that you can drop it in a VS project to test it. Just make a new project, drop an MapPoint activeX component on it, name it MP, assign the AfterViewChange and you can see it working.

In this example I delete all datasets on the map. You don't want this of course. I suggest to create a dataset for the route and only delete that particular dataset. But this is only a quick example of course. The data is in Belgium, but maybe someone can make a CSV file from a route somewhere in US and post the link here, so that the example is also usable with MapPoint North America version.

Discuss this story in the forum.

Author: Wilfried Mestdagh
Email: wilfried(AT)mestdagh.biz
Wilfried Mestdagh works as software engineer at the company Sonal in Mortsel, Belgium. His main work is writing software for fleet management and onboard computers. Fleet management is mainly written in Delphi and C# while the onboard computers are mostly programmed in C. His department started years ago for specializing in tracking and tracing security and dangerous transport vehicles, but it is grown to satisfy a very wide of vehicle / truck fleet customers.



Google
 
MP2Kmag Internet


 Recent Discussion
 Resources
Browse GIS books and periodicals
Find a MapPoint Partner or Consultant
Real Estate Columbia For Sale By Owner


Want Your Site To Appear Here?

   © 1999-2012 MP2K. Questions and comments to: website@mp2kmag.com
  Microsoft and MapPoint 2002/2004/2006/2009/2010/2011/2013 are either trademarks or registered trademarks of Microsoft.