1) The earth is not flat, so your math needs to account for the curvature of the earth.

2) The Virtual Earth API has, among other things, MapPolygon objects, which are treated as scaled geometry. In my example, I wanted to draw a circle around a point that showed what was on the map 10 miles in any direction. This video shows my reasons why:

I pulled all the parts to build this application from the web. You can find the formulas to calculate distances on a curved surface in Wikipedia: Great Circle Distance

Tim Heuer had a post on calculating the points in a circle using Java: Drawing a Radius

In my project I converted all the code to C# and separated the calculation of locations and the generation of a MapPolygon into two functions.

void AddCircle(Location oLoc, double dRadius, double dOpacity)

{

MapPolygon polygon = new MapPolygon();

polygon.Fill = new SolidColorBrush(Colors.Green);

polygon.Stroke = new SolidColorBrush(Colors.Blue);

polygon.StrokeThickness = 5;

polygon.Opacity = dOpacity;

//this works in miles

polygon.Locations = DrawACircle(oLoc, dRadius);

Map1.AddChild(polygon);

}

public LocationCollection DrawACircle(Location oLoc, double dRadius)

{

var oLocs = new LocationCollection();

var earthRadius = GeoCodeCalc.EarthRadiusInMiles;

var lat = GeoCodeCalc.ToRadian(oLoc.Latitude); //radians

var lon = GeoCodeCalc.ToRadian(oLoc.Longitude); //radians

var d = dRadius / earthRadius; // d = angular distance covered on earths surface

for (int x = 0; x<= 360; x++)

{

var brng = GeoCodeCalc.ToRadian(x); //radians

var latRadians = Math.Asin(Math.Sin(lat) * Math.Cos(d) + Math.Cos(lat) * Math.Sin(d) * Math.Cos(brng));

var lngRadians = lon + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(lat), Math.Cos(d) - Math.Sin(lat) * Math.Sin(latRadians));

var pt = new Location(180.0 * latRadians / Math.PI, 180.0 * lngRadians / Math.PI);

oLocs.Add(pt);

}

return oLocs;

}

Integrating this with the current UI was straight-forward -- I just added a little more code to the actions the application was already doing. So drawing circles around the current location became this:

void btnCircle_Click(object sender, RoutedEventArgs e)

{

var oMyLoc = from oItem in m_oList

where oItem is MyLocation

select oItem;

PointOfInterest oWhereIAm = oMyLoc.First<PointOfInterest>();

if (oWhereIAm == null)

{

MessageBox.Show("Add Where Am I to the Map");

return;

}

for (int i = 1; i <= 5; i++)

{

double dRadius = i * 10.0;

AddCircle(oWhereIAm.Loc, dRadius, .15);

}

}

And drawing a circle of a given radius about any mouse click became this:

void Map1_MouseClick(object sender, MapMouseEventArgs e)

{

var oLocation = Map1.ViewportPointToLocation(e.ViewportPoint);

var oPOI = new PointOfInterest() { Loc = oLocation };

m_oList.Add(oPOI);

RenderPoint(oPOI);

if (cbDraw.IsChecked == true)

try

{

double dRadius = double.Parse(txtRadius.Text);

AddCircle(oPOI.Loc, dRadius, .5);

}

catch( Exception ex)

{

MessageBox.Show(ex.Message);

}

}

In the next post I will show how to measure between two user-selected points.

## 2 comments:

Good to see my old javascript code still kicking around :)

The original source, and a very useful site, for the algorithms is:

http://www.movable-type.co.uk/scripts/LatLong.html

John.

Nice one. I like how your circles are not scaling with the map (as you zoom in they are bigger and as you zoom out they are smaller). I'm trying to get the same effect with MapPolyline but just can't get it to work.

My MapPolyline is quite simple. I just pass in coordinates to Locations collection and style its stroke n stuff. But it scales with zooming and i don't like it :(

Post a Comment