Geocoding APIs
April 5, 2011 1 Comment
I am building my first Windows Phone 7 Application. As part of the applications requirements, I need to find the geo coordinates of cross streets. Unlike many phone apps, I do not need to gather that information real-time. Rather, I have a database of 700 cross streets (key locations) that I will load into the phone at app start-up and then use the built in GeocordinateWatcher class of the .NET phone framework to compare to this list when the PositionChanged event is raised. The problem is that the initial list of 700 addresses do not have geocoordinates. I built an application that hits the major map APIs (Yahoo, Bing, Google) to see how well they can create geocoordinates from cross street information that can be used by my phone application.
Bing
I started with Microsoft because this is a .NET application. The really cool thing about the Microsoft API is that I can consume the WCF service so there is no HTTP requests to code or XML/JSON responses to parse.
string queryString = String.Format("{0} AT {1} {2}, NC", intersection.RoadA, intersection.RoadB, crash.City); string results = string.Empty; GeocodeRequest geocodeRequest = new GeocodeRequest(); geocodeRequest.Credentials = new GeocodeService.Credentials(); geocodeRequest.Credentials.ApplicationId = "XXXXXXXXXX"; geocodeRequest.Query = queryString; ConfidenceFilter[] filters = new ConfidenceFilter[1]; filters[0] = new ConfidenceFilter(); filters[0].MinimumConfidence = GeocodeService.Confidence.High; GeocodeOptions geocodeOptions = new GeocodeOptions(); geocodeOptions.Filters = filters; geocodeRequest.Options = geocodeOptions; GeocodeServiceClient geocodeServiceClient = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService"); GeocodeResponse geocodeResponse = geocodeServiceClient.Geocode(geocodeRequest); if (geocodeResponse.Results.Length > 0) { if (geocodeResponse.Results[0].Locations[0].Latitude >= 33 && geocodeResponse.Results[0].Locations[0].Latitude <= 36 && geocodeResponse.Results[0].Locations[0].Longitude >= -84 && geocodeResponse.Results[0].Locations[0].Longitude <= -76) { intersection.Latitude = geocodeResponse.Results[0].Locations[0].Latitude; intersection.Longitude = geocodeResponse.Results[0].Locations[0].Longitude; intersection.GeoCodeSource = "Bing"; } }
Getting a developer key was a snap because I already had a liveID.
· Search Result Rating = Medium
· Developer Experience = High
Yahoo
I then went to Yahoo to supplement the data that was not found by Bing. Coding the Yahoo API was a straight Web Request/Response:
StringBuilder queryString = new StringBuilder(); queryString.Append("http://where.yahooapis.com/geocode?"); queryString.Append("?street="); queryString.Append(intersection.RoadA); queryString.Append("&xstreet="); queryString.Append(intersection.RoadB); queryString.Append("&city="); queryString.Append(intersection.City); queryString.Append("&state=NC"); queryString.Append("&appid="); queryString.Append("XXXXXX--"); HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(queryString.ToString()); WebResponse webResponse = webRequest.GetResponse(); Stream stream = webResponse.GetResponseStream(); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load(stream); if(xmlDocument != null) { XmlNode rootNode = xmlDocument.DocumentElement; string returnCode = rootNode.SelectSingleNode("Error").InnerText; if (returnCode == "0") { double latitude = Double.Parse(rootNode.SelectSingleNode("Result/latitude").InnerText); double longitude = Double.Parse(rootNode.SelectSingleNode("Result/longitude").InnerText); if (latitude >= 33 && latitude <= 36 && longitude >= -84 && longitude <= -76) { intesection.Latitude = latitude; intesection.Longitude = longitude; intesection.GeoCodeSource = "Yahoo"; } } }
Getting the applicationId was a snap because I already had a YahooId. The one amusing thing to me is that when I went to Yahoo Search and tped in “Maps”, Google’s Map page came up before Yahoo.
· Search Result Rating = Medium
· Developer Experience = Medium
I then tried Google’s API. Coding Google was the same as Yahoo (using HTTP Request and Response) with a slightly less verbose query string:
StringBuilder queryString = new StringBuilder(); queryString.Append("http://maps.googleapis.com/maps/api/geocode/xml?"); queryString.Append("?address="); queryString.Append(intersection.RoadA); queryString.Append(" + and + "); queryString.Append(intersection.RoadB); queryString.Append("&city="); queryString.Append(intersection.City); queryString.Append("&state=NC"); queryString.Append("&sensor="); queryString.Append("false");
The problem was that the data coming back is all wrong. For example, I tried to geocode a major street intersection near my house (you can put this into your browser to see the results):
http://maps.googleapis.com/maps/api/geocode/xml?address=I 440+&+Wake Forest Road+Raleigh,+NC&sensor=false
Check out what I got back:
I then tried this:
http://maps.googleapis.com/maps/api/geocode/xml?address=I 440+and+Wake Forest Road+Raleigh,+NC&sensor=false
At least I am in the country now, but not anywhere near the intersection. It appears that Bing and Yahaoo have a much better way of geocoding cross streets. After too many false-positives (and no correct hits), I gave up on the Google API. The nice thing about the Google API is that I didn’t have to register for an app id. The not-so-nice thing is that I am limited to 2,500 a day unless I joined something called Premium Developer.
· Search Result Rating = Low
· Developer Experience = Medium
For my project then I am using Bing and supplementing with Yahoo. I am not using Google.
Great info!! I have been working with maps more than I would like to these days and I am sure I will be able to use the advice you have given here, thanks!! And I think the 2500 limitation to google maps v3 is per IP address, so you actually get way more requests than just 2500 that way…just food for thought incase you were wanting to use it on any projects…thanks for you info!!!