steveh-geokit 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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