Consuming GeoCode YHOO API via REST - Dealing with Multiple Results - Part 4

This is the fourth part of my Consuming Services with Windows Phone series. Previously we covered using HttpWebRequest to grab a Yahoo GeoCoding XML feed and process a single geopoint result. In this post we are going to finish out the previous article by handling multiple return results.  The query URL we used for a single result is https://local.yahooapis.com/MapsService/V1/geocode?appid=YD-9G7bey8_JXxQP6rxl.fBFGgCdNjoDMACQA--&street=5426+Bay+Center+Drive&city=Tampa&state=FL  . If you examine the query string you can see we are passing in a full street, city and state. We get one result back.

But what happens if we change the query to be a bit more vague. For instance I grew up in a small town in Georgia called "Covington". The query for just cities named "Covington" would be https://local.yahooapis.com/MapsService/V1/geocode?appid=YD-9G7bey8_JXxQP6rxl.fBFGgCdNjoDMACQA--&city=Covington  . If you click on the query URL you will see more than one result set returned, as show below. We can see Covington is present in GA, as well as OH, OK, TN, and more.

 <?xml version="1.0"?>
<ResultSet xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" 
xsi:schemaLocation="urn:yahoo:maps https://local.yahooapis.com/MapsService/V1/GeocodeResponse.xsd">
<Result precision="zip">
    <Latitude>33.596845</Latitude><Longitude>-83.861644</Longitude><Address></Address>
    <City>Covington</City><State>GA</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
<Latitude>40.136645</Latitude><Longitude>-87.396929</Longitude><Address></Address>
<City>Covington</City><State>IN</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>39.083780</Latitude><Longitude>-84.510179</Longitude><Address></Address>
    <City>Covington</City><State>KY</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>30.479685</Latitude><Longitude>-90.095304</Longitude><Address></Address>
    <City>Covington</City><State>LA</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>46.541910</Latitude><Longitude>-88.535739</Longitude><Address></Address>
    <City>Covington</City><State>MI</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>42.846495</Latitude><Longitude>-78.010008</Longitude><Address></Address>
    <City>Covington</City><State>NY</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>40.117955</Latitude><Longitude>-84.354639</Longitude><Address></Address>
    <City>Covington</City><State>OH</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>36.307410</Latitude><Longitude>-97.586334</Longitude><Address></Address>
    <City>Covington</City><State>OK</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>41.744775</Latitude><Longitude>-77.077184</Longitude><Address></Address>
    <City>Covington</City><State>PA</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>35.564551</Latitude><Longitude>-89.646069</Longitude><Address></Address>
    <City>Covington</City><State>TN</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>32.178935</Latitude><Longitude>-97.258269</Longitude><Address></Address>
    <City>Covington</City><State>TX</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>37.793533</Latitude><Longitude>-79.995899</Longitude><Address></Address>
    <City>Covington</City><State>VA</State><Zip></Zip><Country>US</Country>
</Result>
<Result precision="zip">
    <Latitude>47.358970</Latitude><Longitude>-122.117624</Longitude><Address></Address>
    <City>Covington</City><State>WA</State><Zip></Zip><Country>US</Country>
</Result>
</ResultSet>
<!-- ws05.ydn.gq1.yahoo.com uncompressed/chunked Tue May 10 07:09:26 PDT 2011 -->

So we need to modify our LINQ to XML to handle multiple results inside the result set.  Currently our processing (shown below) uses LINQ to XML, but simply grabs the first result and parses it out. Now we simply need to expand our handling to grab the full set of results.

 GeoCodeResult _geocode = 
    (from _result in _xdoc.Descendants(XName.Get( "Result", "urn:yahoo:maps"))
    select new GeoCodeResult
    {
        Latitude = _result.Element( XName.Get( "Latitude", "urn:yahoo:maps" )).Value,
        Longitude = _result.Element( XName.Get( "Longitude", "urn:yahoo:maps" )).Value,
        Address = _result.Element( XName.Get( "Address", "urn:yahoo:maps" )).Value,
        City = _result.Element( XName.Get( "City", "urn:yahoo:maps" )).Value,
        State = _result.Element( XName.Get( "State", "urn:yahoo:maps" )).Value,
        Zip = _result.Element( XName.Get( "Zip", "urn:yahoo:maps" )).Value,
        Country = _result.Element( XName.Get( "Country", "urn:yahoo:maps")).Value
    }).FirstOrDefault();

string _output = string.Format("lat={0}\nlon={1}\naddr={2}", _geocode.Latitude, 
    _geocode.Longitude, _geocode.Address);

TextBlockResults.Text = _output;

First we need to change the return type from a single instance of GeoCodeResult to an IEnumerable collection of GeoCodeResult

 IEnumerable<GeoCodeResult> _geocodes =   from _result in _xdoc...

We also need to remove the FirstOrDefault from our LINQ to XML query. Finally we need to modify our simple string output mechanism to recognize we are in a collection. Revised code is as follows with notable changes in bold.

 System.Xml.Linq.XDocument _xdoc = System.Xml.Linq.XDocument.Parse(results);
IEnumerable<GeoCodeResult> _geocodes =  from _result in _xdoc.Descendants(XName.Get("Result", "urn:yahoo:maps"))
    select new GeoCodeResult
    {
        Latitude = _result.Element(XName.Get("Latitude", "urn:yahoo:maps")).Value,
        Longitude = _result.Element(XName.Get("Longitude", "urn:yahoo:maps")).Value,
        Address = _result.Element(XName.Get("Address", "urn:yahoo:maps")).Value,
        City = _result.Element(XName.Get("City", "urn:yahoo:maps")).Value,
        State = _result.Element(XName.Get("State", "urn:yahoo:maps")).Value,
        Zip = _result.Element(XName.Get("Zip", "urn:yahoo:maps")).Value,
        Country = _result.Element(XName.Get("Country", "urn:yahoo:maps")).Value
    };

System.Text.StringBuilder _sb = new System.Text.StringBuilder(2048);
foreach (GeoCodeResult _geocode in _geocodes)  {  _sb.AppendFormat("{2} {3} : lat={0}, lon={1}\n", _geocode.Latitude, _geocode.Longitude, _geocode.City, _geocode.State );  }

TextBlockResults.Text = _sb.ToString();

Run the project, and we see a list of all the matches for "Covington" with the center point latitude and logitudes for each. Screenshot below. Nice. And useful, even better!

wp7services-multipleresults
YHOO GeoCode results for Covington

Source Code - yes, this is a bit much to type in. The source code for the Windows Phone web services samples as well as some other wp7 snips is available in my Windows Phone 7 Tacklebox sample, available here - https://www.devfish.net/downloads/files/WP7TackleBox.zip  . Any feedback let me know. The Tacklebox is an exercise in continual modification.

YHOO thoughts Why'd I use Yahoo APIs? They've got a some pretty cool API at their GeoCoding site - https://developer.yahoo.com/maps/rest/V1/geocode.html . If I used all Microsoft APIs to test against, well, nice for us, but not so real world. Note this is a deprecated api. You should get your own yahoo key, the one we use here is the sample one on the Yahoo website. Also note calls to this API are limited to 5000 hits per day. Before using in production read the Yahoo licensing info. Details on all that and more are at https://developer.yahoo.com/maps/rest/V1/geocode.html  .

Consuming Windows Phone Services Series
Part 1 - Web Client Basics - https://blogs.msdn.com/b/devfish/archive/2011/04/06/webclient-windows-phone-services-consumption-part-1.aspx
Part 2 - HttpWebRequest Fundamentals - https://blogs.msdn.com/b/devfish/archive/2011/04/07/httpwebrequest-fundamentals-windows-phone-services-consumption-part-2.aspx
Part 3 - Parsing REST based XML Data - Part A - Single Result - https://blogs.msdn.com/b/devfish/archive/2011/05/05/consuming-geocode-yhoo-api-via-rest-windows-phone-services-consumption-part-3.aspx
Part 4 - Parsing REST based XML Data - Part B - Multiple Results - https://blogs.msdn.com/b/devfish/archive/2011/05/10/consuming-geocode-yhoo-api-via-rest-dealing-with-multiple-results-part-4.aspx