Geocoding with Ruby and Google Maps API

By wgoulet

As part of my trip planning for my upcoming vacation to Spain, I found myself trying to solve the problem of figuring out where smaller villages that would have bed and breakfast style inns would be located in proximity to AVE train stops. I found good maps that show where the train stops are, but these maps aren’t very helpful in figuring out which nearby towns have bed and breakfast inns. In addition, building a list of towns with bed and breakfast inns is pretty hard because you find yourself just doing lots of searches and finding responses in forum posts, travel websites, and sometimes word of mouth.

But in the end, I did manage to collect a pretty decent sized list of about 44 towns with B&Bs. In addition, I have a list of about 15 towns with train stops along the route I want to take. How do I draw a map that shows these two elements together? Google maps seemed to be the most obvious solution, but it doesn’t have an interface to let you specify large lists of addresses. It does however have an ‘import’ feature that lets you upload maps that conform to several standards. So my solution was to basically format my 2 lists into files that could be imported to Google Maps.

In order to do this, I first had to get the addresses of the towns. This was accomplished via the Geocoding interface of google maps (see here for Google’s description of the interface). Once I obtained the addresses (in lat/long coordinates), I then constructed a KML file (which is a structured XML file, see Google’s KML reference here). The resulting KML files can then be uploaded to Google maps so that I can see where the addresses are in proximity to each other.

I wrote a rather simple Ruby script to accomplish this, using the built-in net/http class to handle the geocoding functionality and XML::Builder to write the resulting KML file. I found that the Geocoding interface is really easy to use; the only potential gotcha is that you need to get a google API key in order to use the interface. In my case, I wasn’t interested in hosting the script on a web page I own but rather just wanted to run the script locally on my computer. Google accomodates this by providing a ‘localhost’ option for specifying a domain that your API key is locked to.

So here’s my script (with my API key removed, put your own one in to run this). I will probably play around with this a little more in the future to make the placemarks look different etc.

#!/usr/bin/ruby -w
require 'net/http'
require 'uri'
require 'rubygems'
require 'builder'

addrs = Array.new
placemarkbase = ARGV[1]
placemarks = Hash.new

xml = Builder::XmlMarkup.new(:target => $stdout, :indent => 4)

# Pretty simple; use the geocoder API to fetch the coordinate data for a set of
# addresses. The coordinate data and address names are then mashed into a XML
# file that conforms to the KML specification.

geocoder = "http://maps.google.com/maps/geo?q="
output = "&output=csv"
apikey = "&key=123abc"

File.open(ARGV[0]) do |file|
	file.each_line { |line| line.chomp!; addrs.push(line) }
end

counter = 0
addrs.each do |addr|
	request = geocoder + addr + output + apikey
	# Insert a delay so that I don't overload the geocoder
	sleep 2.0
    # Since the city/country name goes directly into the URL request
    # it needs to be cleaned up.
	url = URI.escape(request)
	resp = Net::HTTP.get_response(URI.parse(url))
	# I'm using the csv output from google maps API
	# so I need to pull out only the lat/long coords from the
	# API.
	fields = resp.body.split(',')
	placemarks[placemarkbase + counter.to_s] = [addr,fields[3],fields[2]]
    counter += 1
end

kml = { 'xmlns' => 'http://earth.google.com/kml/2.2'}
xml.instruct! :x ml, :version => "1.1", :encoding => "US-ASCII"
kml.each do |key,value|
    xml.kml(:xmlns => value) do
        xml.Document do
            placemarks.each do |key, array|
                xml.Placemark do
                 xml.name(array[0])
                 xml.address(array[0])
                 xml.description(placemarkbase)
                 xml.Point do
                     str = array[1].to_s + "," + array[2].to_s + "," + 0.to_s
                     xml.coordinates(str)
                 end
                end
            end
        end
    end
end

Tags: , ,

One Response to “Geocoding with Ruby and Google Maps API”

  1. Stark reminder of state of US economy « Active Minded Says:

    [...] and map them in Google Maps (using the handy geocoding Ruby script I wrote about in an earlier post here), you’ll see that most of these cities are clumped right around the San Jose area. Check out [...]

Leave a Reply