I am new to Ruby, so I am still learning to do things in a "ruby-ish"
way. In my job I do a fair amount of mapping, and thus I have a need to
get latitude/longitude coordinates for addresses that I have. I have
written the program below and though I might share it in case anyone
else needs something similar.
Please feel free to offer suggestions for improvement. As I said I am
new to Ruby! I have commented the code, so you can build it with rdoc
and get at least a very rudimentary documentation.
Code:
# This program is an implementation of geocoding services offered by
{Yahoo! Maps Web
Services}[
http://developer.yahoo.com/maps/rest/V1/geocode.html].
#
# * Author: Jeff Grice
# * Date: 11.16.2006
# ---
require 'net/http'
require 'rexml/document'
# The YGeocoder class implements Yahoo! geocoding services. Given an
address, city, state, and zip, the service returns
# an XML response with the result set containing the latitude,
longitude, precision, an any warnings associated with the result.
class YGeocoder
attr_reader :latitude, :longitude, :precision, :warning, :city, :state,
:zip, :address
YURL = '
http://api.local.yahoo.com/MapsService/V1/geocode'
APPID = 'your_app_id' # use your yahoo! app id here!
@@count = 0
# Class attribute to return the total number of geocodes found.
def YGeocoder.count
@@count
end
# Constructor which takes strings for address, city, state, and zip
def initialize(address, city, state, zip)
@addressArgs = {
'appid' => APPID,
'city' => city,
'state' => state,
'street' => address,
'zip' => zip
}
@qs = buildQueryString(@addressArgs)
@warning = ""
geocode()
end
# Method to write results to a semi-colon delimited string for easy
output to text file
def to_s
";#{@address};#{@city};#{@state};#{@zip};#{@latitude};#{@longitude};#{@p\
recision};#{@warning}"
end
# Method to return array of address, city, state, zip, latitude,
longitude, precision, warning
def to_a
[@address, @city, @state, @zip, @latitude, @longitude, @precision,
@warning]
end
private
# Private Method to return query string based on hash variable
supplied. The address information is passed to this method
# from the class constructor
def buildQueryString(hash)
qs = ""
hash.each {|key, value| qs += "&" + URI.encode(key) + "=" +
URI.encode(value) }
qs[0] = "?"
return YURL + qs
end
# Private Method that does the bulk of the work of the class. Sends
request to Yahoo geocoding service, then parses
# the result and stores them to instance variables that are exposed as
attributes.
def geocode
begin
data = Net::HTTP.get_response(URI.parse(@qs)).body
doc = REXML::Document.new(data)
#only get the data if there is only 1 result
if doc.elements.size == 1 then
doc.elements.each('ResultSet/Result') do |result|
result.attributes.each_attribute do |attr|
case attr.name
when "precision": @precision = attr.value
when "warning": @warning = attr.value
end
end
end
#get the element text and assign to instance variables
@latitude = getElementText(doc, 'ResultSet/Result/Latitude')
@longitude = getElementText(doc, 'ResultSet/Result/Longitude')
@city = getElementText(doc, 'ResultSet/Result/City')
@state = getElementText(doc, 'ResultSet/Result/State')
@zip = getElementText(doc, 'ResultSet/Result/Zip')
@address = getElementText(doc, 'ResultSet/Result/Address')
@@count += 1
else
@warning = "More than one location returned"
end
rescue
puts "Connection Error"
@warning = "Site cannot be geocoded"
end
end
# Method to help extract text out of the XML elements returned from
Yahoo web service.
def getElementText(doc, xpath)
s = ""
doc.elements.each(xpath) do |ele|
s = ele.text.strip
end
return s
end
end