Sergio Schirmer
Sergio Schirmer

Reputation: 301

Xamarin Google Maps API

I'm using the code below to get the city name of a given Latitude and Longitude.

But the code stops when this line is running:

var responseElement = XElement.Parse(response);

It stops responding and doesn't throw any exception.

I'm guessing it may be a problem with the asynchronous call.

Can anyone help me with this, or suggest a similar code that use the iOS SDK?

private async Task<string> ReturnCity(string lat, string lon)
{
    string city = "None";
    string baseUri = string.Format("http://maps.googleapis.com/maps/api/geocode/xml?latlng={0},{1}&sensor=false", lat, lon);
    HttpClient client = new HttpClient();
    var response = await client.GetStringAsync(baseUri);
    var responseElement = XElement.Parse(response);
    IEnumerable<XElement>statusElement = from st in responseElement.Elements("status") select st;
    if (statusElement.FirstOrDefault() != null)
    {
        string status = statusElement.FirstOrDefault().Value;
        if (status.ToLower() == "ok")
        {
            IEnumerable<XElement> resultElement = from rs in responseElement.Elements("result") select rs;
            if (resultElement.FirstOrDefault() != null)
            {
                IEnumerable<XElement> addressElement = from ad in resultElement.FirstOrDefault().Elements("address_component") select ad;
                foreach (XElement element in addressElement)
                {
                    IEnumerable<XElement> typeElement = from te in element.Elements("type") select te;
                    string type = typeElement.FirstOrDefault().Value;
                    if(type=="locality")
                    {
                        IEnumerable<XElement> cityElement = from ln in element.Elements("long_name") select ln;
                        city = cityElement.FirstOrDefault().Value;
                        return city;
                        break;
                    }
                }
            }
        }
    }
    return city;
}

I went through the debugger and found the value of response. It looks like a valid XML. Any thoughts?

<?xml version="1.0" encoding="UTF-8"?>
<GeocodeResponse>
 <status>OK</status>
 <result>
  <type>airport</type>
  <type>transit_station</type>
  <type>establishment</type>
  <formatted_address>Lisbon Portela Airport (LIS), Alameda das Comunidades Portuguesas, 1700-111 Lisboa, Portugal</formatted_address>
  <address_component>
   <long_name>Lisbon Portela Airport</long_name>
   <short_name>Lisbon Portela Airport</short_name>
  </address_component>
  <address_component>
   <long_name>Alameda das Comunidades Portuguesas</long_name>
   <short_name>Alameda das Comunidades Portuguesas</short_name>
   <type>route</type>
  </address_component>
  <address_component>
   <long_name>Lisboa</long_name>
   <short_name>Lisboa</short_name>
   <type>locality</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisboa</long_name>
   <short_name>Lisboa</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>1700-111</long_name>
   <short_name>1700-111</short_name>
   <type>postal_code</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7755936</lat>
    <lng>-9.1353667</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>38.7623900</lat>
     <lng>-9.1475505</lng>
    </southwest>
    <northeast>
     <lat>38.7994779</lat>
     <lng>-9.1236185</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.7623900</lat>
     <lng>-9.1475505</lng>
    </southwest>
    <northeast>
     <lat>38.7994779</lat>
     <lng>-9.1236185</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJgwHZFkYyGQ0RRm0DWD6lZgo</place_id>
 </result>
 <result>
  <type>route</type>
  <formatted_address>Lisbon Portela Airport (LIS), Rua E, 1700-111 Lisboa, Portugal</formatted_address>
  <address_component>
   <long_name>Rua E</long_name>
   <short_name>R. E</short_name>
   <type>route</type>
  </address_component>
  <address_component>
   <long_name>Lisbon Portela Airport</long_name>
   <short_name>Lisbon Portela Airport</short_name>
  </address_component>
  <address_component>
   <long_name>Lisboa</long_name>
   <short_name>Lisboa</short_name>
   <type>locality</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisboa</long_name>
   <short_name>Lisboa</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>1700-111</long_name>
   <short_name>1700-111</short_name>
   <type>postal_code</type>
  </address_component>
  <address_component>
   <long_name>1700</long_name>
   <short_name>1700</short_name>
   <type>postal_code_prefix</type>
   <type>postal_code</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7757984</lat>
    <lng>-9.1304833</lng>
   </location>
   <location_type>GEOMETRIC_CENTER</location_type>
   <viewport>
    <southwest>
     <lat>38.7744765</lat>
     <lng>-9.1317496</lng>
    </southwest>
    <northeast>
     <lat>38.7771745</lat>
     <lng>-9.1290516</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.7748799</lat>
     <lng>-9.1307609</lng>
    </southwest>
    <northeast>
     <lat>38.7767711</lat>
     <lng>-9.1300403</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJjdzMckIyGQ0RvIXYZCCI82k</place_id>
 </result>
 <result>
  <type>locality</type>
  <type>political</type>
  <formatted_address>Lisbon, Portugal</formatted_address>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>locality</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7222524</lat>
    <lng>-9.1393366</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>38.6913994</lat>
     <lng>-9.2298356</lng>
    </southwest>
    <northeast>
     <lat>38.7958538</lat>
     <lng>-9.0905718</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.6913994</lat>
     <lng>-9.2298356</lng>
    </southwest>
    <northeast>
     <lat>38.7958538</lat>
     <lng>-9.0905718</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJO_PkYRozGQ0R0DaQ5L3rAAQ</place_id>
 </result>
 <result>
  <type>postal_code</type>
  <formatted_address>1700-111 Lisbon, Portugal</formatted_address>
  <address_component>
   <long_name>1700-111</long_name>
   <short_name>1700-111</short_name>
   <type>postal_code</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>locality</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Olivais</long_name>
   <short_name>Olivais</short_name>
   <type>administrative_area_level_3</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_2</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7661892</lat>
    <lng>-9.1291486</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>38.7544060</lat>
     <lng>-9.1483947</lng>
    </southwest>
    <northeast>
     <lat>38.7870843</lat>
     <lng>-9.1239064</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.7544060</lat>
     <lng>-9.1483947</lng>
    </southwest>
    <northeast>
     <lat>38.7870843</lat>
     <lng>-9.1239064</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJS-qk_0UyGQ0RN3v7SZnU9D8</place_id>
 </result>
 <result>
  <type>postal_code_prefix</type>
  <type>postal_code</type>
  <formatted_address>1700, Portugal</formatted_address>
  <address_component>
   <long_name>1700</long_name>
   <short_name>1700</short_name>
   <type>postal_code_prefix</type>
   <type>postal_code</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7600325</lat>
    <lng>-9.1400807</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>38.7448641</lat>
     <lng>-9.1567550</lng>
    </southwest>
    <northeast>
     <lat>38.7870843</lat>
     <lng>-9.1239064</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.7448641</lat>
     <lng>-9.1567550</lng>
    </southwest>
    <northeast>
     <lat>38.7870843</lat>
     <lng>-9.1239064</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJdVtq_1kyGQ0RYKU7DsHrABw</place_id>
 </result>
 <result>
  <type>administrative_area_level_3</type>
  <type>political</type>
  <formatted_address>Olivais, Portugal</formatted_address>
  <address_component>
   <long_name>Olivais</long_name>
   <short_name>Olivais</short_name>
   <type>administrative_area_level_3</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_2</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7721510</lat>
    <lng>-9.1179465</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>38.7502229</lat>
     <lng>-9.1483924</lng>
    </southwest>
    <northeast>
     <lat>38.7870963</lat>
     <lng>-9.0905718</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.7502229</lat>
     <lng>-9.1483924</lng>
    </southwest>
    <northeast>
     <lat>38.7870963</lat>
     <lng>-9.0905718</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJccbEUz4yGQ0RsEKQ5L3rAAU</place_id>
 </result>
 <result>
  <type>administrative_area_level_2</type>
  <type>political</type>
  <formatted_address>Lisbon, Portugal</formatted_address>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_2</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7222986</lat>
    <lng>-9.1393188</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>38.6913994</lat>
     <lng>-9.2298356</lng>
    </southwest>
    <northeast>
     <lat>38.7958538</lat>
     <lng>-9.0905718</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.6913994</lat>
     <lng>-9.2298356</lng>
    </southwest>
    <northeast>
     <lat>38.7958538</lat>
     <lng>-9.0905718</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJMabWPRAzGQ0RRFqNEqmj6AI</place_id>
 </result>
 <result>
  <type>administrative_area_level_1</type>
  <type>political</type>
  <formatted_address>Lisbon, Portugal</formatted_address>
  <address_component>
   <long_name>Lisbon</long_name>
   <short_name>Lisbon</short_name>
   <type>administrative_area_level_1</type>
   <type>political</type>
  </address_component>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <geometry>
   <location>
    <lat>38.7223263</lat>
    <lng>-9.1392714</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>38.6731604</lat>
     <lng>-9.5005266</lng>
    </southwest>
    <northeast>
     <lat>39.3177286</lat>
     <lng>-8.7819208</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>38.6731604</lat>
     <lng>-9.5005266</lng>
    </southwest>
    <northeast>
     <lat>39.3177286</lat>
     <lng>-8.7819208</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJWxIr4ajVGA0RwCyQ5L3rAAM</place_id>
 </result>
 <result>
  <type>country</type>
  <type>political</type>
  <formatted_address>Portugal</formatted_address>
  <address_component>
   <long_name>Portugal</long_name>
   <short_name>PT</short_name>
   <type>country</type>
   <type>political</type>
  </address_component>
  <geometry>
   <location>
    <lat>39.3998720</lat>
    <lng>-8.2244540</lng>
   </location>
   <location_type>APPROXIMATE</location_type>
   <viewport>
    <southwest>
     <lat>36.9601772</lat>
     <lng>-9.5171107</lng>
    </southwest>
    <northeast>
     <lat>42.1542048</lat>
     <lng>-6.1902091</lng>
    </northeast>
   </viewport>
   <bounds>
    <southwest>
     <lat>32.4037400</lat>
     <lng>-31.2752439</lng>
    </southwest>
    <northeast>
     <lat>42.1542048</lat>
     <lng>-6.1902091</lng>
    </northeast>
   </bounds>
  </geometry>
  <place_id>ChIJ1SZCvy0kMgsRQfBOHAlLuCo</place_id>
 </result>
</GeocodeResponse>

Upvotes: 2

Views: 408

Answers (2)

Sergio Schirmer
Sergio Schirmer

Reputation: 301

The problem was occurring at

string type = typeElement.FirstOrDefault ().Value;

So I had to test if typeElement.FirstOrDefault () != null

As below:

    private async Task<string> ReturnCity(string lat, string lon)
    {
        string city = "None";
        string baseUri = string.Format ("http://maps.googleapis.com/maps/api/geocode/xml?latlng={0},{1}&sensor=false", lat, lon);
        HttpClient client = new HttpClient ();
        string response = await client.GetStringAsync (baseUri);
        XElement responseElement = XElement.Parse (response);
        IEnumerable<XElement> statusElement = from st in responseElement.Elements ("status")
                                               select st;
        if (statusElement.FirstOrDefault () != null) {
            string status = statusElement.FirstOrDefault ().Value;
            if (status.ToLower () == "ok") {
                IEnumerable<XElement> resultElement = from rs in responseElement.Elements ("result")
                                                      select rs;
                if (resultElement.FirstOrDefault () != null) {
                    IEnumerable<XElement> addressElement = from ad in resultElement.FirstOrDefault ().Elements ("address_component")
                                                           select ad;
                    foreach (XElement element in addressElement) {
                        IEnumerable<XElement> typeElement = from te in element.Elements ("type")
                                                            select te;
                        if (typeElement.FirstOrDefault () != null) {
                            string type = typeElement.FirstOrDefault ().Value;
                            if (type == "locality") {
                                IEnumerable<XElement> cityElement = from ln in element.Elements ("long_name")
                                                                   select ln;
                                city = cityElement.FirstOrDefault ().Value;
                                return city;
                            }
                        }
                    }
                }
            }
        }

        return city;
    }

Upvotes: 0

Iain Smith
Iain Smith

Reputation: 9703

I implemented something similar using the same reverse-geo service that google provides but the JSON version. From my experience you cant assume any of the data you need, e.g. city, will be there. Also you might need to check for zero results. Have a look below on how I implemented it:

public async Task<LocationAddressModel> GetLocationAddressAsync (LocationAddressRequest locationAddressRequest)
    {
        Task<IRestResponse> task = GetAddressForLocation (locationAddressRequest);
        IRestResponse response = await task;
        var locationAddress = new LocationAddressModel ();
        if (response != null) {
            RootObject locationAddresses = JsonConvert.DeserializeObject<RootObject> (response.Content);
            if (locationAddresses.status.Equals (Constants.ZERO_RESULTS)) {
                // Zero results for that location, setting location to Unknown.
                locationAddress.FormattedAddress = "UnknownLocation".Local();
                locationAddress.City = "CityNotFound".Local ();
                locationAddress.County = "CountyNotFound".Local ();
                locationAddress.FirstLineOfAddress = "AddressNotFound".Local ();
                locationAddress.Postcode = "PostcodeNotFound".Local ();
            } else {
                try {
                    Result routeAddress = locationAddresses.results.FirstOrDefault (x => x.types.Contains (Constants.ROUTE));
                    if (routeAddress != null) 
                    {
                        locationAddress.FormattedAddress = routeAddress.formatted_address;

                        var postcode = routeAddress.address_components.FirstOrDefault(x => x.types.Contains (Constants.POSTCODE));
                        locationAddress.Postcode = postcode != null ? postcode.long_name : "PostcodeNotFound".Local ();

                        var firstLineOfAddress = routeAddress.address_components.FirstOrDefault(x => x.types.Contains (Constants.ROUTE));
                        locationAddress.FirstLineOfAddress = firstLineOfAddress != null ? firstLineOfAddress.long_name : "AddressNotFound".Local ();

                        var city = routeAddress.address_components.FirstOrDefault(x => x.types.Contains (Constants.CITY));
                        locationAddress.City = city != null ? city.long_name : "CityNotFound".Local ();

                    } 
                    else 
                    {
                        // If the location doesnt have a route address just return the first result formatted address
                        locationAddress.FormattedAddress = locationAddresses.results.FirstOrDefault ().formatted_address;
                    }

                    // Remove any numbers at the start, to save users from knowing how inaccurate GPS can be.
                    locationAddress.FormattedAddress = Regex.Replace (locationAddress.FormattedAddress, "^\\d+\\s", string.Empty);
                } 
                catch (Exception e)
                {
                    Debug.WriteLine ("Exception in reverse geolocation data");
                    // If we get some crazy data also set it to unknown
                    locationAddress.FormattedAddress = "UnknownLocation".Local();
                }
            }
        }
        return locationAddress;
    }

private async Task<IRestResponse> GetAddressForLocation (LocationAddressRequest locationAddressRequest) {

            IRestRequest restRequest = _restSharpGoogleMapsSingletonBase.Factory.Create(Constants.GET_LOCATION_ADDRESS, Method.GET);

            restRequest.AddUrlSegment ("lat", locationAddressRequest.Lat);
            restRequest.AddUrlSegment ("lng", locationAddressRequest.Lng);

            try
            {
                var result = await _helper.ExecuteTaskAsync(restRequest);

                if (result.StatusCode == (int)HttpStatusCode.OK)
                {
                    Debug.WriteLine("Rest Request Succeeded in Google Maps GeoCode API");
                    return result;
                }
                else
                {
                    Debug.WriteLine("Rest Request Failed in Google Maps GeoCode API: Wrong status code received");
                    return null;
                }
            }
            catch(WebException webException)
            {
                // Windows Store application profile only supports a limited set of WebExceptionStatus. 
                // So the PCL can only support that set, Check for the Timeout here.
                if(webException.Status == (WebExceptionStatus)14)
                {
                    Debug.WriteLine("Timed out in Google Maps GeoCode API");
                }
                return null;
            }
            catch (Exception exception)
            {
                return null;
            }

        }

Constants I am using:

namespace GoogleMaps
{
    public static class Constants
    {
        public static string GOOGLEMAPSURL = "http://maps.googleapis.com/";
        public static string GET_LOCATION_ADDRESS = "/maps/api/geocode/json?latlng={lat},{lng}";

        public static string ROUTE = "route";
        public static string FORMATTED_ADDRESS = "formatted_address";
        public static string POSTCODE = "postal_code";
        public static string CITY = "locality";

        public static string ZERO_RESULTS = "ZERO_RESULTS";
    }
}

Hope this helps.

Upvotes: 1

Related Questions