geokit 1.8.5 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.md +7 -0
- data/README.markdown +31 -14
- data/fixtures/vcr_cassettes/geonames_geocode.yml +1 -1
- data/fixtures/vcr_cassettes/geonames_geocode_premium.yml +42 -0
- data/fixtures/vcr_cassettes/google_country_code_biased_result_orly.yml +160 -0
- data/fixtures/vcr_cassettes/google_country_code_biased_result_toledo.yml +111 -0
- data/fixtures/vcr_cassettes/google_result_toledo_default_bias.yml +275 -0
- data/fixtures/vcr_cassettes/google_sublocality.yml +126 -0
- data/fixtures/vcr_cassettes/mapbox_forward_geocode.yml +59 -0
- data/fixtures/vcr_cassettes/mapbox_reverse_geocode.yml +63 -0
- data/fixtures/vcr_cassettes/opencage_city.yml +51 -0
- data/fixtures/vcr_cassettes/opencage_full.yml +65 -0
- data/fixtures/vcr_cassettes/opencage_language_response_es.yml +66 -0
- data/fixtures/vcr_cassettes/opencage_reverse_madrid.yml +51 -0
- data/fixtures/vcr_cassettes/opencage_reverse_prilep.yml +53 -0
- data/geokit.gemspec +5 -3
- data/lib/geokit/bounds.rb +40 -31
- data/lib/geokit/core_ext.rb +1 -1
- data/lib/geokit/geo_loc.rb +63 -41
- data/lib/geokit/geocoders.rb +13 -13
- data/lib/geokit/geocoders/bing.rb +9 -9
- data/lib/geokit/geocoders/ca_geocoder.rb +29 -29
- data/lib/geokit/geocoders/fcc.rb +4 -4
- data/lib/geokit/geocoders/free_geo_ip.rb +6 -7
- data/lib/geokit/geocoders/geo_plugin.rb +5 -6
- data/lib/geokit/geocoders/geocodio.rb +2 -2
- data/lib/geokit/geocoders/geonames.rb +18 -12
- data/lib/geokit/geocoders/google.rb +31 -30
- data/lib/geokit/geocoders/ip.rb +3 -4
- data/lib/geokit/geocoders/mapbox.rb +94 -0
- data/lib/geokit/geocoders/mapquest.rb +5 -5
- data/lib/geokit/geocoders/opencage.rb +93 -0
- data/lib/geokit/geocoders/openstreetmap.rb +9 -6
- data/lib/geokit/geocoders/ripe.rb +3 -3
- data/lib/geokit/geocoders/us_geocoder.rb +10 -9
- data/lib/geokit/geocoders/yahoo.rb +33 -34
- data/lib/geokit/geocoders/yandex.rb +17 -17
- data/lib/geokit/inflectors.rb +4 -10
- data/lib/geokit/lat_lng.rb +50 -26
- data/lib/geokit/mappable.rb +83 -83
- data/lib/geokit/multi_geocoder.rb +25 -20
- data/lib/geokit/net_adapter/net_http.rb +7 -4
- data/lib/geokit/polygon.rb +36 -4
- data/lib/geokit/version.rb +1 -1
- data/test/helper.rb +15 -13
- data/test/test_base_geocoder.rb +6 -7
- data/test/test_bing_geocoder.rb +20 -21
- data/test/test_bounds.rb +26 -28
- data/test/test_ca_geocoder.rb +9 -10
- data/test/test_fcc_geocoder.rb +1 -1
- data/test/test_free_geo_ip_geocoder.rb +1 -1
- data/test/test_geo_plugin_geocoder.rb +9 -9
- data/test/test_geoloc.rb +7 -6
- data/test/test_geonames_geocoder.rb +28 -6
- data/test/test_google_geocoder.rb +210 -176
- data/test/test_inflector.rb +0 -1
- data/test/test_ipgeocoder.rb +17 -18
- data/test/test_latlng.rb +105 -85
- data/test/test_map_quest.rb +18 -21
- data/test/test_mapbox_geocoder.rb +31 -0
- data/test/test_mappable.rb +46 -0
- data/test/test_maxmind_geocoder.rb +1 -3
- data/test/test_multi_geocoder.rb +8 -9
- data/test/test_multi_ip_geocoder.rb +3 -5
- data/test/test_net_adapter.rb +4 -4
- data/test/test_opencage_geocoder.rb +108 -0
- data/test/test_openstreetmap_geocoder.rb +62 -44
- data/test/{test_polygon_contains.rb → test_polygon.rb} +30 -20
- data/test/test_ripe_geocoder.rb +2 -0
- data/test/test_us_geocoder.rb +7 -8
- data/test/test_yahoo_geocoder.rb +20 -21
- data/test/test_yandex_geocoder.rb +34 -35
- metadata +79 -56
- data/fixtures/vcr_cassettes/google_country_code_biased_result.yml +0 -401
@@ -2,7 +2,6 @@ module Geokit
|
|
2
2
|
module Geocoders
|
3
3
|
# Open Street Map geocoder implementation.
|
4
4
|
class OSMGeocoder < Geocoder
|
5
|
-
|
6
5
|
private
|
7
6
|
|
8
7
|
# Template method which does the geocode lookup.
|
@@ -11,10 +10,12 @@ module Geokit
|
|
11
10
|
options_str << generate_param_for_option(:json_callback, options)
|
12
11
|
options_str << generate_param_for_option(:countrycodes, options)
|
13
12
|
options_str << generate_param_for_option(:viewbox, options)
|
13
|
+
options_str << generate_param_for_option(:'accept-language', options)
|
14
|
+
options_str << generate_param_for_option(:email, options)
|
14
15
|
|
15
16
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
16
17
|
|
17
|
-
url = "http://nominatim.openstreetmap.org/search?format=json#{options_str}&addressdetails=1&q=#{Geokit::Inflector
|
18
|
+
url = "http://nominatim.openstreetmap.org/search?format=json#{options_str}&addressdetails=1&q=#{Geokit::Inflector.url_escape(address_str)}"
|
18
19
|
process :json, url
|
19
20
|
end
|
20
21
|
|
@@ -22,6 +23,8 @@ module Geokit
|
|
22
23
|
latlng = LatLng.normalize(latlng)
|
23
24
|
options_str = generate_param_for(:lat, latlng.lat)
|
24
25
|
options_str << generate_param_for(:lon, latlng.lng)
|
26
|
+
options_str << generate_param_for_option(:'accept-language', options)
|
27
|
+
options_str << generate_param_for_option(:email, options)
|
25
28
|
options_str << generate_param_for_option(:zoom, options)
|
26
29
|
options_str << generate_param_for_option(:osm_type, options)
|
27
30
|
options_str << generate_param_for_option(:osm_id, options)
|
@@ -31,11 +34,11 @@ module Geokit
|
|
31
34
|
end
|
32
35
|
|
33
36
|
def self.generate_param_for(param, value)
|
34
|
-
"&#{param}=#{Geokit::Inflector
|
37
|
+
"&#{param}=#{Geokit::Inflector.url_escape(value.to_s)}"
|
35
38
|
end
|
36
39
|
|
37
40
|
def self.generate_param_for_option(param, options)
|
38
|
-
options[param] ? "&#{param}=#{Geokit::Inflector
|
41
|
+
options[param] ? "&#{param}=#{Geokit::Inflector.url_escape(options[param])}" : ''
|
39
42
|
end
|
40
43
|
|
41
44
|
def self.generate_bool_param_for_option(param, options)
|
@@ -80,7 +83,7 @@ module Geokit
|
|
80
83
|
return unless address_data
|
81
84
|
loc.country = address_data['country']
|
82
85
|
loc.country_code = address_data['country_code'].upcase if address_data['country_code']
|
83
|
-
loc.
|
86
|
+
loc.state_name = address_data['state']
|
84
87
|
loc.city = address_data['city']
|
85
88
|
loc.city = address_data['county'] if loc.city.nil? && address_data['county']
|
86
89
|
loc.zip = address_data['postcode']
|
@@ -93,7 +96,7 @@ module Geokit
|
|
93
96
|
|
94
97
|
def self.set_precision(result_json, loc)
|
95
98
|
# Todo accuracy does not work as Yahoo and Google maps on OSM
|
96
|
-
#loc.accuracy = %w{unknown amenity building highway historic landuse leisure natural place railway shop tourism waterway man_made}.index(loc.precision)
|
99
|
+
# loc.accuracy = %w{unknown amenity building highway historic landuse leisure natural place railway shop tourism waterway man_made}.index(loc.precision)
|
97
100
|
loc.precision = result_json['class']
|
98
101
|
loc.accuracy = result_json['type']
|
99
102
|
end
|
@@ -3,6 +3,7 @@ module Geokit
|
|
3
3
|
# Provides geocoding based upon an IP address. The underlying web service is geoplugin.net
|
4
4
|
class RipeGeocoder < BaseIpGeocoder
|
5
5
|
self.secure = false # supports HTTPS, but Net::HTTPS doesn't like the server
|
6
|
+
|
6
7
|
private
|
7
8
|
|
8
9
|
def self.do_geocode(ip)
|
@@ -27,9 +28,9 @@ module Geokit
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def self.set_address_components(data, loc)
|
30
|
-
match = data['country'].match
|
31
|
+
match = data['country'].match(/([A-Z]+)(\(([A-Z]+)\))?/)
|
31
32
|
if match[3]
|
32
|
-
loc.
|
33
|
+
loc.state_code = match[1]
|
33
34
|
loc.country_code = match[3]
|
34
35
|
else
|
35
36
|
loc.country_code = match[1]
|
@@ -38,6 +39,5 @@ module Geokit
|
|
38
39
|
loc.city = data['city']
|
39
40
|
end
|
40
41
|
end
|
41
|
-
|
42
42
|
end
|
43
43
|
end
|
@@ -2,32 +2,33 @@
|
|
2
2
|
# contain true or false based upon whether authentication is to occur. Conforms to the
|
3
3
|
# interface set by the Geocoder class.
|
4
4
|
module Geokit
|
5
|
-
|
5
|
+
module Geocoders
|
6
6
|
class UsGeocoder < Geocoder
|
7
7
|
config :key
|
8
8
|
|
9
9
|
private
|
10
|
+
|
10
11
|
def self.do_geocode(address)
|
11
12
|
process :csv, submit_url(address)
|
12
13
|
end
|
13
14
|
|
14
15
|
def self.submit_url(address)
|
15
16
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
16
|
-
query = (address_str =~ /^\d{5}(?:-\d{4})?$/ ?
|
17
|
-
base = key ? "http://#{key}@geocoder.us/member" :
|
17
|
+
query = (address_str =~ /^\d{5}(?:-\d{4})?$/ ? 'zip' : 'address') + "=#{Geokit::Inflector.url_escape(address_str)}"
|
18
|
+
base = key ? "http://#{key}@geocoder.us/member" : 'http://geocoder.us'
|
18
19
|
"#{base}/service/csv/geocode?#{query}"
|
19
20
|
end
|
20
21
|
|
21
22
|
def self.parse_csv(array)
|
22
23
|
loc = GeoLoc.new
|
23
24
|
if array.length == 5
|
24
|
-
loc.lat,loc.lng,loc.city,loc.state,loc.zip=array
|
25
|
-
loc.country_code='US'
|
26
|
-
loc.success=true
|
25
|
+
loc.lat, loc.lng, loc.city, loc.state, loc.zip = array
|
26
|
+
loc.country_code = 'US'
|
27
|
+
loc.success = true
|
27
28
|
elsif array.length == 6
|
28
|
-
loc.lat,loc.lng,loc.street_address,loc.city,loc.state,loc.zip=array
|
29
|
-
loc.country_code='US'
|
30
|
-
loc.success=true
|
29
|
+
loc.lat, loc.lng, loc.street_address, loc.city, loc.state, loc.zip = array
|
30
|
+
loc.country_code = 'US'
|
31
|
+
loc.success = true
|
31
32
|
end
|
32
33
|
loc
|
33
34
|
end
|
@@ -7,9 +7,10 @@ module Geokit
|
|
7
7
|
self.secure = true
|
8
8
|
|
9
9
|
private
|
10
|
+
|
10
11
|
def self.submit_url(address)
|
11
12
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
12
|
-
query_string = "?q=#{Geokit::Inflector
|
13
|
+
query_string = "?q=#{Geokit::Inflector.url_escape(address_str)}&flags=J"
|
13
14
|
|
14
15
|
o = OauthUtil.new
|
15
16
|
o.consumer_key = key
|
@@ -59,15 +60,15 @@ module Geokit
|
|
59
60
|
|
60
61
|
def self.set_precision(result_json, loc)
|
61
62
|
loc.precision = case result_json['quality'].to_i
|
62
|
-
when 9,10 then 'country'
|
63
|
+
when 9, 10 then 'country'
|
63
64
|
when 19..30 then 'state'
|
64
|
-
when 39,40 then 'city'
|
65
|
-
when 49,50 then 'neighborhood'
|
66
|
-
when 59,60,64 then 'zip'
|
67
|
-
when 74,75 then 'zip+4'
|
65
|
+
when 39, 40 then 'city'
|
66
|
+
when 49, 50 then 'neighborhood'
|
67
|
+
when 59, 60, 64 then 'zip'
|
68
|
+
when 74, 75 then 'zip+4'
|
68
69
|
when 70..72 then 'street'
|
69
70
|
when 80..87 then 'address'
|
70
|
-
when 62,63,90,99 then 'building'
|
71
|
+
when 62, 63, 90, 99 then 'building'
|
71
72
|
else 'unknown'
|
72
73
|
end
|
73
74
|
|
@@ -83,17 +84,16 @@ end
|
|
83
84
|
# Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
|
84
85
|
# License: http://gist.github.com/375593
|
85
86
|
# Usage: see example.rb below
|
86
|
-
|
87
|
+
|
87
88
|
require 'uri'
|
88
89
|
require 'cgi'
|
89
90
|
require 'openssl'
|
90
91
|
require 'base64'
|
91
|
-
|
92
|
+
|
92
93
|
class OauthUtil
|
93
|
-
|
94
|
-
attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
|
94
|
+
attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
|
95
95
|
:sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
|
96
|
-
|
96
|
+
|
97
97
|
def initialize
|
98
98
|
@consumer_key = ''
|
99
99
|
@consumer_secret = ''
|
@@ -104,37 +104,37 @@ class OauthUtil
|
|
104
104
|
@oauth_version = '1.0'
|
105
105
|
@callback_url = ''
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
# openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
|
109
109
|
# ref http://snippets.dzone.com/posts/show/491
|
110
110
|
def nonce
|
111
111
|
Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
def percent_encode( string )
|
115
115
|
# ref http://snippets.dzone.com/posts/show/1260
|
116
116
|
URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
|
117
117
|
end
|
118
|
-
|
118
|
+
|
119
119
|
# @ref http://oauth.net/core/1.0/#rfc.section.9.2
|
120
120
|
def signature
|
121
121
|
key = percent_encode( @consumer_secret ) + '&' + percent_encode( @token_secret )
|
122
|
-
|
122
|
+
|
123
123
|
# ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
|
124
|
-
digest = OpenSSL::Digest
|
124
|
+
digest = OpenSSL::Digest.new( 'sha1' )
|
125
125
|
hmac = OpenSSL::HMAC.digest( digest, key, @base_str )
|
126
|
-
|
126
|
+
|
127
127
|
# ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
|
128
128
|
Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
|
129
129
|
end
|
130
|
-
|
130
|
+
|
131
131
|
# sort (very important as it affects the signature), concat, and percent encode
|
132
132
|
# @ref http://oauth.net/core/1.0/#rfc.section.9.1.1
|
133
133
|
# @ref http://oauth.net/core/1.0/#9.2.1
|
134
134
|
# @ref http://oauth.net/core/1.0/#rfc.section.A.5.1
|
135
135
|
def query_string
|
136
136
|
pairs = []
|
137
|
-
@params.sort.each { | key, val |
|
137
|
+
@params.sort.each { | key, val |
|
138
138
|
pairs.push( "#{ percent_encode( key ) }=#{ percent_encode( val.to_s ) }" )
|
139
139
|
}
|
140
140
|
pairs.join '&'
|
@@ -143,10 +143,9 @@ class OauthUtil
|
|
143
143
|
def timestamp
|
144
144
|
Time.now.to_i.to_s
|
145
145
|
end
|
146
|
-
|
146
|
+
|
147
147
|
# organize params & create signature
|
148
148
|
def sign( parsed_url )
|
149
|
-
|
150
149
|
@params = {
|
151
150
|
'oauth_consumer_key' => @consumer_key,
|
152
151
|
'oauth_nonce' => nonce,
|
@@ -154,10 +153,10 @@ class OauthUtil
|
|
154
153
|
'oauth_timestamp' => timestamp,
|
155
154
|
'oauth_version' => @oauth_version
|
156
155
|
}
|
157
|
-
|
156
|
+
|
158
157
|
# if url has query, merge key/values into params obj overwriting defaults
|
159
158
|
if parsed_url.query
|
160
|
-
CGI.parse( parsed_url.query ).each do |k,v|
|
159
|
+
CGI.parse( parsed_url.query ).each do |k, v|
|
161
160
|
if v.is_a?(Array) && v.count == 1
|
162
161
|
@params[k] = v.first
|
163
162
|
else
|
@@ -165,24 +164,24 @@ class OauthUtil
|
|
165
164
|
end
|
166
165
|
end
|
167
166
|
end
|
168
|
-
|
167
|
+
|
169
168
|
# @ref http://oauth.net/core/1.0/#rfc.section.9.1.2
|
170
169
|
@req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
|
171
|
-
|
170
|
+
|
172
171
|
# create base str. make it an object attr for ez debugging
|
173
172
|
# ref http://oauth.net/core/1.0/#anchor14
|
174
|
-
@base_str = [
|
175
|
-
@req_method,
|
176
|
-
percent_encode( req_url ),
|
177
|
-
|
173
|
+
@base_str = [
|
174
|
+
@req_method,
|
175
|
+
percent_encode( req_url ),
|
176
|
+
|
178
177
|
# normalization is just x-www-form-urlencoded
|
179
|
-
percent_encode( query_string )
|
180
|
-
|
178
|
+
percent_encode( query_string )
|
179
|
+
|
181
180
|
].join( '&' )
|
182
|
-
|
181
|
+
|
183
182
|
# add signature
|
184
183
|
@params[ 'oauth_signature' ] = signature
|
185
|
-
|
184
|
+
|
186
185
|
self
|
187
186
|
end
|
188
187
|
end
|
@@ -14,7 +14,7 @@ module Geokit
|
|
14
14
|
|
15
15
|
def self.submit_url(address)
|
16
16
|
address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
|
17
|
-
url = "http://geocode-maps.yandex.ru/1.x/?geocode=#{Geokit::Inflector
|
17
|
+
url = "http://geocode-maps.yandex.ru/1.x/?geocode=#{Geokit::Inflector.url_escape(address_str)}&format=json"
|
18
18
|
url += "&key=#{key}" if key
|
19
19
|
url
|
20
20
|
end
|
@@ -22,18 +22,18 @@ module Geokit
|
|
22
22
|
def self.parse_json(result)
|
23
23
|
loc = new_loc
|
24
24
|
|
25
|
-
coll = result[
|
26
|
-
return loc unless coll[
|
25
|
+
coll = result['response']['GeoObjectCollection']
|
26
|
+
return loc unless coll['metaDataProperty']['GeocoderResponseMetaData']['found'].to_i > 0
|
27
27
|
|
28
|
-
l = coll[
|
28
|
+
l = coll['featureMember'][0]['GeoObject']
|
29
29
|
|
30
30
|
loc.success = true
|
31
|
-
ll = l[
|
31
|
+
ll = l['Point']['pos'].split(' ')
|
32
32
|
loc.lng = ll.first
|
33
33
|
loc.lat = ll.last
|
34
34
|
|
35
|
-
country = l[
|
36
|
-
locality = country[
|
35
|
+
country = l['metaDataProperty']['GeocoderMetaData']['AddressDetails']['Country']
|
36
|
+
locality = country['Locality'] || country['AdministrativeArea']['Locality'] || country['AdministrativeArea']['SubAdministrativeArea']['Locality'] rescue nil
|
37
37
|
set_address_components(loc, l, country, locality)
|
38
38
|
set_precision(loc, l, locality)
|
39
39
|
|
@@ -41,19 +41,19 @@ module Geokit
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.set_address_components(loc, l, country, locality)
|
44
|
-
loc.country_code = country[
|
45
|
-
loc.full_address = country[
|
46
|
-
loc.street_address = l[
|
47
|
-
loc.street_number = locality[
|
48
|
-
loc.street_name = locality[
|
49
|
-
loc.city = locality[
|
50
|
-
loc.
|
51
|
-
loc.state ||= country[
|
44
|
+
loc.country_code = country['CountryNameCode']
|
45
|
+
loc.full_address = country['AddressLine']
|
46
|
+
loc.street_address = l['name']
|
47
|
+
loc.street_number = locality['Thoroughfare']['Premise']['PremiseNumber'] rescue nil
|
48
|
+
loc.street_name = locality['Thoroughfare']['ThoroughfareName'] rescue nil
|
49
|
+
loc.city = locality['LocalityName'] rescue nil
|
50
|
+
loc.state_name = country['AdministrativeArea']['AdministrativeAreaName'] rescue nil
|
51
|
+
loc.state ||= country['Locality']['LocalityName'] rescue nil
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.set_precision(loc, l, locality)
|
55
|
-
loc.precision = l[
|
56
|
-
loc.precision =
|
55
|
+
loc.precision = l['metaDataProperty']['GeocoderMetaData']['precision'].sub(/exact/, 'building').sub(/number|near/, 'address').sub(/other/, 'city')
|
56
|
+
loc.precision = 'country' unless locality
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
data/lib/geokit/inflectors.rb
CHANGED
@@ -10,20 +10,14 @@ module Geokit
|
|
10
10
|
|
11
11
|
def underscore(camel_cased_word)
|
12
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(
|
13
|
+
gsub(/([A-Z]+)([A-Z][a-z])/u, '\1_\2').
|
14
|
+
gsub(/([a-z\d])([A-Z])/u, '\1_\2').
|
15
|
+
tr('-', '_').
|
16
16
|
downcase
|
17
17
|
end
|
18
18
|
|
19
19
|
def humanize(lower_case_and_underscored_word)
|
20
|
-
lower_case_and_underscored_word.to_s.gsub(/_id$/,
|
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
|
-
$+.downcase
|
20
|
+
lower_case_and_underscored_word.to_s.gsub(/_id$/, '').gsub(/_/, ' ').capitalize
|
27
21
|
end
|
28
22
|
|
29
23
|
def url_escape(s)
|
data/lib/geokit/lat_lng.rb
CHANGED
@@ -4,9 +4,18 @@ module Geokit
|
|
4
4
|
|
5
5
|
attr_accessor :lat, :lng
|
6
6
|
|
7
|
+
# Provide users with the ability to use :latitude and :longitude
|
8
|
+
# to access the lat/lng instance variables.
|
9
|
+
# Alias the attr_accessor :lat to :latitude
|
10
|
+
alias_method :latitude, :lat
|
11
|
+
alias_method :latitude=, :lat=
|
12
|
+
# Alias the attr_accessor :lng to :longitude
|
13
|
+
alias_method :longitude, :lng
|
14
|
+
alias_method :longitude=, :lng=
|
15
|
+
|
7
16
|
# Accepts latitude and longitude or instantiates an empty instance
|
8
17
|
# if lat and lng are not provided. Converted to floats if provided
|
9
|
-
def initialize(lat=nil, lng=nil)
|
18
|
+
def initialize(lat = nil, lng = nil)
|
10
19
|
lat = lat.to_f if lat && !lat.is_a?(Numeric)
|
11
20
|
lng = lng.to_f if lng && !lng.is_a?(Numeric)
|
12
21
|
@lat = lat
|
@@ -24,7 +33,7 @@ module Geokit
|
|
24
33
|
|
25
34
|
# Longitude attribute setter; stored as a float;
|
26
35
|
def lng=(lng)
|
27
|
-
@lng=lng.to_f if lng
|
36
|
+
@lng = lng.to_f if lng
|
28
37
|
end
|
29
38
|
|
30
39
|
# Returns the lat and lng attributes as a comma-separated string.
|
@@ -42,17 +51,19 @@ module Geokit
|
|
42
51
|
self.class.decimal_to_dms(lng)
|
43
52
|
end
|
44
53
|
|
45
|
-
#returns a string with comma-separated lat,lng values
|
54
|
+
# returns a string with comma-separated lat,lng values
|
46
55
|
def to_s
|
47
56
|
ll
|
48
57
|
end
|
49
58
|
|
50
|
-
#returns a two-element array
|
59
|
+
# returns a two-element array
|
51
60
|
def to_a
|
52
|
-
[lat,lng]
|
61
|
+
[lat, lng]
|
53
62
|
end
|
54
|
-
|
55
|
-
#
|
63
|
+
|
64
|
+
# Returns true if the candidate object is logically equal. Logical
|
65
|
+
# equivalence is true if the lat and lng attributes are the same for both
|
66
|
+
# objects.
|
56
67
|
def ==(other)
|
57
68
|
return false unless other.is_a?(LatLng)
|
58
69
|
lat == other.lat && lng == other.lng
|
@@ -71,23 +82,26 @@ module Geokit
|
|
71
82
|
lat && lng
|
72
83
|
end
|
73
84
|
|
74
|
-
# A *class* method to take anything which can be inferred as a point and
|
75
|
-
# a LatLng from it. You should use this anything you're not sure
|
76
|
-
# and want to deal with it as a LatLng if at all
|
85
|
+
# A *class* method to take anything which can be inferred as a point and
|
86
|
+
# generate a LatLng from it. You should use this anything you're not sure
|
87
|
+
# what the input is, and want to deal with it as a LatLng if at all
|
88
|
+
# possible. Can take:
|
77
89
|
# 1) two arguments (lat,lng)
|
78
90
|
# 2) a string in the format "37.1234,-129.1234" or "37.1234 -129.1234"
|
79
91
|
# 3) a string which can be geocoded on the fly
|
80
92
|
# 4) an array in the format [37.1234,-129.1234]
|
81
93
|
# 5) a LatLng or GeoLoc (which is just passed through as-is)
|
82
|
-
# 6) anything responding to to_lat_lng -- a LatLng will be extracted from
|
83
|
-
|
94
|
+
# 6) anything responding to to_lat_lng -- a LatLng will be extracted from
|
95
|
+
# it
|
96
|
+
def self.normalize(thing, other = nil)
|
84
97
|
return Geokit::LatLng.new(thing, other) if other
|
85
98
|
|
86
99
|
case thing
|
87
100
|
when String
|
88
101
|
from_string(thing)
|
89
102
|
when Array
|
90
|
-
thing.size == 2 or raise ArgumentError.new(
|
103
|
+
thing.size == 2 or raise ArgumentError.new(
|
104
|
+
'Must initialize with an Array with both latitude and longitude')
|
91
105
|
Geokit::LatLng.new(thing[0], thing[1])
|
92
106
|
when LatLng # will also be true for GeoLocs
|
93
107
|
thing
|
@@ -95,14 +109,16 @@ module Geokit
|
|
95
109
|
if thing.respond_to? :to_lat_lng
|
96
110
|
thing.to_lat_lng
|
97
111
|
else
|
98
|
-
raise ArgumentError.new(
|
112
|
+
raise ArgumentError.new(
|
113
|
+
"#{thing} (#{thing.class}) cannot be normalized to a LatLng. " +
|
114
|
+
"We tried interpreting it as an array, string, etc., but no dice.")
|
99
115
|
end
|
100
116
|
end
|
101
117
|
end
|
102
118
|
|
103
119
|
def self.from_string(thing)
|
104
120
|
thing.strip!
|
105
|
-
if match=thing.match(/(\-?\d+\.?\d*)[, ] ?(\-?\d+\.?\d*)$/)
|
121
|
+
if match = thing.match(/(\-?\d+\.?\d*)[, ] ?(\-?\d+\.?\d*)$/)
|
106
122
|
Geokit::LatLng.new(match[1], match[2])
|
107
123
|
else
|
108
124
|
res = Geokit::Geocoders::MultiGeocoder.geocode(thing)
|
@@ -111,22 +127,30 @@ module Geokit
|
|
111
127
|
end
|
112
128
|
end
|
113
129
|
|
114
|
-
# Reverse geocodes a LatLng object using the MultiGeocoder (default), or
|
115
|
-
# using a geocoder of your choosing. Returns a new Geokit::GeoLoc
|
130
|
+
# Reverse geocodes a LatLng object using the MultiGeocoder (default), or
|
131
|
+
# optionally using a geocoder of your choosing. Returns a new Geokit::GeoLoc
|
132
|
+
# object
|
116
133
|
#
|
117
134
|
# ==== Options
|
118
|
-
# * :using - Specifies the geocoder to use for reverse geocoding. Defaults
|
119
|
-
# MultiGeocoder. Can be either the geocoder class (or any
|
120
|
-
# implements do_reverse_geocode for that matter), or
|
121
|
-
# the class without the "Geocoder" part
|
135
|
+
# * :using - Specifies the geocoder to use for reverse geocoding. Defaults
|
136
|
+
# to MultiGeocoder. Can be either the geocoder class (or any
|
137
|
+
# class that implements do_reverse_geocode for that matter), or
|
138
|
+
# the name of the class without the "Geocoder" part
|
139
|
+
# (e.g. :google)
|
122
140
|
#
|
123
141
|
# ==== Examples
|
124
|
-
# LatLng.new(51.4578329, 7.0166848).reverse_geocode
|
125
|
-
#
|
126
|
-
# LatLng.new(51.4578329, 7.0166848).reverse_geocode(:using =>
|
127
|
-
|
142
|
+
# LatLng.new(51.4578329, 7.0166848).reverse_geocode
|
143
|
+
# => #<Geokit::GeoLoc:0x12dac20 @state...>
|
144
|
+
# LatLng.new(51.4578329, 7.0166848).reverse_geocode(:using => :google)
|
145
|
+
# => #<Geokit::GeoLoc:0x12dac20 @state...>
|
146
|
+
# LatLng.new(51.4578329, 7.0166848).reverse_geocode(:using =>
|
147
|
+
# Geokit::Geocoders::GoogleGeocoder)
|
148
|
+
# => #<Geokit::GeoLoc:0x12dac20 @state...>
|
149
|
+
def reverse_geocode(options = { using: Geokit::Geocoders::MultiGeocoder })
|
128
150
|
if options[:using].is_a?(String) || options[:using].is_a?(Symbol)
|
129
|
-
|
151
|
+
class_name =
|
152
|
+
"#{Geokit::Inflector.camelize(options[:using].to_s)}Geocoder"
|
153
|
+
provider = Geokit::Geocoders.const_get(class_name)
|
130
154
|
elsif options[:using].respond_to?(:do_reverse_geocode)
|
131
155
|
provider = options[:using]
|
132
156
|
else
|