abuiles-geokit 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.
Files changed (45) hide show
  1. data/.bundle/config +2 -0
  2. data/.gitignore +4 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +19 -0
  5. data/History.txt +77 -0
  6. data/Manifest.txt +21 -0
  7. data/README.markdown +273 -0
  8. data/Rakefile +13 -0
  9. data/geokit.gemspec +24 -0
  10. data/lib/geokit.rb +55 -0
  11. data/lib/geokit/bounds.rb +95 -0
  12. data/lib/geokit/geo_loc.rb +115 -0
  13. data/lib/geokit/geocoders.rb +68 -0
  14. data/lib/geokit/geocoders/ca_geocoder.rb +54 -0
  15. data/lib/geokit/geocoders/geo_plugin_geocoder.rb +30 -0
  16. data/lib/geokit/geocoders/geocode_error.rb +7 -0
  17. data/lib/geokit/geocoders/geocoder.rb +75 -0
  18. data/lib/geokit/geocoders/geonames_geocoder.rb +53 -0
  19. data/lib/geokit/geocoders/google_geocoder.rb +145 -0
  20. data/lib/geokit/geocoders/google_premier_geocoder.rb +147 -0
  21. data/lib/geokit/geocoders/ip_geocoder.rb +76 -0
  22. data/lib/geokit/geocoders/multi_geocoder.rb +60 -0
  23. data/lib/geokit/geocoders/us_geocoder.rb +50 -0
  24. data/lib/geokit/geocoders/yahoo_geocoder.rb +49 -0
  25. data/lib/geokit/inflector.rb +39 -0
  26. data/lib/geokit/lat_lng.rb +112 -0
  27. data/lib/geokit/mappable.rb +210 -0
  28. data/lib/geokit/too_many_queries_error.rb +4 -0
  29. data/lib/geokit/version.rb +3 -0
  30. data/test/test_base_geocoder.rb +58 -0
  31. data/test/test_bounds.rb +97 -0
  32. data/test/test_ca_geocoder.rb +39 -0
  33. data/test/test_geoloc.rb +72 -0
  34. data/test/test_geoplugin_geocoder.rb +58 -0
  35. data/test/test_google_geocoder.rb +225 -0
  36. data/test/test_google_premier_geocoder.rb +88 -0
  37. data/test/test_google_reverse_geocoder.rb +47 -0
  38. data/test/test_inflector.rb +24 -0
  39. data/test/test_ipgeocoder.rb +109 -0
  40. data/test/test_latlng.rb +209 -0
  41. data/test/test_multi_geocoder.rb +91 -0
  42. data/test/test_multi_ip_geocoder.rb +36 -0
  43. data/test/test_us_geocoder.rb +54 -0
  44. data/test/test_yahoo_geocoder.rb +103 -0
  45. metadata +141 -0
@@ -0,0 +1,53 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # Another geocoding web service
4
+ # http://www.geonames.org
5
+ class GeonamesGeocoder < Geocoder
6
+
7
+ private
8
+
9
+ # Template method which does the geocode lookup.
10
+ def self.do_geocode(address, options = {})
11
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
12
+ # geonames need a space seperated search string
13
+ address_str.gsub!(/,/, " ")
14
+ params = "/postalCodeSearch?placename=#{Geokit::Inflector::url_escape(address_str)}&maxRows=10"
15
+
16
+ if(Geokit::Geocoders::geonames)
17
+ url = "http://ws.geonames.net#{params}&username=#{Geokit::Geocoders::geonames}"
18
+ else
19
+ url = "http://ws.geonames.org#{params}"
20
+ end
21
+
22
+ res = self.call_geocoder_service(url)
23
+
24
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
25
+
26
+ xml=res.body
27
+ logger.debug "Geonames geocoding. Address: #{address}. Result: #{xml}"
28
+ doc=REXML::Document.new(xml)
29
+
30
+ if(doc.elements['//geonames/totalResultsCount'].text.to_i > 0)
31
+ res=GeoLoc.new
32
+
33
+ # only take the first result
34
+ res.lat=doc.elements['//code/lat'].text if doc.elements['//code/lat']
35
+ res.lng=doc.elements['//code/lng'].text if doc.elements['//code/lng']
36
+ res.country_code=doc.elements['//code/countryCode'].text if doc.elements['//code/countryCode']
37
+ res.provider='genomes'
38
+ res.city=doc.elements['//code/name'].text if doc.elements['//code/name']
39
+ res.state=doc.elements['//code/adminName1'].text if doc.elements['//code/adminName1']
40
+ res.zip=doc.elements['//code/postalcode'].text if doc.elements['//code/postalcode']
41
+ res.success=true
42
+ return res
43
+ else
44
+ logger.info "Geonames was unable to geocode address: "+address
45
+ return GeoLoc.new
46
+ end
47
+
48
+ rescue
49
+ logger.error "Caught an error during Geonames geocoding call: "+$!
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,145 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # Google geocoder implementation. Requires the Geokit::Geocoders::GOOGLE variable to
4
+ # contain a Google API key. Conforms to the interface set by the Geocoder class.
5
+ class GoogleGeocoder < Geocoder
6
+
7
+ ENDPOINT = "http://maps.google.com/maps/geo"
8
+
9
+ private
10
+
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
22
+
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.force_encoding('utf-8')
55
+ logger.debug "Google geocoding. Address: #{address}. Result: #{xml}"
56
+ return self.xml2GeoLoc(xml, address)
57
+ end
58
+
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
69
+ end
70
+
71
+ def self.xml2GeoLoc(xml, address="")
72
+ doc=REXML::Document.new(xml)
73
+
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
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
95
+ end
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: "+$!
102
+ return GeoLoc.new
103
+ end
104
+
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'
115
+
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]
130
+
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
137
+
138
+ res.success=true
139
+
140
+ return res
141
+ end
142
+
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,147 @@
1
+ module Geokit
2
+ module Geocoders
3
+ class GooglePremierGeocoder < Geocoder
4
+
5
+ HOST = "maps.googleapis.com"
6
+ ENDPOINT = "/maps/api/geocode/json"
7
+
8
+ protected
9
+
10
+ def self.do_reverse_geocode(latlng)
11
+ latlng = LatLng.normalize(latlng)
12
+ params = { :latlng => latlng.ll, :sensor => false }
13
+ signed_url = signed_url_for_params(params)
14
+ res = self.call_geocoder_service(signed_url)
15
+ return GeoLoc.new unless (res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPOK))
16
+ json = JSON.parse(res.body)
17
+ logger.debug "Google reverse-geocoding. LL: #{latlng}. Result: #{json}"
18
+ return self.json_to_geo_loc(json)
19
+ end
20
+
21
+ def self.do_geocode(address, options = {})
22
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
23
+ params = { :address => address_str, :sensor => false }
24
+ signed_url = signed_url_for_params(params)
25
+ res = self.call_geocoder_service(signed_url)
26
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
27
+ json = JSON.parse(res.body)
28
+ logger.debug "Google geocoding. Address: #{address}. Result: #{json}"
29
+ return self.json_to_geo_loc(json, address)
30
+ end
31
+
32
+ def self.json_to_geo_loc(json, address="")
33
+ if json["status"] == "OK"
34
+ geoloc = nil
35
+
36
+ # iterate through each and extract as a geoloc
37
+ json["results"].each do |result|
38
+ extracted_geoloc = extract_result(result) # g is now an instance of GeoLoc
39
+ if geoloc.nil?
40
+ # first time through, geoloc is still nil, so we make it the geoloc we just extracted
41
+ geoloc = extracted_geoloc
42
+ else
43
+ # second (and subsequent) iterations, we push additional
44
+ # geolocs onto "geoloc.all"
45
+ geoloc.all.push(extracted_geoloc)
46
+ end
47
+ end
48
+ return geoloc
49
+ elsif json["status"] == "ZERO_RESULTS"
50
+ return GeoLoc.new
51
+ elsif json["status"] == "OVER_QUERY_LIMIT"
52
+ raise Geokit::TooManyQueriesError
53
+ elsif json["status"] == "REQUEST_DENIED"
54
+ logger.error "Google Premier request denied: "+$!
55
+ elsif json["status"] == "INVALID_REQUEST"
56
+ logger.error "Google Premier invalid request: "+$!
57
+ else
58
+ logger.info "Google was unable to geocode address: "+address
59
+ return GeoLoc.new
60
+ end
61
+ rescue Geokit::TooManyQueriesError
62
+ raise Geokit::TooManyQueriesError, "Google Premier 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."
63
+ rescue
64
+ logger.error "Caught an error during Google Premier geocoding call: "+$!
65
+ return GeoLoc.new
66
+ end
67
+
68
+ ACCURACY_MAP = {
69
+ "ROOFTOP" => 8,
70
+ "RANGE_INTERPOLATED" => 7,
71
+ "GEOMETRIC_CENTER" => 6,
72
+ "APPROXIMATE" => 5,
73
+ }
74
+
75
+ # extracts a single geoloc from a result in the google results json
76
+ def self.extract_result(result)
77
+ res = GeoLoc.new
78
+
79
+ res.provider = "google_premier"
80
+
81
+ geometry = result["geometry"] if result
82
+
83
+ res.accuracy = ACCURACY_MAP[geometry["location_type"]] if geometry
84
+ res.precision = geometry["location_type"].underscore if geometry
85
+
86
+ location = geometry["location"] if geometry
87
+ res.lat = location["lat"] if location
88
+ res.lng = location["lng"] if location
89
+
90
+ res.full_address = result["formatted_address"]
91
+ res.street_address = result["formatted_address"]
92
+
93
+ address_components = result["address_components"] if result
94
+ address_components.each do |component|
95
+ res.zip = component["long_name"] if component["types"].include?("postal_code")
96
+ res.country = component["long_name"] if component["types"].include?("country")
97
+ res.country_code = component["short_name"] if component["types"].include?("country")
98
+ res.city = component["long_name"] if component["types"].include?("locality")
99
+ res.state = component["long_name"] if component["types"].include?("administrative_area_level_1")
100
+ res.province = component["long_name"] if component["types"].include?("administrative_area_level_2")
101
+ res.district = component["long_name"] if component["types"].include?("administrative_area_level_3")
102
+ end if address_components
103
+
104
+ bounds = geometry["bounds"]
105
+
106
+ res.suggested_bounds = Bounds.normalize(
107
+ [bounds["southwest"]["lat"], bounds["southwest"]["lng"]],
108
+ [bounds["northeast"]["lat"], bounds["northeast"]["lng"]]
109
+ ) if bounds
110
+
111
+ res.success = true
112
+
113
+ res
114
+ end
115
+
116
+ private
117
+
118
+ def self.url_for_params(params)
119
+ params_to_sign = params
120
+ "http://" + HOST + string_to_sign
121
+ end
122
+
123
+ def self.signed_url_for_params(params)
124
+ params_to_sign = params.merge({ :client => Geokit::Geocoders.google_client, :channel => Geokit::Geocoders.google_channel }).reject{ |key, value| value.nil? }
125
+ string_to_sign = "#{ENDPOINT}?#{params_to_sign.to_query}"
126
+ signature = signature_for_string(string_to_sign)
127
+ "http://" + HOST + string_to_sign + "&signature=#{signature}"
128
+ end
129
+
130
+ def self.signature_for_string(string)
131
+ raw_private_key = url_safe_base64_decode(Geokit::Geocoders.google)
132
+ digest = OpenSSL::Digest::Digest.new("sha1")
133
+ raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string)
134
+ url_safe_base64_encode(raw_signature)
135
+ end
136
+
137
+ def self.url_safe_base64_decode(base64_string)
138
+ Base64.decode64(base64_string.tr("-_","+/"))
139
+ end
140
+
141
+ def self.url_safe_base64_encode(raw)
142
+ Base64.encode64(raw).tr("+/","-_").strip
143
+ end
144
+
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,76 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # Provides geocoding based upon an IP address. The underlying web service is a hostip.info
4
+ # which sources their data through a combination of publicly available information as well
5
+ # as community contributions.
6
+ class IpGeocoder < Geocoder
7
+
8
+ # A number of non-routable IP ranges.
9
+ #
10
+ # --
11
+ # Sources for these:
12
+ # RFC 3330: Special-Use IPv4 Addresses
13
+ # The bogon list: http://www.cymru.com/Documents/bogon-list.html
14
+
15
+ NON_ROUTABLE_IP_RANGES = [
16
+ IPAddr.new('0.0.0.0/8'), # "This" Network
17
+ IPAddr.new('10.0.0.0/8'), # Private-Use Networks
18
+ IPAddr.new('14.0.0.0/8'), # Public-Data Networks
19
+ IPAddr.new('127.0.0.0/8'), # Loopback
20
+ IPAddr.new('169.254.0.0/16'), # Link local
21
+ IPAddr.new('172.16.0.0/12'), # Private-Use Networks
22
+ IPAddr.new('192.0.2.0/24'), # Test-Net
23
+ IPAddr.new('192.168.0.0/16'), # Private-Use Networks
24
+ IPAddr.new('198.18.0.0/15'), # Network Interconnect Device Benchmark Testing
25
+ IPAddr.new('224.0.0.0/4'), # Multicast
26
+ IPAddr.new('240.0.0.0/4') # Reserved for future use
27
+ ].freeze
28
+
29
+ private
30
+
31
+ # Given an IP address, returns a GeoLoc instance which contains latitude,
32
+ # longitude, city, and country code. Sets the success attribute to false if the ip
33
+ # parameter does not match an ip address.
34
+ def self.do_geocode(ip, options = {})
35
+ return GeoLoc.new unless /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(ip)
36
+ return GeoLoc.new if self.private_ip_address?(ip)
37
+ url = "http://api.hostip.info/get_html.php?ip=#{ip}&position=true"
38
+ response = self.call_geocoder_service(url)
39
+ response.is_a?(Net::HTTPSuccess) ? parse_body(response.body) : GeoLoc.new
40
+ rescue
41
+ logger.error "Caught an error during HostIp geocoding call: "+$!
42
+ return GeoLoc.new
43
+ end
44
+
45
+ # Converts the body to YAML since its in the form of:
46
+ #
47
+ # Country: UNITED STATES (US)
48
+ # City: Sugar Grove, IL
49
+ # Latitude: 41.7696
50
+ # Longitude: -88.4588
51
+ #
52
+ # then instantiates a GeoLoc instance to populate with location data.
53
+ def self.parse_body(body) # :nodoc:
54
+ yaml = YAML.load(body)
55
+ res = GeoLoc.new
56
+ res.provider = 'hostip'
57
+ res.city, res.state = yaml['City'].split(', ')
58
+ country, res.country_code = yaml['Country'].split(' (')
59
+ res.lat = yaml['Latitude']
60
+ res.lng = yaml['Longitude']
61
+ res.country_code.chop!
62
+ res.success = !(res.city =~ /\(.+\)/)
63
+ res
64
+ end
65
+
66
+ # Checks whether the IP address belongs to a private address range.
67
+ #
68
+ # This function is used to reduce the number of useless queries made to
69
+ # the geocoding service. Such queries can occur frequently during
70
+ # integration tests.
71
+ def self.private_ip_address?(ip)
72
+ return NON_ROUTABLE_IP_RANGES.any? { |range| range.include?(ip) }
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,60 @@
1
+ module Geokit
2
+ module Geocoders
3
+ # -------------------------------------------------------------------------------------------
4
+ # The Multi Geocoder
5
+ # -------------------------------------------------------------------------------------------
6
+
7
+ # Provides methods to geocode with a variety of geocoding service providers, plus failover
8
+ # among providers in the order you configure. When 2nd parameter is set 'true', perform
9
+ # ip location lookup with 'address' as the ip address.
10
+ #
11
+ # Goal:
12
+ # - homogenize the results of multiple geocoders
13
+ #
14
+ # Limitations:
15
+ # - currently only provides the first result. Sometimes geocoders will return multiple results.
16
+ # - currently discards the "accuracy" component of the geocoding calls
17
+ class MultiGeocoder < Geocoder
18
+
19
+ private
20
+ # This method will call one or more geocoders in the order specified in the
21
+ # configuration until one of the geocoders work.
22
+ #
23
+ # The failover approach is crucial for production-grade apps, but is rarely used.
24
+ # 98% of your geocoding calls will be successful with the first call
25
+ def self.do_geocode(address, options = {})
26
+ geocode_ip = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.match(address)
27
+ provider_order = geocode_ip ? Geokit::Geocoders::ip_provider_order : Geokit::Geocoders::provider_order
28
+
29
+ provider_order.each do |provider|
30
+ begin
31
+ klass = Geokit::Geocoders.const_get "#{Geokit::Inflector::camelize(provider.to_s)}Geocoder"
32
+ res = klass.send :geocode, address, options
33
+ return res if res.success?
34
+ rescue
35
+ logger.error("Something has gone very wrong during geocoding, OR you have configured an invalid class name in Geokit::Geocoders::provider_order. Address: #{address}. Provider: #{provider}")
36
+ end
37
+ end
38
+ # If we get here, we failed completely.
39
+ GeoLoc.new
40
+ end
41
+
42
+ # This method will call one or more geocoders in the order specified in the
43
+ # configuration until one of the geocoders work, only this time it's going
44
+ # to try to reverse geocode a geographical point.
45
+ def self.do_reverse_geocode(latlng)
46
+ Geokit::Geocoders::provider_order.each do |provider|
47
+ begin
48
+ klass = Geokit::Geocoders.const_get "#{Geokit::Inflector::camelize(provider.to_s)}Geocoder"
49
+ res = klass.send :reverse_geocode, latlng
50
+ return res if res.success?
51
+ rescue
52
+ logger.error("Something has gone very wrong during reverse geocoding, OR you have configured an invalid class name in Geokit::Geocoders::provider_order. LatLng: #{latlng}. Provider: #{provider}")
53
+ end
54
+ end
55
+ # If we get here, we failed completely.
56
+ GeoLoc.new
57
+ end
58
+ end
59
+ end
60
+ end