Friday, July 3, 2009

Bing Maps Silverlight Control Part 5: Calculating Distance

My dad was the first person to show me how to measure distances on a map using a pair of dividers and the scale printed on the map key. There is a much better way to do this using the Bing Maps Silverlight control -- it just takes a little programming and finding the right equation.

Here is the Bobcat Video I told you about:

Back to Programming...

Here is how you calculate the distance between points on the earth. You also need to know EarthRadiusInMiles = 3956.0;

/// <summary>
/// Calculate the distance between two geocodes. Defaults to using Miles.
/// </summary>
public static double CalcDistance(double lat1, double lng1, double lat2, double lng2)
return CalcDistance(lat1, lng1, lat2, lng2, GeoCodeCalcMeasurement.Miles);
/// <summary>
/// Calculate the distance between two geocodes.
/// </summary>
public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, GeoCodeCalcMeasurement m)
double radius = GeoCodeCalc.EarthRadiusInMiles;
if (m == GeoCodeCalcMeasurement.Kilometers) { radius = GeoCodeCalc.EarthRadiusInKilometers; }
return radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2)) / 2.0), 2.0) + Math.Cos(ToRadian(lat1)) * Math.Cos(ToRadian(lat2)) * Math.Pow(Math.Sin((DiffRadian(lng1, lng2)) / 2.0), 2.0)))));

I created a Marker class to mark the end points of the distance calculation. To show the user that they are in distance calculation mode, I have also changed the cursor on the map control. In this case, I alternate between the arrow and the pointing finger.

PointsOfInterest m_oMarkers = new PointsOfInterest();

void cbMeasure_Checked(object sender, RoutedEventArgs e)
Map1.Cursor = Cursors.Hand;
void cbMeasure_Unchecked(object sender, RoutedEventArgs e)
Map1.Cursor = Cursors.Arrow;
if ( m_oMarkers.Count > 1 )

The code for collecting the user's selection of points is here:

void Map1_MouseClick(object sender, MapMouseEventArgs e)
var oLocation = Map1.ViewportPointToLocation(e.ViewportPoint);

PointOfInterest oPOI = null;
if (cbMeasure.IsChecked == true)
oPOI = new Marker() { Loc = oLocation };
oPOI = new PointOfInterest() { Loc = oLocation };


if (cbDraw.IsChecked == true)
double dRadius = double.Parse(txtRadius.Text);
AddCircle(oLocation, dRadius, .5);
catch( Exception ex)

Using a polyline and a tool tip is great way to report the distance:

double DistanceInMiles(LocationCollection oLocs)
double dDistance = 0.0;
for (int i = 0; i < oLocs.Count() - 1; i++)
dDistance += DistanceInMiles(oLocs[i], oLocs[i + 1]);

return dDistance;

double DistanceInMiles(Location Loc1, Location Loc2)
double dMiles = GeoCodeCalc.CalcDistance(Loc1.Latitude, Loc1.Longitude, Loc2.Latitude, Loc2.Longitude);
return dMiles;

LocationCollection RenderLine(List<PointOfInterest> oList)

var oPOIs = (from oPOI in oList
where oPOI is Marker
select oPOI).ToList<PointOfInterest>();

var oLocs = new LocationCollection();
foreach (var oPOI in oPOIs)

return oLocs;

void AddLine(List<PointOfInterest> oList, double dOpacity)
MapPolyline polyline = new MapPolyline();
polyline.Stroke = new SolidColorBrush(Colors.Red);
polyline.StrokeThickness = 1;
polyline.Opacity = dOpacity;

//this works in miles
LocationCollection oLocs = RenderLine(oList);
polyline.Locations = oLocs;

double dDist = DistanceInMiles(oLocs);

ToolTipService.SetToolTip(polyline, string.Format("{0:0.0} mi \n {1:0.00} yd", dDist, dDist * 5280.0 / 3.0));

1 comment:

tningal said...

Great job, this is interesting and useful for distance determination. I have been trying to measure the heights of street trees from Bird's eyeview and wonder if there is a way. There is no measure tool in Bird's eyeview, and even if there is, there are issues with scale for each photo tile. However, I am sure that it should be possible. If you know of a way to measure tree heights in Bird's eyeview, I'll be delighted to know.

My contact is

Thanks and cheers

UCD, Dublin, Ireland