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 2009
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 2009

Programming MapPoint in .NET

MapPoint Book

  Spatial Community
ViaVirtualEarth
SVG Tutorials
MapPoint

Map Visitors

 Direct Media

Travel Insurance Compare over 450 travel insurance policies (inc. annual multi-trip, single trip & winter sports)

  ARTICLES  


Snap to a Road

Wilfried Mestdagh's article shows how to take a lat/lon and find the nearest road relative to that point. The technique, written entirely in Delphi, employs reverse geocoding and determines the lat/lon using a CalcXY routine.

This article will show how to snap a position to a road. Many times a GPS receiver's calculated position is not as accurate as we should like it to be, and when you project the position in MapPoint by means of a pushpin the vehicle is not on the road.

Also a vehicle might be not on any (digitized) road at all, for example it can be on a field or a parking lot. In that case you don't want to snap it to a road but you maybe want information like: 'The vehicle is 20 meters south-east from streetname, streetnumber'. And this is exacly what the code given in this article will produce.

One warning is that if your application assumes a position that is not the real (actually the wrong) position that the GPS receiver gives you, you might get into trouble if customar is not fully informed of this assumption, so please be very clear on this. Getting wrong or mis-leading data is one thing, but letting an application make assumptions can get dangerous because it can be false information.

The best thing to do is make a vector with given increments, for example 5 meters, because most of the streets are wider. Then on the circumference of that circle we plot a point also every 5 meter until we hit a street or have completed the whole 360 degrees. Then we increment the radius with 5 meters and do it all over again.

This code example is written in Delphi, but even if you code in another language it should be fine readable for you. Afterall you're a programmer :).

First we create a little class named TMapPoint which handles the program's execution. This constructor creates a hidden MapPoint object because even if we have a visible map in the application we do not want to change it when this class is executing. There is only one public method:

function TMapPoint.GetStreetAddr(Lat, Lon: double; Streets: TStrings;
out Direction: string; out Distance: integer): boolean;
var Angle: integer; begin Streets.Clear; Distance := 0; Direction := ''; if not FindAddr(Lat, Lon, Streets) then if CalcInCircle(Lat, Lon, Streets, Distance, Angle) then Direction := GetDirection(Angle); Result := Streets.Count > 0; end;
It take as arguments Latitude Lat and Longitude Lon and puts the result values in Streets, Direction and Distance.

It first checks if the given coordinates are on a street calling FindAddr, if not then we see if we have a street in a given radius by means of CalcInCircle.

I'm not going to discuss the GetDirection function. All it does is divide the circle into 8 panes and return a string according to the angle, eg N, NE, etc.

The FindAddr encapsulates the MapPoint ObjectsFromPoint method:

function TMapPoint.FindAddr(Lat, Lon: double; Streets: TStrings): boolean;
var StreetResults: FindResults; Loc: Location; n, x, y: integer; Index: OLEVariant; begin FMP.ActiveMap.GoToLatLong(Lat, Lon, 1); Loc := FMP.ActiveMap.GetLocation(Lat, Lon, 1); x := FMP.ActiveMap.LocationToX(Loc); y := FMP.ActiveMap.LocationToY(Loc); StreetResults := FMP.ActiveMap.ObjectsFromPoint(x, y); if StreetResults.ResultsQuality < geoNoGoodResult then for n := 1 to StreetResults.Count do try Index := n; Loc := StreetResults.Item[Index] as Location;
if Assigned(Loc.StreetAddress) then Streets.Add(Loc.StreetAddress.Value); except end; Result := Streets.Count > 0; end;
What it does is check the StreetResults collection. It can have several or non Location objects, and it can contain other objects too, so we have to take all this into account. Finally it checks if the StreetAddress property is not nil and it adds the street to the output.

The CalcInCircle function is the one that will try to find an address as close as possible within a given maximum radius:

function TMapPoint.CalcInCircle(CenterLat, CenterLon: double;
Streets: TStrings; out Distance, Angle: integer): boolean;
var Lat, Lon: double; Radius: double; // radius of the circle in degree Circum: integer; // circumfence of the circle in Distance Count: integer; // the amount of points to calculate on the circumfence AngleInc: integer; // angle increments in degree per calculation begin Distance := FDistanceResolution; while Distance <= FMaxDistance do begin Radius := 1 / 60 / 1852 * Distance;
Circum := Trunc(Distance * 2 * PI);
Count := Circum div FDistanceResolution;
AngleInc := 360 div Count;
if AngleInc <= 0 then begin Result := False; Exit; end; Angle := 0;
while Angle < 360 do begin CalcXY(CenterLat, CenterLon, Angle, Radius, Lat, Lon); Result := FindAddr(Lat, Lon, Streets); if Result then Exit; Inc(Angle, AngleInc); end; Inc(Distance, FDistanceResolution); end; Result := False; end;
As you see we have two loops in there. The outer loop checks if we are within the maximum radius and the inner loop computes if we have a street on the circumfence of the circle. The mysterious 1 / 60 / 1852 is because we will plot our points in meters. FDistanceResolution is the distance between our search points on the circumfence and also the increment value of the radius of the circle. CalcXY computes for each point the correct Latitude and Longitude, and findAddr (already discussed) checks if we are on a street. The moment we found a street we return from this function.

I think the rest is self-expanatory, so we only have to discuss the calcultion CalcXY itself:


   given A en c
a = c * sin(A)
b = c * cos(A)
  • A == 0 is direction East in this drawing
  • a is Latitude offset
  • b is Longitude offset
  • c is Radius of circle
So that's where CalcXY makes use of:
procedure TMapPoint.CalcXY(centerLat, centerLon, Angle, Radius: double; out Lat, lon: double);
var a, b, c, r: double; begin r := DegToRad(Angle); c := Radius; a := c * Sin(r); b := c * Cos(r) / Cos(DegToRad(centerLat)); // correct longitude for circle Lat := CenterLat + a; Lon := CenterLon + b; end;

As you see it is used as in the above drawing to calculate a latitude / longitude pair of the points on the circumfence. We need the Cos function to correct longitude distance if we are not on the equator.

I might share an interesting word about the CalcInCircle function. As you see it gives up the search when it found 1 address. And yes in one of my applications it had to be that way, but in another one I had to give all addresses on the same circle if there was more than one. This can easy be done by adding a HitCount variable and instead of exit in the inner loop just increment that variable. This way it will allways finish the circle. And of course you exit the outer loop if HitCount is greater than zero.

I have dropped the complete code here in this textbox so you can easily copy it if needed. After all, so much of programming is just a copy / paste operation :).

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
-bingmaps: Great video of Estradas de Portugal's use of #BingMaps for highways and traffic portal http://bit.ly/9O2HkA
-bingmaps: 4 easy steps to embed #BingMaps Twitter maps on your website http://bit.ly/9d0jEX
-bingmaps: RT @ChrisPendleton: After much ado, the Bing Twitter Maps EMBED feature is LIVE (for real, this time!): http://bit.ly/cUslPQ. @BingMaps
-bingmaps: @USAToday talks to @ChrisPendleton about #BingMaps http://bit.ly/cdzZqJ
-MapPoint 2010 optimize gives different results
-Mappoint on an access form
-blank screen when searching place or address
-Embed Bing Maps Twitter Maps On Your Site
-bingmaps: RT @JohannesKebeck: New blog post: http://tinyurl.com/ygmlayc - Spatial Data Management and Data Visualization with SQL Server 2008 & Bi ...
-Getting 2006 symbols in 2010?
-bingmaps: At #wherecampeu today? Look out for @johanneskebeck and @mrtea72 from the #BingMaps team
-Using MapPoint to see Deaths
-Spatial Data Management and Data Visualization with SQL Server 2008 & Bing Maps
-bingmaps: Join the UK #BingMaps team for the finance and insurance sector webcast tomorrow http://bit.ly/chMYcb
-Bing Maps at MIX10!
-Bing Maps at MIX10!
-bingmaps: Want live Twitter and Maps on your website, use the new widget from Bing Maps: http://bit.ly/9KkFSa
-linked files
-Criteria for extracting postcodes in Mappoint Euro Ver
-Spatial Data Import COM Add-in / Shapes (SHP file)
-BingMapsDev: @jont87 For Silverlight I would start here: http://is.gd/a4BAq and then look at the Bing Maps iSDK: http://is.gd/a4C4y
-bingmaps: Channel 5's Gadget Show are impressed by #BingMaps Birds Eye (forward to 24 mins) http://bit.ly/9Cn6sY
-bingmaps: #BingMaps SXSW Panel Announced http://bit.ly/9GWSeT
-bingmaps: Bing Maps retail and supply chain webcast starts in 10 minutes. Register here: http://bit.ly/dsNNJD
-Bing Maps SXSW Panel Announced
 Resources
Browse GIS books and periodicals
Best Car Contract Hire Lease Save
Find a MapPoint Partner or Consultant
Real Estate Columbia, MO


Want Your Site To Appear Here?

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

• Replacement Cordless Phone Batteries from DSMiller
Improve your career with a computer degree LocalEDU.com
• Tim Cohn's Marketing Strategy Consultancy
• Buy Toner Cartridges at Marquee Office Solutions
• Looking for Dry Erase Boards? See Dry Erase Boards • The Search Starts Here Used Cars
• 5339 - We Know Bikes: Road Bikes / Triathlon Bikes / Mountain Bikes / BMX Bikes
• Buy Your Car Used Cars UK, Car Lease and Contract Hire