Archive

Archive for February, 2012

Translating an address to GPS coordinates with the Google Geocoding REST service

February 21, 2012 1 comment

For a hobby project, I recently needed to calculate distances between stores and find nearby stores. In order to implement the required functionality, I have used the Google Geocoding service to have GPS coordinates for all my database entries and then it was just a matter of applying the correct math.

The Google Geocoding service is a REST service offered free of charge by Google and no developer sign-up is required. It can translate an address string into GPS coordinates. Result data can be returned in XML or JSON format. The only limitation is that the free version can only geocode 2500 addresses per day.

More technical information can be found on the following web page: http://code.google.com/intl/en-US/apis/maps/documentation/geocoding.

In this blog post, I want to share the tinny wrapper I have created to simplify calling this web service.

I first created a generic interface for the Geocoder.

public interface IGeocoder
{
    Coordinates Geocode(string address);
}

I also created a small data structure for my coordinates.

public class Coordinates
{
    public Coordinates(double latitude, double longitude)
    {
        Latitude = latitude;
        Longitude = longitude;
    }

    public double Latitude { get; private set; }

    public double Longitude { get; private set; }
}

Then I wrote the following implementation that uses the Google web service. I use the XML format for geocoded addresses because XML is easier to parse in .Net applications without requiring 3rd party libraries. If you want to use this service from a WP7 application it would be better to use the more compact JSON format.

public class GoogleGeocoder : IGeocoder
{
    private const string ServiceUri = "http://maps.googleapis.com/maps/api/geocode/xml?address={0}&region=be&sensor=false";

    public Coordinates Geocode(string address)
    {
        if (string.IsNullOrEmpty(address))
            throw new ArgumentNullException("address");

        string requestUriString = string.Format(ServiceUri, Uri.EscapeDataString(address));

        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestUriString);

        try
        {
            WebResponse response = request.GetResponse();

            XDocument xdoc = XDocument.Load(response.GetResponseStream());

            // Verify the GeocodeResponse status
            string status = xdoc.Element("GeocodeResponse").Element("status").Value;
            ValidateGeocodeResponseStatus(status, address);

            XElement locationElement = xdoc.Element("GeocodeResponse").Element("result").Element("geometry").Element("location");
            double latitude = (double)locationElement.Element("lat");
            double longitude = (double)locationElement.Element("lng");

            return new Coordinates(latitude, longitude);
        }
        catch (WebException ex)
        {
            switch(ex.Status)
            {
                case WebExceptionStatus.NameResolutionFailure:
                    throw new ServiceOfflineException("The Google Maps geocoding service appears to be offline.", ex);
                default:
                    throw;
            }
        }

    }

    private void ValidateGeocodeResponseStatus(string status, string address)
    {
        switch (status)
        {
            case "ZERO_RESULTS":
                string message = string.Format("No coordinates found for address \"{0}\".", address);
                throw new UnknownAddressException(message);
            case "OVER_QUERY_LIMIT":
                throw new OverQueryLimitException();
            case "OK":
                break;
            default:
                throw new Exception("Unkown status code: " + status + ".");
        }
    }
}
Categories: C#, REST