steveh-geokit 1.6.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,16 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- geokit (1.5.0)
4
+ steveh-geokit (1.6.0)
5
+ activesupport (~> 3)
5
6
 
6
7
  GEM
7
8
  remote: http://rubygems.org/
8
9
  specs:
10
+ activesupport (3.0.4)
9
11
  mocha (0.9.12)
10
12
 
11
13
  PLATFORMS
12
14
  ruby
13
15
 
14
16
  DEPENDENCIES
15
- geokit!
16
- mocha
17
+ activesupport (~> 3)
18
+ mocha (>= 0.9)
19
+ steveh-geokit!
@@ -18,5 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_development_dependency("mocha", [">= 0"])
21
+ s.add_runtime_dependency("activesupport", ["~> 3"])
22
+
23
+ s.add_development_dependency("mocha", [">= 0.9"])
22
24
  end
@@ -28,11 +28,19 @@ require 'rexml/document'
28
28
  require 'yaml'
29
29
  require 'timeout'
30
30
  require 'logger'
31
+ require 'active_support/core_ext/hash'
32
+ require 'active_support/core_ext/object/conversions'
33
+ require 'openssl'
34
+ require 'base64'
35
+ require 'json'
31
36
 
32
37
  require 'geokit/too_many_queries_error'
33
38
  require 'geokit/inflector'
34
39
  require 'geokit/geocoders'
35
40
  require 'geokit/mappable'
41
+ require 'geokit/lat_lng'
42
+ require 'geokit/geo_loc'
43
+ require 'geokit/bounds'
36
44
  require 'geokit/geocoders/geocode_error'
37
45
  require 'geokit/geocoders/geocoder'
38
46
 
@@ -40,6 +48,7 @@ require 'geokit/geocoders/ca_geocoder'
40
48
  require 'geokit/geocoders/geo_plugin_geocoder'
41
49
  require 'geokit/geocoders/geonames_geocoder'
42
50
  require 'geokit/geocoders/google_geocoder'
51
+ require 'geokit/geocoders/google_premier_geocoder'
43
52
  require 'geokit/geocoders/ip_geocoder'
44
53
  require 'geokit/geocoders/multi_geocoder'
45
54
  require 'geokit/geocoders/us_geocoder'
@@ -0,0 +1,95 @@
1
+ module Geokit
2
+ # Bounds represents a rectangular bounds, defined by the SW and NE corners
3
+ class Bounds
4
+ # sw and ne are LatLng objects
5
+ attr_accessor :sw, :ne
6
+
7
+ # provide sw and ne to instantiate a new Bounds instance
8
+ def initialize(sw,ne)
9
+ raise ArgumentError if !(sw.is_a?(Geokit::LatLng) && ne.is_a?(Geokit::LatLng))
10
+ @sw,@ne=sw,ne
11
+ end
12
+
13
+ #returns the a single point which is the center of the rectangular bounds
14
+ def center
15
+ @sw.midpoint_to(@ne)
16
+ end
17
+
18
+ # a simple string representation:sw,ne
19
+ def to_s
20
+ "#{@sw.to_s},#{@ne.to_s}"
21
+ end
22
+
23
+ # a two-element array of two-element arrays: sw,ne
24
+ def to_a
25
+ [@sw.to_a, @ne.to_a]
26
+ end
27
+
28
+ # Returns true if the bounds contain the passed point.
29
+ # allows for bounds which cross the meridian
30
+ def contains?(point)
31
+ point=Geokit::LatLng.normalize(point)
32
+ res = point.lat > @sw.lat && point.lat < @ne.lat
33
+ if crosses_meridian?
34
+ res &= point.lng < @ne.lng || point.lng > @sw.lng
35
+ else
36
+ res &= point.lng < @ne.lng && point.lng > @sw.lng
37
+ end
38
+ res
39
+ end
40
+
41
+ # returns true if the bounds crosses the international dateline
42
+ def crosses_meridian?
43
+ @sw.lng > @ne.lng
44
+ end
45
+
46
+ # Returns true if the candidate object is logically equal. Logical equivalence
47
+ # is true if the lat and lng attributes are the same for both objects.
48
+ def ==(other)
49
+ other.is_a?(Bounds) ? self.sw == other.sw && self.ne == other.ne : false
50
+ end
51
+
52
+ # Equivalent to Google Maps API's .toSpan() method on GLatLng's.
53
+ #
54
+ # Returns a LatLng object, whose coordinates represent the size of a rectangle
55
+ # defined by these bounds.
56
+ def to_span
57
+ lat_span = (@ne.lat - @sw.lat).abs
58
+ lng_span = (crosses_meridian? ? 360 + @ne.lng - @sw.lng : @ne.lng - @sw.lng).abs
59
+ Geokit::LatLng.new(lat_span, lng_span)
60
+ end
61
+
62
+ class <<self
63
+
64
+ # returns an instance of bounds which completely encompases the given circle
65
+ def from_point_and_radius(point,radius,options={})
66
+ point=LatLng.normalize(point)
67
+ p0=point.endpoint(0,radius,options)
68
+ p90=point.endpoint(90,radius,options)
69
+ p180=point.endpoint(180,radius,options)
70
+ p270=point.endpoint(270,radius,options)
71
+ sw=Geokit::LatLng.new(p180.lat,p270.lng)
72
+ ne=Geokit::LatLng.new(p0.lat,p90.lng)
73
+ Geokit::Bounds.new(sw,ne)
74
+ end
75
+
76
+ # Takes two main combinations of arguments to create a bounds:
77
+ # point,point (this is the only one which takes two arguments
78
+ # [point,point]
79
+ # . . . where a point is anything LatLng#normalize can handle (which is quite a lot)
80
+ #
81
+ # NOTE: everything combination is assumed to pass points in the order sw, ne
82
+ def normalize (thing,other=nil)
83
+ # maybe this will be simple -- an actual bounds object is passed, and we can all go home
84
+ return thing if thing.is_a? Bounds
85
+
86
+ # no? OK, if there's no "other," the thing better be a two-element array
87
+ thing,other=thing if !other && thing.is_a?(Array) && thing.size==2
88
+
89
+ # Now that we're set with a thing and another thing, let LatLng do the heavy lifting.
90
+ # Exceptions may be thrown
91
+ Bounds.new(Geokit::LatLng.normalize(thing),Geokit::LatLng.normalize(other))
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,115 @@
1
+ module Geokit
2
+ # This class encapsulates the result of a geocoding call.
3
+ # It's primary purpose is to homogenize the results of multiple
4
+ # geocoding providers. It also provides some additional functionality, such as
5
+ # the "full address" method for geocoders that do not provide a
6
+ # full address in their results (for example, Yahoo), and the "is_us" method.
7
+ #
8
+ # Some geocoders can return multple results. Geoloc can capture multiple results through
9
+ # its "all" method.
10
+ #
11
+ # For the geocoder setting the results, it would look something like this:
12
+ # geo=GeoLoc.new(first_result)
13
+ # geo.all.push(second_result)
14
+ # geo.all.push(third_result)
15
+ #
16
+ # Then, for the user of the result:
17
+ #
18
+ # puts geo.full_address # just like usual
19
+ # puts geo.all.size => 3 # there's three results total
20
+ # puts geo.all.first # all is just an array or additional geolocs,
21
+ # so do what you want with it
22
+ class GeoLoc < LatLng
23
+
24
+ # Location attributes. Full address is a concatenation of all values. For example:
25
+ # 100 Spear St, San Francisco, CA, 94101, US
26
+ attr_accessor :street_address, :city, :state, :zip, :country_code, :country, :full_address, :all, :district, :province
27
+ # Attributes set upon return from geocoding. Success will be true for successful
28
+ # geocode lookups. The provider will be set to the name of the providing geocoder.
29
+ # Finally, precision is an indicator of the accuracy of the geocoding.
30
+ attr_accessor :success, :provider, :precision, :suggested_bounds
31
+ # Street number and street name are extracted from the street address attribute.
32
+ attr_reader :street_number, :street_name
33
+ # accuracy is set for Yahoo and Google geocoders, it is a numeric value of the
34
+ # precision. see http://code.google.com/apis/maps/documentation/geocoding/#GeocodingAccuracy
35
+ attr_accessor :accuracy
36
+
37
+ # Constructor expects a hash of symbols to correspond with attributes.
38
+ def initialize(h={})
39
+ @all = [self]
40
+
41
+ @street_address=h[:street_address]
42
+ @city=h[:city]
43
+ @state=h[:state]
44
+ @zip=h[:zip]
45
+ @country_code=h[:country_code]
46
+ @province = h[:province]
47
+ @success=false
48
+ @precision='unknown'
49
+ @full_address=nil
50
+ super(h[:lat],h[:lng])
51
+ end
52
+
53
+ # Returns true if geocoded to the United States.
54
+ def is_us?
55
+ country_code == 'US'
56
+ end
57
+
58
+ def success?
59
+ success == true
60
+ end
61
+
62
+ # full_address is provided by google but not by yahoo. It is intended that the google
63
+ # geocoding method will provide the full address, whereas for yahoo it will be derived
64
+ # from the parts of the address we do have.
65
+ def full_address
66
+ @full_address ? @full_address : to_geocodeable_s
67
+ end
68
+
69
+ # Extracts the street number from the street address if the street address
70
+ # has a value.
71
+ def street_number
72
+ street_address[/(\d*)/] if street_address
73
+ end
74
+
75
+ # Returns the street name portion of the street address.
76
+ def street_name
77
+ street_address[street_number.length, street_address.length].strip if street_address
78
+ end
79
+
80
+ # gives you all the important fields as key-value pairs
81
+ def hash
82
+ res={}
83
+ [:success,:lat,:lng,:country_code,:city,:state,:zip,:street_address,:province,:district,:provider,:full_address,:is_us?,:ll,:precision].each { |s| res[s] = self.send(s.to_s) }
84
+ res
85
+ end
86
+ alias to_hash hash
87
+
88
+ # Sets the city after capitalizing each word within the city name.
89
+ def city=(city)
90
+ @city = Geokit::Inflector::titleize(city) if city
91
+ end
92
+
93
+ # Sets the street address after capitalizing each word within the street address.
94
+ def street_address=(address)
95
+ @street_address = Geokit::Inflector::titleize(address) if address
96
+ end
97
+
98
+ # Returns a comma-delimited string consisting of the street address, city, state,
99
+ # zip, and country code. Only includes those attributes that are non-blank.
100
+ def to_geocodeable_s
101
+ a=[street_address, district, city, province, state, zip, country_code].compact
102
+ a.delete_if { |e| !e || e == '' }
103
+ a.join(', ')
104
+ end
105
+
106
+ def to_yaml_properties
107
+ (instance_variables - ['@all']).sort
108
+ end
109
+
110
+ # Returns a string representation of the instance.
111
+ def to_s
112
+ "Provider: #{provider}\nStreet: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nSuccess: #{success}"
113
+ end
114
+ end
115
+ end
@@ -26,6 +26,8 @@ module Geokit
26
26
  @@request_timeout = nil
27
27
  @@yahoo = 'REPLACE_WITH_YOUR_YAHOO_KEY'
28
28
  @@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
29
+ @@google_client = 'REPLACE_WITH_YOUR_GOOGLE_CLIENT'
30
+ @@google_channel = 'REPLACE_WITH_YOUR_GOOGLE_CHANNEL'
29
31
  @@geocoder_us = false
30
32
  @@geocoder_ca = false
31
33
  @@geonames = false
@@ -4,135 +4,142 @@ module Geokit
4
4
  # contain a Google API key. Conforms to the interface set by the Geocoder class.
5
5
  class GoogleGeocoder < Geocoder
6
6
 
7
+ ENDPOINT = "http://maps.google.com/maps/geo"
8
+
7
9
  private
8
10
 
9
- # Template method which does the reverse-geocode lookup.
10
- def self.do_reverse_geocode(latlng)
11
- latlng=LatLng.normalize(latlng)
12
- res = self.call_geocoder_service("http://maps.google.com/maps/geo?ll=#{Geokit::Inflector::url_escape(latlng.ll)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8")
13
- # res = Net::HTTP.get_response(URI.parse("http://maps.google.com/maps/geo?ll=#{Geokit::Inflector::url_escape(address_str)}&output=xml&key=#{Geokit::Geocoders::google}&oe=utf-8"))
14
- return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
15
- xml = res.body
16
- logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{xml}"
17
- return self.xml2GeoLoc(xml)
18
- end
11
+ # Template method which does the reverse-geocode lookup.
12
+ def self.do_reverse_geocode(latlng)
13
+ latlng = LatLng.normalize(latlng)
14
+ params = { :ll => latlng.ll, :output => "xml", :key => Geokit::Geocoders::google, :oe => "utf-8" }
15
+ url = "#{ENDPOINT}?" + params.to_query
16
+ res = self.call_geocoder_service(url)
17
+ return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
18
+ xml = res.body
19
+ logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{xml}"
20
+ return self.xml2GeoLoc(xml)
21
+ end
19
22
 
20
- # Template method which does the geocode lookup.
21
- #
22
- # Supports viewport/country code biasing
23
- #
24
- # ==== OPTIONS
25
- # * :bias - This option makes the Google Geocoder return results biased to a particular
26
- # country or viewport. Country code biasing is achieved by passing the ccTLD
27
- # ('uk' for .co.uk, for example) as a :bias value. For a list of ccTLD's,
28
- # look here: http://en.wikipedia.org/wiki/CcTLD. By default, the geocoder
29
- # will be biased to results within the US (ccTLD .com).
30
- #
31
- # If you'd like the Google Geocoder to prefer results within a given viewport,
32
- # you can pass a Geokit::Bounds object as the :bias value.
33
- #
34
- # ==== EXAMPLES
35
- # # By default, the geocoder will return Syracuse, NY
36
- # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse').country_code # => 'US'
37
- # # With country code biasing, it returns Syracuse in Sicily, Italy
38
- # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse', :bias => :it).country_code # => 'IT'
39
- #
40
- # # By default, the geocoder will return Winnetka, IL
41
- # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka').state # => 'IL'
42
- # # When biased to an bounding box around California, it will now return the Winnetka neighbourhood, CA
43
- # bounds = Geokit::Bounds.normalize([34.074081, -118.694401], [34.321129, -118.399487])
44
- # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka', :bias => bounds).state # => 'CA'
45
- def self.do_geocode(address, options = {})
46
- bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
47
- address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
48
- res = self.call_geocoder_service("http://maps.google.com/maps/geo?q=#{Geokit::Inflector::url_escape(address_str)}&output=xml#{bias_str}&key=#{Geokit::Geocoders::google}&oe=utf-8")
49
- return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
50
- xml = res.body
51
- logger.debug "Google geocoding. Address: #{address}. Result: #{xml}"
52
- return self.xml2GeoLoc(xml, address)
53
- end
23
+ # Template method which does the geocode lookup.
24
+ #
25
+ # Supports viewport/country code biasing
26
+ #
27
+ # ==== OPTIONS
28
+ # * :bias - This option makes the Google Geocoder return results biased to a particular
29
+ # country or viewport. Country code biasing is achieved by passing the ccTLD
30
+ # ('uk' for .co.uk, for example) as a :bias value. For a list of ccTLD's,
31
+ # look here: http://en.wikipedia.org/wiki/CcTLD. By default, the geocoder
32
+ # will be biased to results within the US (ccTLD .com).
33
+ #
34
+ # If you'd like the Google Geocoder to prefer results within a given viewport,
35
+ # you can pass a Geokit::Bounds object as the :bias value.
36
+ #
37
+ # ==== EXAMPLES
38
+ # # By default, the geocoder will return Syracuse, NY
39
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse').country_code # => 'US'
40
+ # # With country code biasing, it returns Syracuse in Sicily, Italy
41
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse', :bias => :it).country_code # => 'IT'
42
+ #
43
+ # # By default, the geocoder will return Winnetka, IL
44
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka').state # => 'IL'
45
+ # # When biased to an bounding box around California, it will now return the Winnetka neighbourhood, CA
46
+ # bounds = Geokit::Bounds.normalize([34.074081, -118.694401], [34.321129, -118.399487])
47
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka', :bias => bounds).state # => 'CA'
48
+ def self.do_geocode(address, options = {})
49
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
50
+ params = { :q => address_str, :output => "xml", :key => Geokit::Geocoders::google, :oe => "utf-8" }.merge(bias_options(options[:bias]))
51
+ url = "#{ENDPOINT}?" + params.to_query
52
+ res = self.call_geocoder_service(url)
53
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
54
+ xml = res.body
55
+ logger.debug "Google geocoding. Address: #{address}. Result: #{xml}"
56
+ return self.xml2GeoLoc(xml, address)
57
+ end
54
58
 
55
- def self.construct_bias_string_from_options(bias)
56
- if bias.is_a?(String) or bias.is_a?(Symbol)
57
- # country code biasing
58
- "&gl=#{bias.to_s.downcase}"
59
- elsif bias.is_a?(Bounds)
60
- # viewport biasing
61
- "&ll=#{bias.center.ll}&spn=#{bias.to_span.ll}"
59
+ def self.bias_options(bias)
60
+ if bias.is_a?(String) or bias.is_a?(Symbol)
61
+ # country code biasing
62
+ { :gl => bias.to_s.downcase }
63
+ elsif bias.is_a?(Bounds)
64
+ # viewport biasing
65
+ { :ll => bias.center.ll, :spn => bias.to_span.ll }
66
+ else
67
+ {}
68
+ end
62
69
  end
63
- end
64
70
 
65
- def self.xml2GeoLoc(xml, address="")
66
- doc=REXML::Document.new(xml)
71
+ def self.xml2GeoLoc(xml, address="")
72
+ doc=REXML::Document.new(xml)
67
73
 
68
- if doc.elements['//kml/Response/Status/code'].text == '200'
69
- geoloc = nil
70
- # Google can return multiple results as //Placemark elements.
71
- # iterate through each and extract each placemark as a geoloc
72
- doc.each_element('//Placemark') do |e|
73
- extracted_geoloc = extract_placemark(e) # g is now an instance of GeoLoc
74
- if geoloc.nil?
75
- # first time through, geoloc is still nil, so we make it the geoloc we just extracted
76
- geoloc = extracted_geoloc
77
- else
78
- # second (and subsequent) iterations, we push additional
79
- # geolocs onto "geoloc.all"
80
- geoloc.all.push(extracted_geoloc)
74
+ if doc.elements['//kml/Response/Status/code'].text == '200'
75
+ geoloc = nil
76
+ # Google can return multiple results as //Placemark elements.
77
+ # iterate through each and extract each placemark as a geoloc
78
+ doc.each_element('//Placemark') do |e|
79
+ extracted_geoloc = extract_placemark(e) # g is now an instance of GeoLoc
80
+ if geoloc.nil?
81
+ # first time through, geoloc is still nil, so we make it the geoloc we just extracted
82
+ geoloc = extracted_geoloc
83
+ else
84
+ # second (and subsequent) iterations, we push additional
85
+ # geolocs onto "geoloc.all"
86
+ geoloc.all.push(extracted_geoloc)
87
+ end
81
88
  end
89
+ return geoloc
90
+ elsif doc.elements['//kml/Response/Status/code'].text == '620'
91
+ raise Geokit::TooManyQueriesError
92
+ else
93
+ logger.info "Google was unable to geocode address: "+address
94
+ return GeoLoc.new
82
95
  end
83
- return geoloc
84
- elsif doc.elements['//kml/Response/Status/code'].text == '620'
85
- raise Geokit::TooManyQueriesError
86
- else
87
- logger.info "Google was unable to geocode address: "+address
96
+
97
+ rescue Geokit::TooManyQueriesError
98
+ # re-raise because of other rescue
99
+ raise Geokit::TooManyQueriesError, "Google returned a 620 status, too many queries. The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time. If you're sending multiple requests in parallel or in a tight loop, use a timer or pause in your code to make sure you don't send the requests too quickly."
100
+ rescue
101
+ logger.error "Caught an error during Google geocoding call: "+$!
88
102
  return GeoLoc.new
89
103
  end
90
104
 
91
- rescue Geokit::TooManyQueriesError
92
- # re-raise because of other rescue
93
- raise Geokit::TooManyQueriesError, "Google returned a 620 status, too many queries. The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time. If you're sending multiple requests in parallel or in a tight loop, use a timer or pause in your code to make sure you don't send the requests too quickly."
94
- rescue
95
- logger.error "Caught an error during Google geocoding call: "+$!
96
- return GeoLoc.new
97
- end
105
+ # extracts a single geoloc from a //placemark element in the google results xml
106
+ def self.extract_placemark(doc)
107
+ res = GeoLoc.new
108
+ coordinates=doc.elements['.//coordinates'].text.to_s.split(',')
109
+
110
+ #basics
111
+ res.lat=coordinates[1]
112
+ res.lng=coordinates[0]
113
+ res.country_code=doc.elements['.//CountryNameCode'].text if doc.elements['.//CountryNameCode']
114
+ res.provider='google'
98
115
 
99
- # extracts a single geoloc from a //placemark element in the google results xml
100
- def self.extract_placemark(doc)
101
- res = GeoLoc.new
102
- coordinates=doc.elements['.//coordinates'].text.to_s.split(',')
116
+ #extended -- false if not not available
117
+ res.city = doc.elements['.//LocalityName'].text if doc.elements['.//LocalityName']
118
+ res.state = doc.elements['.//AdministrativeAreaName'].text if doc.elements['.//AdministrativeAreaName']
119
+ res.province = doc.elements['.//SubAdministrativeAreaName'].text if doc.elements['.//SubAdministrativeAreaName']
120
+ res.full_address = doc.elements['.//address'].text if doc.elements['.//address'] # google provides it
121
+ res.zip = doc.elements['.//PostalCodeNumber'].text if doc.elements['.//PostalCodeNumber']
122
+ res.street_address = doc.elements['.//ThoroughfareName'].text if doc.elements['.//ThoroughfareName']
123
+ res.country = doc.elements['.//CountryName'].text if doc.elements['.//CountryName']
124
+ res.district = doc.elements['.//DependentLocalityName'].text if doc.elements['.//DependentLocalityName']
125
+ # Translate accuracy into Yahoo-style token address, street, zip, zip+4, city, state, country
126
+ # For Google, 1=low accuracy, 8=high accuracy
127
+ address_details=doc.elements['.//*[local-name() = "AddressDetails"]']
128
+ res.accuracy = address_details ? address_details.attributes['Accuracy'].to_i : 0
129
+ res.precision=%w{unknown country state state city zip zip+4 street address building}[res.accuracy]
103
130
 
104
- #basics
105
- res.lat=coordinates[1]
106
- res.lng=coordinates[0]
107
- res.country_code=doc.elements['.//CountryNameCode'].text if doc.elements['.//CountryNameCode']
108
- res.provider='google'
131
+ # google returns a set of suggested boundaries for the geocoded result
132
+ if suggested_bounds = doc.elements['//LatLonBox']
133
+ res.suggested_bounds = Bounds.normalize(
134
+ [suggested_bounds.attributes['south'], suggested_bounds.attributes['west']],
135
+ [suggested_bounds.attributes['north'], suggested_bounds.attributes['east']])
136
+ end
109
137
 
110
- #extended -- false if not not available
111
- res.city = doc.elements['.//LocalityName'].text if doc.elements['.//LocalityName']
112
- res.state = doc.elements['.//AdministrativeAreaName'].text if doc.elements['.//AdministrativeAreaName']
113
- res.province = doc.elements['.//SubAdministrativeAreaName'].text if doc.elements['.//SubAdministrativeAreaName']
114
- res.full_address = doc.elements['.//address'].text if doc.elements['.//address'] # google provides it
115
- res.zip = doc.elements['.//PostalCodeNumber'].text if doc.elements['.//PostalCodeNumber']
116
- res.street_address = doc.elements['.//ThoroughfareName'].text if doc.elements['.//ThoroughfareName']
117
- res.country = doc.elements['.//CountryName'].text if doc.elements['.//CountryName']
118
- res.district = doc.elements['.//DependentLocalityName'].text if doc.elements['.//DependentLocalityName']
119
- # Translate accuracy into Yahoo-style token address, street, zip, zip+4, city, state, country
120
- # For Google, 1=low accuracy, 8=high accuracy
121
- address_details=doc.elements['.//*[local-name() = "AddressDetails"]']
122
- res.accuracy = address_details ? address_details.attributes['Accuracy'].to_i : 0
123
- res.precision=%w{unknown country state state city zip zip+4 street address building}[res.accuracy]
138
+ res.success=true
124
139
 
125
- # google returns a set of suggested boundaries for the geocoded result
126
- if suggested_bounds = doc.elements['//LatLonBox']
127
- res.suggested_bounds = Bounds.normalize(
128
- [suggested_bounds.attributes['south'], suggested_bounds.attributes['west']],
129
- [suggested_bounds.attributes['north'], suggested_bounds.attributes['east']])
140
+ return res
130
141
  end
131
142
 
132
- res.success=true
133
-
134
- return res
135
- end
136
143
  end
137
144
  end
138
145
  end