C# and Google Geocoding Web Service v3

Posted on |

Need to geocode addresses using the v3 Google Geocoding Web Service? There are some good reasons to choose the new v3 edition — most importantly, you don’t need an API key. You could use geocoding.net which — at the time of writing —  has some support for v3. I decided to hack up my own wrapper though, and using Windows Communication Foundation, it turned out to be really simple! Note that if you need more of the attributes returned by the Web Service, you should add them to the DataContract classes.

using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Net;
using System.Web;

.
.
.

private static GeoResponse CallGeoWS(string address)
{
	string url = string.Format(
		"http://maps.google.com/maps/api/geocode/json?address={0}&region=dk&sensor=false",
		HttpUtility.UrlEncode(address)
		);
	var request = (HttpWebRequest)HttpWebRequest.Create(url);
	request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
	request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
	DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(GeoResponse));
	var res = (GeoResponse)serializer.ReadObject(request.GetResponse().GetResponseStream());
	return res;
}

[DataContract]
class GeoResponse
{
	[DataMember(Name="status")]
	public string Status { get; set; }
	[DataMember(Name="results")]
	public CResult[] Results { get; set; }

	[DataContract]
	public class CResult
	{
		[DataMember(Name="geometry")]
		public CGeometry Geometry { get; set; }

		[DataContract]
		public class CGeometry
		{
			[DataMember(Name="location")]
			public CLocation Location { get; set; }

			[DataContract]
			public class CLocation
			{
				[DataMember(Name="lat")]
				public double Lat { get; set; }
				[DataMember(Name = "lng")]
				public double Lng { get; set; }
			}
		}
	}
}

If you need to geocode a lot of addresses, you need to manage your request rate. Google will help you throttle requests by returning OVER_QUERY_LIMIT statuses if you are going too fast. I use the method below to manage this. It’s decidedly unelegant, please post a reply if you come up with something better.

private static int sleepinterval = 200;

private static GeoResponse CallWSCount(string address, int badtries)
{
	Thread.Sleep(sleepinterval);
	GeoResponse res;
	try
	{
		res = CallGeoWS(address);
	}
	catch (Exception e)
	{
		Console.WriteLine("Caught exception: " + e);
		res = null;
	}
	if (res == null || res.Status == "OVER_QUERY_LIMIT")
	{
		// we're hitting Google too fast, increase interval
		sleepinterval = Math.Min(sleepinterval + ++badtries * 1000, 60000);

		Console.WriteLine("Interval:" + sleepinterval + "                           \r");
		return CallWSCount(address, badtries);
	}
	else
	{
		// no throttling, go a little bit faster
		if (sleepinterval > 10000)
			sleepinterval = 200;
		else
			sleepinterval = Math.Max(sleepinterval / 2, 50);

		Console.WriteLine("Interval:" + sleepinterval);
		return res;
	}
}

9 thoughts on “C# and Google Geocoding Web Service v3”

  • Very nice code. I needed the address information also and I will be including it for what it is worth. Thanks for the insight into how to make this happen.

    [DataContract]
    public class GeoResponse
    {
    [DataMember(Name = “status”)]
    public string Status { get; set; }
    [DataMember(Name = “results”)]
    public CResult[] Results { get; set; }

    [DataContract]
    public class CResult
    {

    [DataMember(Name = “formatted_address”)]
    public string formatted_address { get; set; }
    [DataMember(Name = “address_components”)]
    public GeocodeAddressComponent[] AddressComponents { get; set; }

    [DataContract]
    public class GeocodeAddressComponent
    {
    [DataMember(Name = “long_name”)]
    public string LongName { get; set; }
    [DataMember(Name = “short_name”)]
    public string ShortName { get; set; }
    [DataMember(Name = “types”)]
    public string[] Type { get; set; }
    }

    [DataMember(Name = “geometry”)]
    public CGeometry Geometry { get; set; }

    [DataContract]
    public class CGeometry
    {
    [DataMember(Name = “location”)]
    public CLocation Location { get; set; }

    [DataContract]
    public class CLocation
    {
    [DataMember(Name = “lat”)]
    public double Latitude { get; set; }
    [DataMember(Name = “lng”)]
    public double Longitude { get; set; }

    }
    }
    }
    }

  • “most importantly, you don’t need an API key”

    Actually, moving away from the “key” model of v2 is not beneficially in some environments. The v3 api looks at the IP from which requests are coming. If you happen to share rackspace at an ISP with other applications that are doing the same thing, you may hit the “OVER_QUERY_LIMIT” before your application has made 2500 requests. Decidedly inconvenient and a very poor model in my opinion. The key was much better.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>