sayso-geokit 1.5.0.3.001

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt ADDED
@@ -0,0 +1,21 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.markdown
4
+ Rakefile
5
+ geokit.gemspec
6
+ lib/geokit.rb
7
+ lib/geokit/geocoders.rb
8
+ lib/geokit/mappable.rb
9
+ test/test_base_geocoder.rb
10
+ test/test_bounds.rb
11
+ test/test_ca_geocoder.rb
12
+ test/test_geoloc.rb
13
+ test/test_geoplugin_geocoder.rb
14
+ test/test_google_geocoder.rb
15
+ test/test_google_reverse_geocoder.rb
16
+ test/test_inflector.rb
17
+ test/test_ipgeocoder.rb
18
+ test/test_latlng.rb
19
+ test/test_multi_geocoder.rb
20
+ test/test_us_geocoder.rb
21
+ test/test_yahoo_geocoder.rb
data/README.markdown ADDED
@@ -0,0 +1,200 @@
1
+ ## GEOKIT GEM DESCRIPTION
2
+
3
+ The Geokit gem provides:
4
+
5
+ * Distance calculations between two points on the earth. Calculate the distance in miles, kilometers, or nautical miles, with all the trigonometry abstracted away by GeoKit.
6
+ * Geocoding from multiple providers. It supports Google geocoder. It provides a uniform response structure from all of them.
7
+ It also provides a fail-over mechanism, in case your input fails to geocode in one service.
8
+ * Rectangular bounds calculations: is a point within a given rectangular bounds?
9
+ * Heading and midpoint calculations
10
+
11
+ * Repository at Github: [http://github.com/bartes/geokit-gem/tree/master](http://github.com/andre/geokit-gem/tree/master).
12
+
13
+ ## INSTALL
14
+
15
+ sudo gem install bartes-geokit
16
+
17
+ ## QUICK START
18
+
19
+ irb> require 'rubygems'
20
+ irb> require 'geokit'
21
+ irb> a=Geokit::Geocoders::GoogleGeocoder.geocode '140 Market St, San Francisco, CA'
22
+ irb> a.ll
23
+ => 37.79363,-122.396116
24
+ irb> b=Geokit::Geocoders::GoogleGeocoder.geocode '789 Geary St, San Francisco, CA'
25
+ irb> b.ll
26
+ => 37.786217,-122.41619
27
+ irb> a.distance_to(b)
28
+ => 1.21120007413626
29
+ irb> a.heading_to(b)
30
+ => 244.959832435678
31
+ irb(main):006:0> c=a.midpoint_to(b) # what's halfway from a to b?
32
+ irb> c.ll
33
+ => "37.7899239257175,-122.406153503469"
34
+ irb(main):008:0> d=c.endpoint(90,10) # what's 10 miles to the east of c?
35
+ irb> d.ll
36
+ => "37.7897825005142,-122.223214776155"
37
+
38
+ FYI, that `.ll` method means "latitude longitude".
39
+
40
+ See the RDOC more more ... there are also operations on rectangular bounds (e.g., determining if a point is within bounds, find the center, etc).
41
+
42
+ ## CONFIGURATION
43
+
44
+ If you're using this gem by itself, here are the configuration options:
45
+
46
+ # These defaults are used in Geokit::Mappable.distance_to and in acts_as_mappable
47
+ Geokit::default_units = :miles
48
+ Geokit::default_formula = :sphere
49
+
50
+ # This is the timeout value in seconds to be used for calls to the geocoder web
51
+ # services. For no timeout at all, comment out the setting. The timeout unit
52
+ # is in seconds.
53
+ Geokit::Geocoders::request_timeout = 3
54
+
55
+ # This is your Google Maps geocoder key.
56
+ # See http://www.google.com/apis/maps/signup.html
57
+ # and http://www.google.com/apis/maps/documentation/#Geocoding_Examples
58
+ Geokit::Geocoders::google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
59
+
60
+ # You can also set multiple API KEYS for different domains that may be directed to this same application.
61
+ # The domain from which the current user is being directed will automatically be updated for Geokit via
62
+ # the GeocoderControl class, which gets it's begin filter mixed into the ActionController.
63
+ # You define these keys with a Hash as follows:
64
+ #Geokit::Geocoders::google = { 'rubyonrails.org' => 'RUBY_ON_RAILS_API_KEY', 'ruby-docs.org' => 'RUBY_DOCS_API_KEY' }
65
+
66
+ ## SUPPORTED GEOCODERS
67
+
68
+ ### address geocoders that also provide reverse geocoding
69
+ * Google Geocoder - requires an API key. Also supports multiple results and bounding box/country code biasing.
70
+
71
+ ### Google Geocoder Tricks
72
+
73
+ The Google Geocoder sports a number of useful tricks that elevate it a little bit above the rest of the currently supported geocoders. For starters, it returns a `suggested_bounds` property for all your geocoded results, so you can more easily decide where and how to center a map on the places you geocode. Here's a quick example:
74
+
75
+ irb> res = Geokit::Geocoders::GoogleGeocoder.geocode('140 Market St, San Francisco, CA')
76
+ irb> pp res.suggested_bounds
77
+ #<Geokit::Bounds:0x53b36c
78
+ @ne=#<Geokit::LatLng:0x53b204 @lat=37.7968528, @lng=-122.3926933>,
79
+ @sw=#<Geokit::LatLng:0x53b2b8 @lat=37.7905576, @lng=-122.3989885>>
80
+
81
+ In addition, you can use viewport or country code biasing to make sure the geocoders prefers results within a specific area. Say we wanted to geocode the city of Syracuse in Italy. A normal geocoding query would look like this:
82
+
83
+ irb> res = Geokit::Geocoder::GoogleGeocoder.geocode('Syracuse')
84
+ irb> res.full_address
85
+ => "Syracuse, NY, USA"
86
+
87
+ Not exactly what we were looking for. We know that Syracuse is in Italy, so we can tell the Google Geocoder to prefer results from Italy first, and then wander the Syracuses of the world. To do that, we have to pass Italy's ccTLD (country code top-level domain) to the `:bias` option of the `geocode` method. You can find a comprehensive list of all ccTLDs here: http://en.wikipedia.org/wiki/CcTLD.
88
+
89
+ irb> res = Geokit::Geocoder::GoogleGeocoder.geocode('Syracuse', :bias => 'it')
90
+ irb> res.full_address
91
+ => "Syracuse, Italy"
92
+
93
+ Alternatively, we can speficy the geocoding bias as a bounding box object. Say we wanted to geocode the Winnetka district in Los Angeles.
94
+
95
+ irb> res = Geokit::Geocoder::GoogleGeocoder.geocode('Winnetka')
96
+ irb> res.full_address
97
+ => "Winnetka, IL, USA"
98
+
99
+ Not it. What we can do is tell the geocoder to return results only from in and around LA.
100
+
101
+ irb> la_bounds = Geokit::Geocoder::GoogleGeocoder.geocode('Los Angeles').suggested_bounds
102
+ irb> res = Geokit::Geocoder::GoogleGeocoder.geocode('Winnetka', :bias => la_bounds)
103
+ irb> res.full_address
104
+ => "Winnetka, California, USA"
105
+
106
+
107
+ ## MULTIPLE RESULTS
108
+ Some geocoding services will return multple results if the there isn't one clear result.
109
+ Geoloc can capture multiple results through its "all" method. Currently only the Google geocoder
110
+ supports multiple results:
111
+
112
+ irb> geo=Geokit::Geocoders::GoogleGeocoder.geocode("900 Sycamore Drive")
113
+ irb> geo.full_address
114
+ => "900 Sycamore Dr, Arkadelphia, AR 71923, USA"
115
+ irb> geo.all.size
116
+ irb> geo.all.each { |e| puts e.full_address }
117
+ 900 Sycamore Dr, Arkadelphia, AR 71923, USA
118
+ 900 Sycamore Dr, Burkburnett, TX 76354, USA
119
+ 900 Sycamore Dr, TN 38361, USA
120
+ ....
121
+
122
+ geo.all is just an array of additional Geolocs, so do what you want with it. If you call .all on a
123
+ geoloc that doesn't have any additional results, you will get an array of one.
124
+
125
+
126
+ ## NOTES ON WHAT'S WHERE
127
+
128
+ mappable.rb contains the Mappable module, which provides basic
129
+ distance calculation methods, i.e., calculating the distance
130
+ between two points.
131
+
132
+ mappable.rb also contains LatLng, GeoLoc, and Bounds.
133
+ LatLng is a simple container for latitude and longitude, but
134
+ it's made more powerful by mixing in the above-mentioned Mappable
135
+ module -- therefore, you can calculate easily the distance between two
136
+ LatLng ojbects with `distance = first.distance_to(other)`
137
+
138
+ GeoLoc (also in mappable.rb) represents an address or location which
139
+ has been geocoded. You can get the city, zipcode, street address, etc.
140
+ from a GeoLoc object. GeoLoc extends LatLng, so you also get lat/lng
141
+ AND the Mappable modeule goodness for free.
142
+
143
+ geocoders.rb contains all the geocoder implemenations. All the gercoders
144
+ inherit from a common base (class Geocoder) and implement the private method
145
+ do_geocode.
146
+
147
+ ## WRITING YOUR OWN GEOCODERS
148
+
149
+ If you would like to write your own geocoders, you can do so by requiring 'geokit' or 'geokit/geocoders.rb' in a new file and subclassing the base class (which is class "Geocoder").
150
+ You must then also require such extenal file back in your main geokit configuration.
151
+
152
+ require "geokit"
153
+
154
+ module Geokit
155
+ module Geocoders
156
+
157
+ # Should be overriden as Geokit::Geocoders::external_key in your configuration file
158
+ @@external_key = 'REPLACE_WITH_YOUR_API_KEY'
159
+ __define_accessors
160
+
161
+ # Replace name 'External' (below) with the name of your custom geocoder class
162
+ # and use :external to specify this geocoder in your list of geocoders.
163
+ class ExternalGeocoder < Geocoder
164
+ private
165
+ def self.do_geocode(address, options = {})
166
+ # Main geocoding method
167
+ end
168
+ end
169
+
170
+ end
171
+ end
172
+
173
+ ## GOOGLE GROUP
174
+
175
+ Follow the Google Group for updates and discussion on Geokit: http://groups.google.com/group/geokit
176
+
177
+ ## LICENSE
178
+
179
+ (The MIT License)
180
+
181
+ Copyright (c) 2007-2009 Andre Lewis and Bill Eisenhauer
182
+
183
+ Permission is hereby granted, free of charge, to any person obtaining
184
+ a copy of this software and associated documentation files (the
185
+ 'Software'), to deal in the Software without restriction, including
186
+ without limitation the rights to use, copy, modify, merge, publish,
187
+ distribute, sublicense, and/or sell copies of the Software, and to
188
+ permit persons to whom the Software is furnished to do so, subject to
189
+ the following conditions:
190
+
191
+ The above copyright notice and this permission notice shall be
192
+ included in all copies or substantial portions of the Software.
193
+
194
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
195
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
196
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
197
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
198
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
199
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
200
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/geokit.rb'
6
+
7
+ # undefined method `empty?' for nil:NilClass
8
+ # /Library/Ruby/Site/1.8/rubygems/specification.rb:886:in `validate'
9
+ class NilClass
10
+ def empty?
11
+ true
12
+ end
13
+ end
14
+
15
+ project=Hoe.new('geokit', Geokit::VERSION) do |p|
16
+ #p.rubyforge_name = 'geokit' # if different than lowercase project name
17
+ p.developer('Andre Lewis', 'andre@earthcode.com')
18
+ p.summary="Geokit provides geocoding and distance calculation in an easy-to-use API"
19
+ end
20
+
21
+
22
+ # vim: syntax=Ruby
@@ -0,0 +1,187 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/geocoders')
2
+
3
+ module Geokit
4
+ module Geocoders
5
+ # -------------------------------------------------------------------------------------------
6
+ # Address geocoders that also provide reverse geocoding
7
+ # -------------------------------------------------------------------------------------------
8
+
9
+ # Google geocoder implementation. Requires the Geokit::Geocoders::GOOGLE variable to
10
+ # contain a Google API key. Conforms to the interface set by the Geocoder class.
11
+ class GoogleGeocoder < Geocoder
12
+
13
+ private
14
+
15
+ # Template method which does the reverse-geocode lookup.
16
+ def self.do_reverse_geocode(latlng)
17
+ latlng = LatLng.normalize(latlng)
18
+ url = "http://maps.google.com/maps/geo?ll=#{Geokit::Inflector.url_escape(latlng.ll)}&key=#{Geokit::Geocoders::google}&oe=utf-8"
19
+ res = call_geocoder_service(url)
20
+ return GeoLoc.new if res.nil?
21
+ toGeoLoc(res)
22
+ end
23
+
24
+ # Template method which does the geocode lookup.
25
+ #
26
+ # Supports viewport/country code biasing
27
+ #
28
+ # ==== OPTIONS
29
+ # * :bias - This option makes the Google Geocoder return results biased to a particular
30
+ # country or viewport. Country code biasing is achieved by passing the ccTLD
31
+ # ('uk' for .co.uk, for example) as a :bias value. For a list of ccTLD's,
32
+ # look here: http://en.wikipedia.org/wiki/CcTLD. By default, the geocoder
33
+ # will be biased to results within the US (ccTLD .com).
34
+ #
35
+ # If you'd like the Google Geocoder to prefer results within a given viewport,
36
+ # you can pass a Geokit::Bounds object as the :bias value.
37
+ #
38
+ # ==== EXAMPLES
39
+ # # By default, the geocoder will return Syracuse, NY
40
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse').country_code # => 'US'
41
+ # # With country code biasing, it returns Syracuse in Sicily, Italy
42
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Syracuse', :bias => :it).country_code # => 'IT'
43
+ #
44
+ # # By default, the geocoder will return Winnetka, IL
45
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka').state # => 'IL'
46
+ # # When biased to an bounding box around California, it will now return the Winnetka neighbourhood, CA
47
+ # bounds = Geokit::Bounds.normalize([34.074081, -118.694401], [34.321129, -118.399487])
48
+ # Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka', :bias => bounds).state # => 'CA'
49
+ #
50
+
51
+ GOOGLE_STATUS_CODES = {
52
+ 200 => "G_GEO_SUCCESS",
53
+ # InvalidStatusCodeError
54
+ 400 => "G_GEO_BAD_REQUEST",
55
+ 500 => "G_GEO_SERVER_ERROR",
56
+ 601 => "G_GEO_MISSING_QUERY",
57
+ # UnableToGeocodeError
58
+ 602 => "G_GEO_UNKNOWN_ADDRESS",
59
+ 603 => "G_GEO_UNAVAILABLE_ADDRESS",
60
+ 604 => "G_GEO_UNKNOWN_DIRECTIONS",
61
+ # BadKey
62
+ 610 => "G_GEO_BAD_KEY",
63
+ # TooManyQueriesError
64
+ 620 => "G_GEO_TOO_MANY_QUERIES",
65
+ }
66
+
67
+
68
+ def self.do_geocode(address, options = {})
69
+ bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
70
+ lang_str = options[:lang].to_s.empty? ? '' : "&hl=#{options[:lang]}"
71
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
72
+ return nil if address_str.to_s.strip.empty?
73
+ url = "http://maps.google.com/maps/geo?q=#{Geokit::Inflector.url_escape(address_str)}#{bias_str}#{lang_str}&key=#{Geokit::Geocoders::google}&oe=utf-8"
74
+ res = call_geocoder_service(url)
75
+ raise Geokit::InvalidResponseError if res.nil?
76
+ toGeoLoc(res)
77
+ end
78
+
79
+ def self.construct_bias_string_from_options(bias)
80
+ if bias.is_a?(String) or bias.is_a?(Symbol)
81
+ # country code biasing
82
+ "&gl=#{bias.to_s.downcase}"
83
+ elsif bias.is_a?(Bounds)
84
+ # viewport biasing
85
+ "&ll=#{bias.center.ll}&spn=#{bias.to_span.ll}"
86
+ end
87
+ end
88
+
89
+ def self.toGeoLoc(res)
90
+ response_code = res["Status"]["code"]
91
+ if response_code == 200
92
+ geoloc = nil
93
+ # Google can return multiple results as //Placemark elements.
94
+ # iterate through each and extract each placemark as a geoloc
95
+ res['Placemark'].each do |e|
96
+ extracted_geoloc = extract_placemark(e) # g is now an instance of GeoLoc
97
+ if geoloc.nil?
98
+ # first time through, geoloc is still nil, so we make it the geoloc we just extracted
99
+ geoloc = extracted_geoloc
100
+ else
101
+ # second (and subsequent) iterations, we push additional
102
+ # geolocs onto "geoloc.all"
103
+ geoloc.all.push(extracted_geoloc)
104
+ end
105
+ end
106
+ return geoloc
107
+ else
108
+ if [602, 603, 604].include?(response_code)
109
+ # G_GEO_UNKNOWN_ADDRESS, G_GEO_UNAVAILABLE_ADDRESS, G_GEO_UNKNOWN_DIRECTIONS
110
+ return nil
111
+ elsif [400, 500, 601].include?(response_code)
112
+ # G_GEO_BAD_REQUEST, G_GEO_SERVER_ERROR, G_GEO_MISSING_QUERY, G_GEO_BAD_KEY
113
+ raise Geokit::InvalidStatusCodeError.new(GOOGLE_STATUS_CODES[response_code])
114
+ elsif response_code == 620
115
+ # G_GEO_TOO_MANY_QUERIES
116
+ raise Geokit::TooManyQueriesError.new(GOOGLE_STATUS_CODES[response_code])
117
+ elsif response_code == 610
118
+ # G_GEO_BAD_KEY
119
+ raise Geokit::BadKeyError.new(GOOGLE_STATUS_CODES[response_code])
120
+ else
121
+ raise Geokit::GeokitError
122
+ end
123
+ end
124
+ #rescue => e
125
+ logger.error "Caught an error during Google geocoding call: #{e.inspect}"
126
+ return GeoLoc.new
127
+ end
128
+
129
+ # extracts a single geoloc from a //placemark element in the google results xml
130
+ def self.extract_placemark(placemark)
131
+ res = GeoLoc.new
132
+ res.provider = 'google'
133
+
134
+ coordinates = placemark["Point"]['coordinates']
135
+ res.lat = coordinates[1]
136
+ res.lng = coordinates[0]
137
+
138
+ address_details = placemark['AddressDetails']
139
+ if country = address_details['Country']
140
+ res.country_code = country['CountryNameCode']
141
+ res.country = country['CountryName']
142
+ if area = country['AdministrativeArea']
143
+ res.state = area['AdministrativeAreaName']
144
+ if subarea = area['SubAdministrativeArea']
145
+ locality = subarea['Locality']
146
+ res.province = subarea['SubAdministrativeAreaName']
147
+ end
148
+ locality ||= area['Locality']
149
+ if locality
150
+ street = locality['Thoroughfare']
151
+ postal_code = locality['PostalCode']
152
+ dependant_locality = locality["DependentLocality"]
153
+ if dependant_locality
154
+ res.district = dependant_locality['DependentLocalityName']
155
+ street ||= dependant_locality['Thoroughfare']
156
+ postal_code ||= dependant_locality['PostalCode']
157
+ end
158
+ res.city = locality['LocalityName']
159
+ end
160
+ end
161
+ street ||= country['Thoroughfare']
162
+ postal_code ||= country['PostalCode']
163
+ res.street_address = street['ThoroughfareName'] if street
164
+ res.zip = postal_code['PostalCodeNumber'] if postal_code
165
+ end
166
+ res.full_address = placemark['address']
167
+
168
+ # Translate accuracy into Yahoo-style token address, street, zip, zip+4, city, state, country
169
+ # For Google, 1=low accuracy, 8=high accuracy
170
+
171
+ res.accuracy = address_details ? address_details['Accuracy'].to_i : 0
172
+ res.precision = %w{unknown country state state city zip zip+4 street address building}[res.accuracy]
173
+
174
+ # google returns a set of suggested boundaries for the geocoded result
175
+ extended_data = placemark['ExtendedData']
176
+ if extended_data && (suggested_bounds = extended_data['LatLonBox'])
177
+ res.suggested_bounds = Bounds.normalize(
178
+ [suggested_bounds['south'], suggested_bounds['west']],
179
+ [suggested_bounds['north'], suggested_bounds['east']])
180
+ end
181
+ res.success = true
182
+
183
+ res
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,119 @@
1
+ require 'timeout'
2
+ require 'logger'
3
+ require 'yajl'
4
+ require 'uri'
5
+ require 'yajl/version'
6
+ require 'yajl/http_stream'
7
+ require 'i18n'
8
+ require 'active_support'
9
+ require 'active_support/inflector'
10
+
11
+ module Geokit
12
+
13
+ class GeokitError < StandardError; end
14
+ class BadKeyError < GeokitError; end
15
+ class GeocoderServiceError < GeokitError; end
16
+ class TimeoutError < GeocoderServiceError; end
17
+ class InvalidResponseError < GeocoderServiceError; end
18
+ class InvalidStatusCodeError < GeocoderServiceError; end
19
+ class TooManyQueriesError < GeocoderServiceError; end
20
+
21
+ module Geocoders
22
+ @@request_timeout = nil
23
+ @@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
24
+ @@logger = Logger.new(STDOUT)
25
+ @@logger.level = Logger::INFO
26
+ @@domain = nil
27
+
28
+ def self.__define_accessors
29
+ class_variables.each do |v|
30
+ sym = v.to_s.delete("@").to_sym
31
+ unless self.respond_to? sym
32
+ module_eval <<-EOS, __FILE__, __LINE__
33
+ def self.#{sym}
34
+ value = if defined?(#{sym.to_s.upcase})
35
+ #{sym.to_s.upcase}
36
+ else
37
+ @@#{sym}
38
+ end
39
+ if value.is_a?(Hash)
40
+ value = (self.domain.nil? ? nil : value[self.domain]) || value.values.first
41
+ end
42
+ value
43
+ end
44
+
45
+ def self.#{sym}=(obj)
46
+ @@#{sym} = obj
47
+ end
48
+ EOS
49
+ end
50
+ end
51
+ end
52
+
53
+ __define_accessors
54
+
55
+ # -------------------------------------------------------------------------------------------
56
+ # Geocoder Base class -- every geocoder should inherit from this
57
+ # -------------------------------------------------------------------------------------------
58
+
59
+ # The Geocoder base class which defines the interface to be used by all
60
+ # other geocoders.
61
+ class Geocoder
62
+
63
+ # Main method which calls the do_geocode template method which subclasses
64
+ # are responsible for implementing. Returns a populated GeoLoc or an
65
+ # nil one with a failed success code.
66
+ def self.geocode(address, options = {})
67
+ if address.is_a?(String)
68
+ converted_address = ActiveSupport::Inflector.transliterate(address)
69
+ else
70
+ converted_address = address
71
+ end
72
+ do_geocode(converted_address, options)
73
+ end
74
+ # Main method which calls the do_reverse_geocode template method which subclasses
75
+ # are responsible for implementing. Returns a populated GeoLoc or an
76
+ # nil one with a failed success code.
77
+ def self.reverse_geocode(latlng)
78
+ do_reverse_geocode(latlng)
79
+ end
80
+
81
+ # Call the geocoder service using the timeout if configured.
82
+ def self.call_geocoder_service(url)
83
+ Timeout::timeout(Geokit::Geocoders::request_timeout) { return self.do_get(url) } if Geokit::Geocoders::request_timeout
84
+ logger.info "Getting geocode from #{url}"
85
+ return self.do_get(url)
86
+ rescue TimeoutError, Timeout::Error
87
+ raise Geokit::TimeoutError
88
+ rescue Yajl::HttpStream::HttpError
89
+ raise Geokit::InvalidResponseError
90
+ end
91
+
92
+ # Not all geocoders can do reverse geocoding. So, unless the subclass explicitly overrides this method,
93
+ # a call to reverse_geocode will return an empty GeoLoc. If you happen to be using MultiGeocoder,
94
+ # this will cause it to failover to the next geocoder, which will hopefully be one which supports reverse geocoding.
95
+ def self.do_reverse_geocode(latlng)
96
+ return nil
97
+ end
98
+
99
+ protected
100
+
101
+ def self.logger()
102
+ Geokit::Geocoders::logger
103
+ end
104
+
105
+ private
106
+
107
+ # Wraps the geocoder call around a proxy if necessary.
108
+ def self.do_get(url)
109
+ uri = URI.parse(url)
110
+ result = nil
111
+ Yajl::HttpStream.get(uri, {:check_utf8 => false}) do |data|
112
+ result = data
113
+ end
114
+ result
115
+ end
116
+
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,41 @@
1
+ module Geokit
2
+
3
+ module Inflector
4
+
5
+ extend self
6
+
7
+ def titleize(word)
8
+ humanize(underscore(word)).gsub(/\b([a-z])/u) { $1.capitalize }
9
+ end
10
+
11
+ def underscore(camel_cased_word)
12
+ camel_cased_word.to_s.gsub(/::/, '/').
13
+ gsub(/([A-Z]+)([A-Z][a-z])/u,'\1_\2').
14
+ gsub(/([a-z\d])([A-Z])/u,'\1_\2').
15
+ tr("-", "_").
16
+ downcase
17
+ end
18
+
19
+ def humanize(lower_case_and_underscored_word)
20
+ lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
21
+ end
22
+
23
+ def snake_case(s)
24
+ return s.downcase if s =~ /^[A-Z]+$/u
25
+ s.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/u, '_\&') =~ /_*(.*)/
26
+ return $+.downcase
27
+
28
+ end
29
+
30
+ def url_escape(s)
31
+ s.gsub(/([^ a-zA-Z0-9_.-]+)/nu) do
32
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
33
+ end.tr(' ', '+')
34
+ end
35
+
36
+ def camelize(str)
37
+ str.split('_').map {|w| w.capitalize}.join
38
+ end
39
+ end
40
+
41
+ end