geocoder 1.1.4 → 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of geocoder might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +9 -0
- data/README.md +24 -10
- data/lib/geocoder/configuration.rb +1 -1
- data/lib/geocoder/lookups/base.rb +35 -16
- data/lib/geocoder/lookups/bing.rb +1 -1
- data/lib/geocoder/lookups/freegeoip.rb +1 -1
- data/lib/geocoder/lookups/geocoder_ca.rb +1 -1
- data/lib/geocoder/lookups/mapquest.rb +25 -4
- data/lib/geocoder/lookups/nominatim.rb +1 -1
- data/lib/geocoder/lookups/yahoo.rb +26 -42
- data/lib/geocoder/lookups/yandex.rb +1 -1
- data/lib/geocoder/models/mongoid.rb +1 -1
- data/lib/geocoder/query.rb +7 -1
- data/lib/geocoder/results/base.rb +8 -2
- data/lib/geocoder/results/mapquest.rb +46 -2
- data/lib/geocoder/results/yahoo.rb +9 -2
- data/lib/geocoder/stores/active_record.rb +14 -9
- data/lib/geocoder/stores/base.rb +1 -1
- data/lib/geocoder/version.rb +1 -1
- data/lib/oauth_util.rb +106 -0
- data/test/cache_test.rb +19 -0
- data/test/error_handling_test.rb +4 -0
- data/test/fixtures/mapquest_madison_square_garden.json +50 -25
- data/test/fixtures/mapquest_no_results.json +7 -1
- data/test/fixtures/yahoo_error.json +1 -0
- data/test/fixtures/yahoo_madison_square_garden.json +48 -48
- data/test/fixtures/yahoo_no_results.json +7 -7
- data/test/lookup_test.rb +1 -6
- data/test/near_test.rb +11 -0
- data/test/query_test.rb +5 -0
- data/test/result_test.rb +1 -0
- data/test/services_test.rb +43 -23
- data/test/test_helper.rb +46 -15
- metadata +8 -6
- data/test/fixtures/yahoo_v1_madison_square_garden.json +0 -46
- data/test/fixtures/yahoo_v1_no_results.json +0 -10
data/CHANGELOG.rdoc
CHANGED
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
Per-release changes to Geocoder.
|
4
4
|
|
5
|
+
== 1.1.5 (2012 Nov 9)
|
6
|
+
|
7
|
+
* Replace support for old Yahoo Placefinder with Yahoo BOSS (thanks github.com/pwoltman).
|
8
|
+
* Add support for actual Mapquest API (was previously just a proxy for Nominatim), including the paid service (thanks github.com/jedschneider).
|
9
|
+
* Add support for :select => :id_only option to near scope.
|
10
|
+
* Treat a given query as blank (don't do a lookup) if coordinates are given but latitude or longitude is nil.
|
11
|
+
* Speed up 'near' queries by adding bounding box condition (thanks github.com/mlandauer).
|
12
|
+
* Fix: don't redefine Object#hash in Yahoo result object (thanks github.com/m0thman).
|
13
|
+
|
5
14
|
== 1.1.4 (2012 Oct 2)
|
6
15
|
|
7
16
|
* Deprecate Geocoder::Result::Nominatim#class and #type methods. Use #place_class and #place_type instead.
|
data/README.md
CHANGED
@@ -263,7 +263,7 @@ By default Geocoder uses Google's geocoding API to fetch coordinates and street
|
|
263
263
|
Geocoder.configure do |config|
|
264
264
|
|
265
265
|
# geocoding service (see below for supported options):
|
266
|
-
config.lookup = :
|
266
|
+
config.lookup = :yandex
|
267
267
|
|
268
268
|
# to use an API key:
|
269
269
|
config.api_key = "..."
|
@@ -304,17 +304,19 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
|
|
304
304
|
* **Limitations**: "You must not use or display the Content without a corresponding Google map, unless you are explicitly permitted to do so in the Maps APIs Documentation, or through written permission from Google." "You must not pre-fetch, cache, or store any Content, except that you may store: (i) limited amounts of Content for the purpose of improving the performance of your Maps API Implementation..."
|
305
305
|
* **Notes**: To use Google Premier set `Geocoder::Configuration.lookup = :google_premier` and `Geocoder::Configuration.api_key = [key, client, channel]`.
|
306
306
|
|
307
|
-
#### Yahoo (`:yahoo`)
|
307
|
+
#### Yahoo BOSS (`:yahoo`)
|
308
308
|
|
309
|
-
|
310
|
-
|
311
|
-
* **
|
309
|
+
Yahoo BOSS is **not a free service**. As of November 17, 2012 Yahoo no longer offers a free geocoding API.
|
310
|
+
|
311
|
+
* **API key**: requires OAuth consumer key and secret (set `Geocoder::Configuration.api_key = [key, secret]`)
|
312
|
+
* **Key signup**: http://developer.yahoo.com/boss/geo/
|
313
|
+
* **Quota**: unlimited, but subject to usage fees
|
312
314
|
* **Region**: world
|
313
315
|
* **SSL support**: no
|
314
|
-
* **Languages**:
|
315
|
-
* **Documentation**: http://developer.yahoo.com/geo/
|
316
|
-
* **Terms of Service**: http://info.yahoo.com/legal/us/yahoo/
|
317
|
-
* **Limitations**:
|
316
|
+
* **Languages**: en, fr, de, it, es, pt, nl, zh, ja, ko
|
317
|
+
* **Documentation**: http://developer.yahoo.com/boss/geo/docs/index.html
|
318
|
+
* **Terms of Service**: http://info.yahoo.com/legal/us/yahoo/boss/tou/?pir=ucJPcJ1ibUn.h.d.lVmlcbcEkoHjwJ_PvxG9SLK9VIbIQAw1XFrnDqY-
|
319
|
+
* **Limitations**: No mass downloads, no commercial map production based on the data, no storage of data except for caching.
|
318
320
|
|
319
321
|
#### Bing (`:bing`)
|
320
322
|
|
@@ -363,8 +365,11 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
|
|
363
365
|
|
364
366
|
#### Mapquest (`:mapquest`)
|
365
367
|
|
366
|
-
* **API key**:
|
368
|
+
* **API key**: required for the licensed API, do not use for open tier
|
367
369
|
* **Quota**: ?
|
370
|
+
* **HTTP Headers**: in order to use the licensed API you can configure the http_headers to include a referer as so:
|
371
|
+
`Geocoder::Configuration.http_headers = { "Referer" => "http://foo.com" }`
|
372
|
+
You can also allow a blank referer from the API management console via mapquest but it is potentially a security risk that someone else could use your API key from another domain.
|
368
373
|
* **Region**: world
|
369
374
|
* **SSL support**: no
|
370
375
|
* **Languages**: English
|
@@ -524,6 +529,15 @@ Calling `obj.coordinates` directly returns the internal representation of the co
|
|
524
529
|
|
525
530
|
For consistency with the rest of Geocoder, always use the `to_coordinates` method instead.
|
526
531
|
|
532
|
+
Optimisation of Distance Queries
|
533
|
+
--------------------------------
|
534
|
+
|
535
|
+
In MySQL and Postgres the finding of objects near a given point is speeded up by using a bounding box to limit the number of points over which a full distance calculation needs to be done.
|
536
|
+
|
537
|
+
To take advantage of this optimisation you need to add a composite index on latitude and longitude. In your Rails migration:
|
538
|
+
|
539
|
+
add_index :table, [:latitude, :longitude]
|
540
|
+
|
527
541
|
|
528
542
|
Distance Queries in SQLite
|
529
543
|
--------------------------
|
@@ -90,6 +90,16 @@ module Geocoder
|
|
90
90
|
fail
|
91
91
|
end
|
92
92
|
|
93
|
+
##
|
94
|
+
# Key to use for caching a geocoding result. Usually this will be the
|
95
|
+
# request URL, but in cases where OAuth is used and the nonce,
|
96
|
+
# timestamp, etc varies from one request to another, we need to use
|
97
|
+
# something else (like the URL before OAuth encoding).
|
98
|
+
#
|
99
|
+
def cache_key(query)
|
100
|
+
query_url(query)
|
101
|
+
end
|
102
|
+
|
93
103
|
##
|
94
104
|
# Class of the result objects
|
95
105
|
#
|
@@ -144,25 +154,34 @@ module Geocoder
|
|
144
154
|
end
|
145
155
|
|
146
156
|
##
|
147
|
-
#
|
157
|
+
# Fetch a raw geocoding result (JSON string).
|
158
|
+
# The result might or might not be cached.
|
148
159
|
#
|
149
160
|
def fetch_raw_data(query)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
response = client.get(uri.request_uri, Geocoder::Configuration.http_headers)
|
159
|
-
body = response.body
|
160
|
-
if cache and (200..399).include?(response.code.to_i)
|
161
|
-
cache[url] = body
|
162
|
-
end
|
163
|
-
@cache_hit = false
|
161
|
+
key = cache_key(query)
|
162
|
+
if cache and body = cache[key]
|
163
|
+
@cache_hit = true
|
164
|
+
else
|
165
|
+
response = make_api_request(query)
|
166
|
+
body = response.body
|
167
|
+
if cache and (200..399).include?(response.code.to_i)
|
168
|
+
cache[key] = body
|
164
169
|
end
|
165
|
-
|
170
|
+
@cache_hit = false
|
171
|
+
end
|
172
|
+
body
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Make an HTTP(S) request to a geocoding API and
|
177
|
+
# return the response object.
|
178
|
+
#
|
179
|
+
def make_api_request(query)
|
180
|
+
timeout(Geocoder::Configuration.timeout) do
|
181
|
+
uri = URI.parse(query_url(query))
|
182
|
+
client = http_client.new(uri.host, uri.port)
|
183
|
+
client.use_ssl = true if Geocoder::Configuration.use_https
|
184
|
+
client.get(uri.request_uri, Geocoder::Configuration.http_headers)
|
166
185
|
end
|
167
186
|
end
|
168
187
|
|
@@ -29,7 +29,7 @@ module Geocoder::Lookup
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def query_url(query)
|
32
|
-
"
|
32
|
+
"#{protocol}://dev.virtualearth.net/REST/v1/Locations" +
|
33
33
|
(query.reverse_geocode? ? "/#{query.sanitized_text}?" : "?") +
|
34
34
|
url_query_string(query)
|
35
35
|
end
|
@@ -1,15 +1,36 @@
|
|
1
|
+
require 'cgi'
|
1
2
|
require 'geocoder/lookups/base'
|
2
|
-
require "geocoder/lookups/nominatim"
|
3
3
|
require "geocoder/results/mapquest"
|
4
4
|
|
5
5
|
module Geocoder::Lookup
|
6
|
-
class Mapquest <
|
6
|
+
class Mapquest < Base
|
7
7
|
|
8
8
|
private # ---------------------------------------------------------------
|
9
9
|
|
10
10
|
def query_url(query)
|
11
|
-
|
12
|
-
|
11
|
+
key = Geocoder::Configuration.api_key
|
12
|
+
domain = key ? "www" : "open"
|
13
|
+
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
|
14
|
+
url + url_query_string(query)
|
13
15
|
end
|
16
|
+
|
17
|
+
def search_type(query)
|
18
|
+
query.reverse_geocode? ? "reverse" : "address"
|
19
|
+
end
|
20
|
+
|
21
|
+
def query_url_params(query)
|
22
|
+
key = Geocoder::Configuration.api_key
|
23
|
+
params = { :location => query.sanitized_text }
|
24
|
+
if key
|
25
|
+
params[:key] = CGI.unescape(key)
|
26
|
+
end
|
27
|
+
super.merge(params)
|
28
|
+
end
|
29
|
+
|
30
|
+
def results(query)
|
31
|
+
return [] unless doc = fetch_data(query)
|
32
|
+
doc["results"][0]['locations']
|
33
|
+
end
|
34
|
+
|
14
35
|
end
|
15
36
|
end
|
@@ -34,7 +34,7 @@ module Geocoder::Lookup
|
|
34
34
|
|
35
35
|
def query_url(query)
|
36
36
|
method = query.reverse_geocode? ? "reverse" : "search"
|
37
|
-
"
|
37
|
+
"#{protocol}://nominatim.openstreetmap.org/#{method}?" + url_query_string(query)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'geocoder/lookups/base'
|
2
2
|
require "geocoder/results/yahoo"
|
3
|
+
require 'oauth_util'
|
3
4
|
|
4
5
|
module Geocoder::Lookup
|
5
6
|
class Yahoo < Base
|
@@ -12,47 +13,16 @@ module Geocoder::Lookup
|
|
12
13
|
|
13
14
|
def results(query)
|
14
15
|
return [] unless doc = fetch_data(query)
|
15
|
-
doc = doc['
|
16
|
-
if
|
17
|
-
|
18
|
-
|
19
|
-
return version_2_results(doc)
|
20
|
-
else
|
21
|
-
warn "Yahoo Geocoding API error: #{doc['Error']} (#{doc['ErrorMessage']})."
|
22
|
-
return []
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def api_version(doc)
|
27
|
-
if doc.include?('version')
|
28
|
-
return doc['version'].to_f
|
29
|
-
elsif doc.include?('@version')
|
30
|
-
return doc['@version'].to_f
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def version_1_results(doc)
|
35
|
-
if doc['Error'] == 0
|
36
|
-
if doc['Found'] > 0
|
37
|
-
return doc['Results']
|
38
|
-
else
|
39
|
-
return []
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
##
|
45
|
-
# Return array of results, or nil if an error.
|
46
|
-
#
|
47
|
-
def version_2_results(doc)
|
48
|
-
# seems to have Error == 7 when no results, though this is not documented
|
49
|
-
if [0, 7].include?(doc['Error'].to_i)
|
50
|
-
if doc['Found'].to_i > 0
|
51
|
-
r = doc['Result']
|
52
|
-
return r.is_a?(Array) ? r : [r]
|
16
|
+
doc = doc['bossresponse']
|
17
|
+
if doc['responsecode'].to_i == 200
|
18
|
+
if doc['placefinder']['count'].to_i > 0
|
19
|
+
return doc['placefinder']['results']
|
53
20
|
else
|
54
21
|
return []
|
55
22
|
end
|
23
|
+
else
|
24
|
+
warn "Yahoo Geocoding API error: #{doc['responsecode']} (#{doc['reason']})."
|
25
|
+
return []
|
56
26
|
end
|
57
27
|
end
|
58
28
|
|
@@ -60,14 +30,28 @@ module Geocoder::Lookup
|
|
60
30
|
super.merge(
|
61
31
|
:location => query.sanitized_text,
|
62
32
|
:flags => "JXTSR",
|
63
|
-
:gflags => "AC#{'R' if query.reverse_geocode?}"
|
64
|
-
:locale => "#{Geocoder::Configuration.language}_US",
|
65
|
-
:appid => Geocoder::Configuration.api_key
|
33
|
+
:gflags => "AC#{'R' if query.reverse_geocode?}"
|
66
34
|
)
|
67
35
|
end
|
68
36
|
|
37
|
+
def cache_key(query)
|
38
|
+
raw_url(query)
|
39
|
+
end
|
40
|
+
|
41
|
+
def base_url
|
42
|
+
"#{protocol}://yboss.yahooapis.com/geo/placefinder?"
|
43
|
+
end
|
44
|
+
|
45
|
+
def raw_url(query)
|
46
|
+
base_url + url_query_string(query)
|
47
|
+
end
|
48
|
+
|
69
49
|
def query_url(query)
|
70
|
-
|
50
|
+
parsed_url = URI.parse(raw_url(query))
|
51
|
+
o = OauthUtil.new
|
52
|
+
o.consumer_key = Geocoder::Configuration.api_key[0]
|
53
|
+
o.consumer_secret = Geocoder::Configuration.api_key[1]
|
54
|
+
base_url + o.sign(parsed_url).query_string
|
71
55
|
end
|
72
56
|
end
|
73
57
|
end
|
@@ -18,7 +18,7 @@ module Geocoder
|
|
18
18
|
super(options)
|
19
19
|
if options[:skip_index] == false
|
20
20
|
# create 2d index
|
21
|
-
if (::Mongoid::VERSION >= "3"
|
21
|
+
if defined?(::Mongoid::VERSION) && ::Mongoid::VERSION >= "3"
|
22
22
|
index({ geocoder_options[:coordinates].to_sym => '2d' },
|
23
23
|
{:min => -180, :max => 180})
|
24
24
|
else
|
data/lib/geocoder/query.rb
CHANGED
@@ -40,7 +40,13 @@ module Geocoder
|
|
40
40
|
# Is the Query text blank? (ie, should we not bother searching?)
|
41
41
|
#
|
42
42
|
def blank?
|
43
|
-
|
43
|
+
# check whether both coordinates given
|
44
|
+
if text.is_a?(Array)
|
45
|
+
text.compact.size < 2
|
46
|
+
# else assume a string
|
47
|
+
else
|
48
|
+
!!text.to_s.match(/^\s*$/)
|
49
|
+
end
|
44
50
|
end
|
45
51
|
|
46
52
|
##
|
@@ -1,10 +1,16 @@
|
|
1
1
|
module Geocoder
|
2
2
|
module Result
|
3
3
|
class Base
|
4
|
-
|
4
|
+
|
5
|
+
# data (hash) fetched from geocoding service
|
6
|
+
attr_accessor :data
|
7
|
+
|
8
|
+
# true if result came from cache, false if from request to geocoding
|
9
|
+
# service; nil if cache is not configured
|
10
|
+
attr_accessor :cache_hit
|
5
11
|
|
6
12
|
##
|
7
|
-
# Takes a hash of
|
13
|
+
# Takes a hash of data from a parsed geocoding service response.
|
8
14
|
#
|
9
15
|
def initialize(data)
|
10
16
|
@data = data
|
@@ -1,7 +1,51 @@
|
|
1
1
|
require 'geocoder/results/base'
|
2
|
-
require 'geocoder/results/nominatim'
|
3
2
|
|
4
3
|
module Geocoder::Result
|
5
|
-
class Mapquest <
|
4
|
+
class Mapquest < Base
|
5
|
+
def latitude
|
6
|
+
@data["latLng"]["lat"]
|
7
|
+
end
|
8
|
+
|
9
|
+
def longitude
|
10
|
+
@data["latLng"]["lng"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def coordinates
|
14
|
+
[latitude, longitude]
|
15
|
+
end
|
16
|
+
|
17
|
+
def city
|
18
|
+
@data['adminArea5']
|
19
|
+
end
|
20
|
+
|
21
|
+
def street
|
22
|
+
@data['street']
|
23
|
+
end
|
24
|
+
|
25
|
+
def state
|
26
|
+
@data['adminArea3']
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :state_code, :state
|
30
|
+
|
31
|
+
#FIXME: these might not be right, unclear with MQ documentation
|
32
|
+
alias_method :provinice, :state
|
33
|
+
alias_method :province_code, :state
|
34
|
+
|
35
|
+
def postal_code
|
36
|
+
@data['postalCode'].to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def country
|
40
|
+
@data['adminArea1']
|
41
|
+
end
|
42
|
+
|
43
|
+
def country_code
|
44
|
+
country
|
45
|
+
end
|
46
|
+
|
47
|
+
def address
|
48
|
+
[street, city, state, postal_code, country].reject{|s| s.length == 0 }.join(", ")
|
49
|
+
end
|
6
50
|
end
|
7
51
|
end
|