geokit 1.8.5 → 1.9.0
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.
- 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
|