require 'open-uri' require 'rexml/document' ## # Library for looking up coordinates with Google's Geocoding API. # # http://www.google.com/apis/maps/documentation/#Geocoding_HTTP_Request class GoogleGeocode ## # Base error class class Error < RuntimeError; end ## # Raised when you try to locate an invalid address. class AddressError < Error; end ## # Raised when you use an invalid key. class KeyError < Error; end ## # Location struct Location = Struct.new :address, :latitude, :longitude ## # Creates a new GoogleGeocode that will use Google Maps API key +key+. You # can sign up for an API key here: # # http://www.google.com/apis/maps/signup.html def initialize(key) @key = key @url = URI.parse 'http://maps.google.com/maps/geo' end ## # Locates +address+ returning a Location struct. def locate(address) get :q => address end private def parse_response(xml) l = Location.new l.address = xml.elements['/kml/Response/Placemark/address'].text coordinates = xml.elements['/kml/Response/Placemark/Point/coordinates'].text l.longitude, l.latitude, = coordinates.split(',').map { |v| v.to_f } return l end def check_error(obj) obj = REXML::Document.new obj.read unless REXML::Document === obj status = obj.elements['/kml/Response/Status/code'].text.to_i case status when 200 # ignore, ok when 602 raise AddressError, 'invalid address' when 610 raise KeyError, 'invalid key' else raise Error, "unknown error #{status}" end end def get(params) url = make_url params url.open do |xml| res = REXML::Document.new xml.read check_error res return parse_response(res) end rescue OpenURI::HTTPError => e check_error e.io raise end def make_url(params) params[:key] = @key params[:output] = 'xml' escaped_params = params.sort_by { |k,v| k.to_s }.map do |k,v| "#{URI.escape k.to_s}=#{URI.escape v.to_s}" end url = @url.dup url.query = escaped_params.join '&' return url end end ## # A Location contains the following fields: # # +latitude+:: Latitude of the location # +longitude+:: Longitude of the location # +address+:: Street address of the result. class GoogleGeocode::Location ## # The coordinates for this location. def coordinates [latitude, longitude] end end