abuiles-geokit 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
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