geocoder 1.1.4 → 1.1.8
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.
- data/.travis.yml +4 -0
- data/{CHANGELOG.rdoc → CHANGELOG.md} +120 -39
- data/README.md +200 -54
- data/examples/autoexpire_cache_dalli.rb +62 -0
- data/examples/{autoexpire_cache.rb → autoexpire_cache_redis.rb} +4 -6
- data/lib/generators/geocoder/config/templates/initializer.rb +19 -23
- data/lib/geocoder/cache.rb +6 -1
- data/lib/geocoder/calculations.rb +45 -13
- data/lib/geocoder/cli.rb +10 -11
- data/lib/geocoder/configuration.rb +67 -43
- data/lib/geocoder/configuration_hash.rb +11 -0
- data/lib/geocoder/exceptions.rb +3 -0
- data/lib/geocoder/lookup.rb +3 -1
- data/lib/geocoder/lookups/base.rb +89 -27
- data/lib/geocoder/lookups/bing.rb +21 -11
- data/lib/geocoder/lookups/esri.rb +48 -0
- data/lib/geocoder/lookups/freegeoip.rb +8 -4
- data/lib/geocoder/lookups/geocoder_ca.rb +11 -7
- data/lib/geocoder/lookups/google.rb +18 -9
- data/lib/geocoder/lookups/google_premier.rb +16 -8
- data/lib/geocoder/lookups/mapquest.rb +33 -5
- data/lib/geocoder/lookups/maxmind.rb +88 -0
- data/lib/geocoder/lookups/nominatim.rb +13 -8
- data/lib/geocoder/lookups/ovi.rb +52 -0
- data/lib/geocoder/lookups/test.rb +10 -0
- data/lib/geocoder/lookups/yahoo.rb +51 -38
- data/lib/geocoder/lookups/yandex.rb +17 -9
- data/lib/geocoder/models/mongoid.rb +1 -1
- data/lib/geocoder/query.rb +20 -5
- data/lib/geocoder/request.rb +7 -1
- data/lib/geocoder/results/base.rb +8 -2
- data/lib/geocoder/results/esri.rb +51 -0
- data/lib/geocoder/results/google.rb +34 -0
- data/lib/geocoder/results/mapquest.rb +46 -2
- data/lib/geocoder/results/maxmind.rb +135 -0
- data/lib/geocoder/results/nominatim.rb +0 -10
- data/lib/geocoder/results/ovi.rb +62 -0
- data/lib/geocoder/results/test.rb +2 -1
- data/lib/geocoder/results/yahoo.rb +9 -2
- data/lib/geocoder/results/yandex.rb +12 -2
- data/lib/geocoder/sql.rb +6 -4
- data/lib/geocoder/stores/active_record.rb +57 -30
- data/lib/geocoder/stores/base.rb +3 -2
- data/lib/geocoder/stores/mongo_base.rb +2 -1
- data/lib/geocoder/version.rb +1 -1
- data/lib/geocoder.rb +2 -4
- data/lib/hash_recursive_merge.rb +74 -0
- data/lib/oauth_util.rb +112 -0
- data/test/cache_test.rb +19 -0
- data/test/calculations_test.rb +10 -4
- data/test/configuration_test.rb +26 -51
- data/test/error_handling_test.rb +8 -4
- data/test/fixtures/bing_invalid_key +1 -0
- data/test/fixtures/esri_madison_square_garden +59 -0
- data/test/fixtures/esri_no_results +8 -0
- data/test/fixtures/esri_reverse +21 -0
- data/test/fixtures/mapquest_madison_square_garden +52 -0
- data/test/fixtures/mapquest_no_results +7 -0
- data/test/fixtures/maxmind_24_24_24_21 +1 -0
- data/test/fixtures/maxmind_24_24_24_22 +1 -0
- data/test/fixtures/maxmind_24_24_24_23 +1 -0
- data/test/fixtures/maxmind_24_24_24_24 +1 -0
- data/test/fixtures/maxmind_74_200_247_59 +1 -0
- data/test/fixtures/maxmind_invalid_key +1 -0
- data/test/fixtures/maxmind_no_results +1 -0
- data/test/fixtures/ovi_madison_square_garden +72 -0
- data/test/fixtures/ovi_no_results +8 -0
- data/test/fixtures/yahoo_error +1 -0
- data/test/fixtures/yahoo_invalid_key +2 -0
- data/test/fixtures/yahoo_madison_square_garden +52 -0
- data/test/fixtures/yahoo_no_results +10 -0
- data/test/fixtures/yahoo_over_limit +2 -0
- data/test/fixtures/yandex_no_city_and_town +112 -0
- data/test/https_test.rb +3 -3
- data/test/integration/smoke_test.rb +2 -2
- data/test/lookup_test.rb +84 -11
- data/test/near_test.rb +43 -0
- data/test/oauth_util_test.rb +30 -0
- data/test/proxy_test.rb +2 -2
- data/test/query_test.rb +13 -0
- data/test/request_test.rb +29 -0
- data/test/result_test.rb +12 -2
- data/test/services_test.rb +208 -57
- data/test/test_helper.rb +66 -100
- data/test/test_mode_test.rb +35 -26
- metadata +58 -35
- data/test/fixtures/mapquest_madison_square_garden.json +0 -27
- data/test/fixtures/mapquest_no_results.json +0 -1
- data/test/fixtures/yahoo_madison_square_garden.json +0 -52
- data/test/fixtures/yahoo_no_results.json +0 -10
- data/test/fixtures/yahoo_v1_madison_square_garden.json +0 -46
- data/test/fixtures/yahoo_v1_no_results.json +0 -10
- /data/test/fixtures/{bing_madison_square_garden.json → bing_madison_square_garden} +0 -0
- /data/test/fixtures/{bing_no_results.json → bing_no_results} +0 -0
- /data/test/fixtures/{bing_reverse.json → bing_reverse} +0 -0
- /data/test/fixtures/{freegeoip_74_200_247_59.json → freegeoip_74_200_247_59} +0 -0
- /data/test/fixtures/{freegeoip_no_results.json → freegeoip_no_results} +0 -0
- /data/test/fixtures/{geocoder_ca_madison_square_garden.json → geocoder_ca_madison_square_garden} +0 -0
- /data/test/fixtures/{geocoder_ca_no_results.json → geocoder_ca_no_results} +0 -0
- /data/test/fixtures/{geocoder_ca_reverse.json → geocoder_ca_reverse} +0 -0
- /data/test/fixtures/{google_garbage.json → google_garbage} +0 -0
- /data/test/fixtures/{google_madison_square_garden.json → google_madison_square_garden} +0 -0
- /data/test/fixtures/{google_no_city_data.json → google_no_city_data} +0 -0
- /data/test/fixtures/{google_no_locality.json → google_no_locality} +0 -0
- /data/test/fixtures/{google_no_results.json → google_no_results} +0 -0
- /data/test/fixtures/{nominatim_madison_square_garden.json → nominatim_madison_square_garden} +0 -0
- /data/test/fixtures/{nominatim_no_results.json → nominatim_no_results} +0 -0
- /data/test/fixtures/{yandex_invalid_key.json → yandex_invalid_key} +0 -0
- /data/test/fixtures/{yandex_kremlin.json → yandex_kremlin} +0 -0
- /data/test/fixtures/{yandex_no_results.json → yandex_no_results} +0 -0
data/README.md
CHANGED
|
@@ -7,18 +7,20 @@ Geocoder is a complete geocoding solution for Ruby. With Rails it adds geocoding
|
|
|
7
7
|
Compatibility
|
|
8
8
|
-------------
|
|
9
9
|
|
|
10
|
-
* Supports multiple Ruby versions: Ruby 1.8.7, 1.9.2, and JRuby.
|
|
10
|
+
* Supports multiple Ruby versions: Ruby 1.8.7, 1.9.2, 1.9.3, and JRuby.
|
|
11
11
|
* Supports multiple databases: MySQL, PostgreSQL, SQLite, and MongoDB (1.7.0 and higher).
|
|
12
|
-
* Supports Rails 3. If you need to use it with Rails 2 please see the `rails2` branch (no longer maintained, limited feature set).
|
|
12
|
+
* Supports Rails 3.x. If you need to use it with Rails 2 please see the `rails2` branch (no longer maintained, limited feature set).
|
|
13
13
|
* Works very well outside of Rails, you just need to install either the `json` (for MRI) or `json_pure` (for JRuby) gem.
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Installation
|
|
17
|
+
------------
|
|
18
|
+
|
|
19
|
+
Install Geocoder like any other Ruby gem:
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
gem install geocoder
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
Or, if you're using Rails/Bundler, add this to your Gemfile:
|
|
22
24
|
|
|
23
25
|
gem "geocoder"
|
|
24
26
|
|
|
@@ -26,21 +28,13 @@ and run at the command prompt:
|
|
|
26
28
|
|
|
27
29
|
bundle install
|
|
28
30
|
|
|
29
|
-
### Or As a Plugin
|
|
30
|
-
|
|
31
|
-
At the command prompt:
|
|
32
|
-
|
|
33
|
-
rails plugin install git://github.com/alexreisner/geocoder.git
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Configure Object Geocoding
|
|
37
|
-
--------------------------
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
Object Geocoding
|
|
33
|
+
----------------
|
|
40
34
|
|
|
41
35
|
### ActiveRecord
|
|
42
36
|
|
|
43
|
-
Your model must have two attributes (database columns) for storing latitude and longitude coordinates. By default they should be called `latitude` and `longitude` but this can be changed (see "
|
|
37
|
+
Your model must have two attributes (database columns) for storing latitude and longitude coordinates. By default they should be called `latitude` and `longitude` but this can be changed (see "Model Configuration" below):
|
|
44
38
|
|
|
45
39
|
rails generate migration AddLatitudeAndLongitudeToModel latitude:float longitude:float
|
|
46
40
|
rake db:migrate
|
|
@@ -81,6 +75,10 @@ Reverse geocoding is similar:
|
|
|
81
75
|
reverse_geocoded_by :coordinates
|
|
82
76
|
after_validation :reverse_geocode # auto-fetch address
|
|
83
77
|
|
|
78
|
+
Once you've set up your model you'll need to create the necessary spatial indices in your database:
|
|
79
|
+
|
|
80
|
+
rake db:mongoid:create_indexes
|
|
81
|
+
|
|
84
82
|
Be sure to read _Latitude/Longitude Order_ in the _Notes on MongoDB_ section below on how to properly retrieve latitude/longitude coordinates from your objects.
|
|
85
83
|
|
|
86
84
|
### MongoMapper
|
|
@@ -111,6 +109,8 @@ Geocoder adds a `location` method to the standard `Rack::Request` object so you
|
|
|
111
109
|
# returns Geocoder::Result object
|
|
112
110
|
result = request.location
|
|
113
111
|
|
|
112
|
+
Note that this will usually return `nil` in your test and development environments because things like "localhost" and "0.0.0.0" are not an Internet IP addresses.
|
|
113
|
+
|
|
114
114
|
See _Advanced Geocoding_ below for more information about `Geocoder::Result` objects.
|
|
115
115
|
|
|
116
116
|
|
|
@@ -126,9 +126,11 @@ To find objects by location, use the following scopes:
|
|
|
126
126
|
|
|
127
127
|
With geocoded objects you can do things like this:
|
|
128
128
|
|
|
129
|
-
obj.
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
if obj.geocoded?
|
|
130
|
+
obj.nearbys(30) # other objects within 30 miles
|
|
131
|
+
obj.distance_from([40.714,-100.234]) # distance from arbitrary point to object
|
|
132
|
+
obj.bearing_to("Paris, France") # direction from object to arbitrary point
|
|
133
|
+
end
|
|
132
134
|
|
|
133
135
|
Some utility methods are also available:
|
|
134
136
|
|
|
@@ -178,11 +180,11 @@ To calculate accurate distance and bearing with SQLite or MongoDB:
|
|
|
178
180
|
obj.bearing_to([43.9,-98.6]) # bearing from obj to point
|
|
179
181
|
obj.bearing_from(obj2) # bearing from obj2 to obj
|
|
180
182
|
|
|
181
|
-
The `bearing_from/to` methods take a single argument which can be: a `[lat,lon]` array, a geocoded object, or a geocodable address (string). The `distance_from/to` methods also take a units argument (`:mi
|
|
183
|
+
The `bearing_from/to` methods take a single argument which can be: a `[lat,lon]` array, a geocoded object, or a geocodable address (string). The `distance_from/to` methods also take a units argument (`:mi`, `:km`, or `:nm` for nautical miles).
|
|
182
184
|
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
Model Configuration
|
|
187
|
+
-------------------
|
|
186
188
|
|
|
187
189
|
You are not stuck with using the `latitude` and `longitude` database column names (with ActiveRecord) or the `coordinates` array (Mongo) for storing coordinates. For example:
|
|
188
190
|
|
|
@@ -257,32 +259,37 @@ If you're familiar with the results returned by the geocoding service you're usi
|
|
|
257
259
|
Geocoding Services
|
|
258
260
|
------------------
|
|
259
261
|
|
|
260
|
-
By default Geocoder uses Google's geocoding API to fetch coordinates and street addresses (FreeGeoIP is
|
|
262
|
+
By default Geocoder uses Google's geocoding API to fetch coordinates and street addresses (FreeGeoIP is the default for IP address info). However there are several other APIs supported, as well as a variety of settings. Please see the listing and comparison below for details on specific geocoding services (not all settings are supported by all services). Some common configuration options are:
|
|
261
263
|
|
|
262
264
|
# config/initializers/geocoder.rb
|
|
263
|
-
Geocoder.configure
|
|
265
|
+
Geocoder.configure(
|
|
264
266
|
|
|
265
267
|
# geocoding service (see below for supported options):
|
|
266
|
-
|
|
268
|
+
:lookup => :yandex,
|
|
267
269
|
|
|
268
270
|
# to use an API key:
|
|
269
|
-
|
|
271
|
+
:api_key => "...",
|
|
270
272
|
|
|
271
273
|
# geocoding service request timeout, in seconds (default 3):
|
|
272
|
-
|
|
274
|
+
:timeout => 5,
|
|
273
275
|
|
|
274
276
|
# set default units to kilometers:
|
|
275
|
-
|
|
277
|
+
:units => :km,
|
|
276
278
|
|
|
277
279
|
# caching (see below for details):
|
|
278
|
-
|
|
279
|
-
|
|
280
|
+
:cache => Redis.new,
|
|
281
|
+
:cache_prefix => "..."
|
|
280
282
|
|
|
281
|
-
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
Please see lib/geocoder/configuration.rb for a complete list of configuration options. Additionally, some lookups have their own configuration options, some of which are directly supported by Geocoder. For example, to specify a value for Google's `bounds` parameter:
|
|
286
|
+
|
|
287
|
+
# with Google:
|
|
288
|
+
Geocoder.search("Paris", :bounds => [[32.1,-95.9], [33.9,-94.3]])
|
|
282
289
|
|
|
283
|
-
Please see lib/geocoder/
|
|
290
|
+
Please see the [source code for each lookup](https://github.com/alexreisner/geocoder/tree/master/lib/geocoder/lookups) to learn about directly supported parameters. Parameters which are not directly supported can be specified using the `:params` option, by which you can pass arbitrary parameters to any geocoding service. For example, to use Nominatim's `countrycodes` parameter:
|
|
284
291
|
|
|
285
|
-
|
|
292
|
+
# with Nominatim:
|
|
286
293
|
Geocoder.search("Paris", :params => {:countrycodes => "gb,de,fr,es,us"})
|
|
287
294
|
|
|
288
295
|
|
|
@@ -302,19 +309,21 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
|
|
|
302
309
|
* **Documentation**: http://code.google.com/apis/maps/documentation/geocoding/#JSON
|
|
303
310
|
* **Terms of Service**: http://code.google.com/apis/maps/terms.html#section_10_12
|
|
304
311
|
* **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
|
-
* **Notes**: To use Google Premier set `Geocoder
|
|
312
|
+
* **Notes**: To use Google Premier set `Geocoder.configure(:lookup => :google_premier, :api_key => [key, client, channel])`.
|
|
306
313
|
|
|
307
|
-
#### Yahoo (`:yahoo`)
|
|
314
|
+
#### Yahoo BOSS (`:yahoo`)
|
|
308
315
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
* **
|
|
316
|
+
Yahoo BOSS is **not a free service**. As of November 17, 2012 Yahoo no longer offers a free geocoding API.
|
|
317
|
+
|
|
318
|
+
* **API key**: requires OAuth consumer key and secret (set `Geocoder.configure(:api_key => [key, secret])`)
|
|
319
|
+
* **Key signup**: http://developer.yahoo.com/boss/geo/
|
|
320
|
+
* **Quota**: unlimited, but subject to usage fees
|
|
312
321
|
* **Region**: world
|
|
313
322
|
* **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**:
|
|
323
|
+
* **Languages**: en, fr, de, it, es, pt, nl, zh, ja, ko
|
|
324
|
+
* **Documentation**: http://developer.yahoo.com/boss/geo/docs/index.html
|
|
325
|
+
* **Terms of Service**: http://info.yahoo.com/legal/us/yahoo/boss/tou/?pir=ucJPcJ1ibUn.h.d.lVmlcbcEkoHjwJ_PvxG9SLK9VIbIQAw1XFrnDqY-
|
|
326
|
+
* **Limitations**: No mass downloads, no commercial map production based on the data, no storage of data except for caching.
|
|
318
327
|
|
|
319
328
|
#### Bing (`:bing`)
|
|
320
329
|
|
|
@@ -363,8 +372,11 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
|
|
|
363
372
|
|
|
364
373
|
#### Mapquest (`:mapquest`)
|
|
365
374
|
|
|
366
|
-
* **API key**:
|
|
375
|
+
* **API key**: required for the licensed API, do not use for open tier
|
|
367
376
|
* **Quota**: ?
|
|
377
|
+
* **HTTP Headers**: in order to use the licensed API you can configure the http_headers to include a referer as so:
|
|
378
|
+
`Geocoder.configure(:http_headers => { "Referer" => "http://foo.com" })`
|
|
379
|
+
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
380
|
* **Region**: world
|
|
369
381
|
* **SSL support**: no
|
|
370
382
|
* **Languages**: English
|
|
@@ -372,6 +384,17 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
|
|
|
372
384
|
* **Terms of Service**: http://info.mapquest.com/terms-of-use/
|
|
373
385
|
* **Limitations**: ?
|
|
374
386
|
|
|
387
|
+
#### Ovi/Nokia (`:ovi`)
|
|
388
|
+
|
|
389
|
+
* **API key**: not required, but performance restricted without it
|
|
390
|
+
* **Quota**: ?
|
|
391
|
+
* **Region**: world
|
|
392
|
+
* **SSL support**: no
|
|
393
|
+
* **Languages**: English
|
|
394
|
+
* **Documentation**: http://api.maps.ovi.com/devguide/overview.html
|
|
395
|
+
* **Terms of Service**: http://www.developer.nokia.com/Develop/Maps/TC.html
|
|
396
|
+
* **Limitations**: ?
|
|
397
|
+
|
|
375
398
|
#### FreeGeoIP (`:freegeoip`)
|
|
376
399
|
|
|
377
400
|
* **API key**: none
|
|
@@ -383,13 +406,37 @@ The following is a comparison of the supported geocoding APIs. The "Limitations"
|
|
|
383
406
|
* **Terms of Service**: ?
|
|
384
407
|
* **Limitations**: ?
|
|
385
408
|
|
|
409
|
+
#### MaxMind Web Services (`:maxmind`)
|
|
410
|
+
|
|
411
|
+
* **API key**: required
|
|
412
|
+
* **Quota**: Request Packs can be purchased
|
|
413
|
+
* **Region**: world
|
|
414
|
+
* **SSL support**: yes
|
|
415
|
+
* **Languages**: English
|
|
416
|
+
* **Documentation**: http://www.maxmind.com/app/web_services
|
|
417
|
+
* **Terms of Service**: ?
|
|
418
|
+
* **Limitations**: ?
|
|
419
|
+
* **Notes**: You must specify which MaxMind service you are using in your configuration. For example: `Geocoder.configure(:maxmind => {:service => :omni})`.
|
|
420
|
+
|
|
421
|
+
#### ESRI (`:esri`)
|
|
422
|
+
|
|
423
|
+
* **API key**: none
|
|
424
|
+
* **Quota**: Required for some scenarios (see Terms of Service)
|
|
425
|
+
* **Region**: world
|
|
426
|
+
* **SSL support**: yes
|
|
427
|
+
* **Languages**: English
|
|
428
|
+
* **Documentation**: http://resources.arcgis.com/en/help/arcgis-online-geocoding-rest-api/
|
|
429
|
+
* **Terms of Service**: http://www.esri.com/software/arcgis/arcgisonline/services/geoservices
|
|
430
|
+
* **Limitations**: ?
|
|
431
|
+
* **Notes**: You can specify which projection you want to use by setting, for example: `Geocoder.configure(:esri => {:outSR => 102100})`.
|
|
432
|
+
|
|
386
433
|
|
|
387
434
|
Caching
|
|
388
435
|
-------
|
|
389
436
|
|
|
390
437
|
It's a good idea, when relying on any external service, to cache retrieved data. When implemented correctly it improves your app's response time and stability. It's easy to cache geocoding results with Geocoder, just configure a cache store:
|
|
391
438
|
|
|
392
|
-
Geocoder
|
|
439
|
+
Geocoder.configure(:cache => Redis.new)
|
|
393
440
|
|
|
394
441
|
This example uses Redis, but the cache store can be any object that supports these methods:
|
|
395
442
|
|
|
@@ -402,7 +449,7 @@ Even a plain Ruby hash will work, though it's not a great choice (cleared out wh
|
|
|
402
449
|
|
|
403
450
|
You can also set a custom prefix to be used for cache keys:
|
|
404
451
|
|
|
405
|
-
Geocoder
|
|
452
|
+
Geocoder.configure(:cache_prefix => "...")
|
|
406
453
|
|
|
407
454
|
By default the prefix is `geocoder:`
|
|
408
455
|
|
|
@@ -450,6 +497,30 @@ However, there can be only one set of latitude/longitude attributes, and whichev
|
|
|
450
497
|
|
|
451
498
|
The reason for this is that we don't want ambiguity when doing distance calculations. We need a single, authoritative source for coordinates!
|
|
452
499
|
|
|
500
|
+
Once both forward and reverse geocoding has been applied, it is possible to call them sequentially.
|
|
501
|
+
|
|
502
|
+
For example:
|
|
503
|
+
|
|
504
|
+
class Venue
|
|
505
|
+
|
|
506
|
+
after_validation :geocode, :reverse_geocode
|
|
507
|
+
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
For certain geolocation services such as Google geolocation API this may cause issues during subsequent updates to database records if the longtitude and latitude coordinates cannot be associated known location address (on a large body of water for example). On subsequent callbacks the following call:
|
|
511
|
+
|
|
512
|
+
after_validation :geocode
|
|
513
|
+
|
|
514
|
+
will alter the longtitude and latitude attributes based on the location field, which would be the closest known location to the original coordinates. In this case it is better to add conditions to each call, as not to override coordinates that do not have known location addresses associated with them.
|
|
515
|
+
|
|
516
|
+
For example:
|
|
517
|
+
|
|
518
|
+
class Venue
|
|
519
|
+
|
|
520
|
+
after_validation :reverse_geocode, :if => :has_coordinates
|
|
521
|
+
after_validation :geocode, :if => :has_location, :unless => :has_coordinates
|
|
522
|
+
|
|
523
|
+
end
|
|
453
524
|
|
|
454
525
|
Use Outside of Rails
|
|
455
526
|
--------------------
|
|
@@ -458,7 +529,7 @@ You can use Geocoder outside of Rails by calling the `Geocoder.search` method:
|
|
|
458
529
|
|
|
459
530
|
results = Geocoder.search("McCarren Park, Brooklyn, NY")
|
|
460
531
|
|
|
461
|
-
This returns an array of `Geocoder::Result` objects with all
|
|
532
|
+
This returns an array of `Geocoder::Result` objects with all data provided by the geocoding service.
|
|
462
533
|
|
|
463
534
|
|
|
464
535
|
Testing Apps that Use Geocoder
|
|
@@ -466,7 +537,7 @@ Testing Apps that Use Geocoder
|
|
|
466
537
|
|
|
467
538
|
When writing tests for an app that uses Geocoder it may be useful to avoid network calls and have Geocoder return consistent, configurable results. To do this, configure and use the `:test` lookup. For example:
|
|
468
539
|
|
|
469
|
-
Geocoder
|
|
540
|
+
Geocoder.configure(:lookup => :test)
|
|
470
541
|
|
|
471
542
|
Geocoder::Lookup::Test.add_stub(
|
|
472
543
|
"New York, NY", [
|
|
@@ -482,8 +553,25 @@ When writing tests for an app that uses Geocoder it may be useful to avoid netwo
|
|
|
482
553
|
]
|
|
483
554
|
)
|
|
484
555
|
|
|
485
|
-
Now, any time Geocoder looks up "New York, NY" its results array will contain one result with the above attributes.
|
|
556
|
+
Now, any time Geocoder looks up "New York, NY" its results array will contain one result with the above attributes. You can also set a default stub:
|
|
486
557
|
|
|
558
|
+
Geocoder.configure(:lookup => :test)
|
|
559
|
+
|
|
560
|
+
Geocoder::Lookup::Test.set_default_stub(
|
|
561
|
+
[
|
|
562
|
+
{
|
|
563
|
+
'latitude' => 40.7143528,
|
|
564
|
+
'longitude' => -74.0059731,
|
|
565
|
+
'address' => 'New York, NY, USA',
|
|
566
|
+
'state' => 'New York',
|
|
567
|
+
'state_code' => 'NY',
|
|
568
|
+
'country' => 'United States',
|
|
569
|
+
'country_code' => 'US'
|
|
570
|
+
}
|
|
571
|
+
]
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
Any query that hasn't been explicitly stubbed will return that result.
|
|
487
575
|
|
|
488
576
|
Command Line Interface
|
|
489
577
|
----------------------
|
|
@@ -502,7 +590,6 @@ When you install the Geocoder gem it adds a `geocode` command to your shell. You
|
|
|
502
590
|
|
|
503
591
|
There are also a number of options for setting the geocoding API, key, and language, viewing the raw JSON reponse, and more. Please run `geocode -h` for details.
|
|
504
592
|
|
|
505
|
-
|
|
506
593
|
Notes on MongoDB
|
|
507
594
|
----------------
|
|
508
595
|
|
|
@@ -524,6 +611,22 @@ Calling `obj.coordinates` directly returns the internal representation of the co
|
|
|
524
611
|
|
|
525
612
|
For consistency with the rest of Geocoder, always use the `to_coordinates` method instead.
|
|
526
613
|
|
|
614
|
+
Notes on Non-Rails Frameworks
|
|
615
|
+
-----------------------------
|
|
616
|
+
|
|
617
|
+
If you are using Geocoder with ActiveRecord and a framework other than Rails (like Sinatra or Padrino) you will need to add this in your model before calling Geocoder methods:
|
|
618
|
+
|
|
619
|
+
extend Geocoder::Model::ActiveRecord
|
|
620
|
+
|
|
621
|
+
Optimisation of Distance Queries
|
|
622
|
+
--------------------------------
|
|
623
|
+
|
|
624
|
+
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.
|
|
625
|
+
|
|
626
|
+
To take advantage of this optimisation you need to add a composite index on latitude and longitude. In your Rails migration:
|
|
627
|
+
|
|
628
|
+
add_index :table, [:latitude, :longitude]
|
|
629
|
+
|
|
527
630
|
|
|
528
631
|
Distance Queries in SQLite
|
|
529
632
|
--------------------------
|
|
@@ -555,15 +658,58 @@ http://github.com/alexreisner/geocoder_test
|
|
|
555
658
|
Error Handling
|
|
556
659
|
--------------
|
|
557
660
|
|
|
558
|
-
By default Geocoder will rescue any exceptions raised by calls to
|
|
661
|
+
By default Geocoder will rescue any exceptions raised by calls to a geocoding service and return an empty array (using warn() to inform you of the error). You can override this on a per-exception basis, and also have Geocoder raise its own exceptions for certain events (eg: API quota exceeded) by using the `:always_raise` option:
|
|
559
662
|
|
|
560
|
-
Geocoder
|
|
663
|
+
Geocoder.configure(:always_raise => [SocketError, TimeoutError])
|
|
561
664
|
|
|
562
665
|
You can also do this to raise all exceptions:
|
|
563
666
|
|
|
564
|
-
Geocoder
|
|
667
|
+
Geocoder.configure(:always_raise => :all)
|
|
668
|
+
|
|
669
|
+
The raise-able exceptions are:
|
|
670
|
+
|
|
671
|
+
SocketError
|
|
672
|
+
TimeoutError
|
|
673
|
+
Geocoder::OverQueryLimitError
|
|
674
|
+
Geocoder::RequestDenied
|
|
675
|
+
Geocoder::InvalidRequest
|
|
676
|
+
Geocoder::InvalidApiKey
|
|
677
|
+
|
|
678
|
+
Note that not all lookups support all exceptions.
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
Troubleshooting
|
|
682
|
+
---------------
|
|
683
|
+
|
|
684
|
+
### Mongoid
|
|
685
|
+
|
|
686
|
+
If you get one of these errors:
|
|
687
|
+
|
|
688
|
+
uninitialized constant Geocoder::Model::Mongoid
|
|
689
|
+
uninitialized constant Geocoder::Model::Mongoid::Mongo
|
|
690
|
+
|
|
691
|
+
you should check your Gemfile to make sure the Mongoid gem is listed _before_ Geocoder. If Mongoid isn't loaded when Geocoder is initialized, Geocoder will not load support for Mongoid.
|
|
692
|
+
|
|
693
|
+
### ActiveRecord
|
|
694
|
+
|
|
695
|
+
A lot of debugging time can be saved by understanding how Geocoder works with ActiveRecord. When you use the `near` scope or the `nearbys` method of a geocoded object, Geocoder creates an ActiveModel::Relation object which adds some attributes (eg: distance, bearing) to the SELECT clause. It also adds a condition to the WHERE clause to check that distance is within the given radius. Because the SELECT clause is modified, anything else that modifies the SELECT clause may produce strange results, for example:
|
|
696
|
+
|
|
697
|
+
* using the `pluck` method (selects only a single column)
|
|
698
|
+
* specifying another model through `includes` (selects columns from other tables)
|
|
699
|
+
|
|
700
|
+
### Unexpected Responses from Geocoding Services
|
|
701
|
+
|
|
702
|
+
Take a look at the server's raw JSON response. You can do this by getting the request URL in an app console:
|
|
703
|
+
|
|
704
|
+
Geocoder::Lookup.get(:google).query_url(Geocoder::Query.new("..."))
|
|
705
|
+
|
|
706
|
+
Replace `:google` with the lookup you are using and replace `...` with the address you are trying to geocode. Then visit the returned URL in your web browser. Often the API will return an error message that helps you resolve the problem. If, after reading the raw response, you believe there is a problem with Geocoder, please post an issue and include both the URL and raw response body.
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
Reporting Issues
|
|
710
|
+
----------------
|
|
565
711
|
|
|
566
|
-
|
|
712
|
+
When reporting an issue, please list the version of Geocoder you are using and any relevant information about your application (Rails version, database type and version, etc). Also avoid vague language like "it doesn't work." Please describe as specifically as you can what behavior your are actually seeing (eg: an error message? a nil return value?).
|
|
567
713
|
|
|
568
714
|
|
|
569
715
|
Known Issue
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# This class implements a cache with simple delegation to the the Dalli Memcached client
|
|
2
|
+
# https://github.com/mperham/dalli
|
|
3
|
+
#
|
|
4
|
+
# A TTL is set on initialization
|
|
5
|
+
|
|
6
|
+
class AutoexpireCacheDalli
|
|
7
|
+
def initialize(store, ttl = 86400)
|
|
8
|
+
@store = store
|
|
9
|
+
@keys = 'GeocoderDalliClientKeys'
|
|
10
|
+
@ttl = ttl
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def [](url)
|
|
14
|
+
res = @store.get(url)
|
|
15
|
+
res = YAML::load(res) if res.present?
|
|
16
|
+
res
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def []=(url, value)
|
|
20
|
+
if value.nil?
|
|
21
|
+
del(url)
|
|
22
|
+
else
|
|
23
|
+
key_cache_add(url) if @store.add(key, YAML::dump(value), @ttl)
|
|
24
|
+
end
|
|
25
|
+
value
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def keys
|
|
29
|
+
key_cache
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def del(url)
|
|
33
|
+
key_cache_delete(url) if @store.delete(key)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def key_cache
|
|
39
|
+
the_keys = @store.get(@keys)
|
|
40
|
+
if the_keys.nil?
|
|
41
|
+
@store.add(@keys, YAML::dump([]))
|
|
42
|
+
[]
|
|
43
|
+
else
|
|
44
|
+
YAML::load(the_keys)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def key_cache_add(key)
|
|
49
|
+
@store.replace(@keys, YAML::dump(key_cache << key))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def key_cache_delete(key)
|
|
53
|
+
tmp = key_cache
|
|
54
|
+
tmp.delete(key)
|
|
55
|
+
@store.replace(@keys, YAML::dump(tmp))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Here Dalli is set up as on Heroku using the Memcachier gem.
|
|
60
|
+
# https://devcenter.heroku.com/articles/memcachier#ruby
|
|
61
|
+
# On other setups you might have to specify your Memcached server in Dalli::Client.new
|
|
62
|
+
Geocoder.configure(:cache => AutoexpireCacheDalli.new(Dalli::Client.new))
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# This class implements a cache with simple delegation to the Redis store, but
|
|
2
2
|
# when it creates a key/value pair, it also sends an EXPIRE command with a TTL.
|
|
3
3
|
# It should be fairly simple to do the same thing with Memcached.
|
|
4
|
-
class
|
|
5
|
-
def initialize(store)
|
|
4
|
+
class AutoexpireCacheRedis
|
|
5
|
+
def initialize(store, ttl = 86400)
|
|
6
6
|
@store = store
|
|
7
|
-
@ttl =
|
|
7
|
+
@ttl = ttl
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def [](url)
|
|
@@ -25,6 +25,4 @@ class AutoexpireCache
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
Geocoder.configure
|
|
29
|
-
config.cache = AutoexpireCache.new(Redis.new)
|
|
30
|
-
end
|
|
28
|
+
Geocoder.configure(:cache => AutoexpireCacheRedis.new(Redis.new))
|
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
Geocoder.configure
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
Geocoder.configure(
|
|
2
|
+
# geocoding options
|
|
3
|
+
# :timeout => 3, # geocoding service timeout (secs)
|
|
4
|
+
# :lookup => :google, # name of geocoding service (symbol)
|
|
5
|
+
# :language => :en, # ISO-639 language code
|
|
6
|
+
# :use_https => false, # use HTTPS for lookup requests? (if supported)
|
|
7
|
+
# :http_proxy => nil, # HTTP proxy server (user:pass@host:port)
|
|
8
|
+
# :https_proxy => nil, # HTTPS proxy server (user:pass@host:port)
|
|
9
|
+
# :api_key => nil, # API key for geocoding service
|
|
10
|
+
# :cache => nil, # cache object (must respond to #[], #[]=, and #keys)
|
|
11
|
+
# :cache_prefix => "geocoder:", # prefix (string) to use for all cache keys
|
|
5
12
|
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# config.http_proxy = nil # HTTP proxy server (user:pass@host:port)
|
|
11
|
-
# config.https_proxy = nil # HTTPS proxy server (user:pass@host:port)
|
|
12
|
-
# config.api_key = nil # API key for geocoding service
|
|
13
|
-
# config.cache = nil # cache object (must respond to #[], #[]=, and #keys)
|
|
14
|
-
# config.cache_prefix = "geocoder:" # prefix (string) to use for all cache keys
|
|
15
|
-
|
|
16
|
-
## exceptions that should not be rescued by default
|
|
17
|
-
## (if you want to implement custom error handling);
|
|
18
|
-
## supports SocketError and TimeoutError
|
|
19
|
-
# config.always_raise = []
|
|
20
|
-
|
|
21
|
-
## Calculation options
|
|
22
|
-
# config.units = :mi # :km for kilometers or :mi for miles
|
|
23
|
-
# config.distances = :linear # :spherical or :linear
|
|
24
|
-
end
|
|
13
|
+
# exceptions that should not be rescued by default
|
|
14
|
+
# (if you want to implement custom error handling);
|
|
15
|
+
# supports SocketError and TimeoutError
|
|
16
|
+
# :always_raise => [],
|
|
25
17
|
|
|
18
|
+
# calculation options
|
|
19
|
+
# :units => :mi, # :km for kilometers or :mi for miles
|
|
20
|
+
# :distances => :linear # :spherical or :linear
|
|
21
|
+
)
|
data/lib/geocoder/cache.rb
CHANGED
|
@@ -15,6 +15,8 @@ module Geocoder
|
|
|
15
15
|
store[key_for(url)]
|
|
16
16
|
when store.respond_to?(:get)
|
|
17
17
|
store.get key_for(url)
|
|
18
|
+
when store.respond_to?(:read)
|
|
19
|
+
store.read key_for(url)
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
@@ -27,6 +29,8 @@ module Geocoder
|
|
|
27
29
|
store[key_for(url)] = value
|
|
28
30
|
when store.respond_to?(:set)
|
|
29
31
|
store.set key_for(url), value
|
|
32
|
+
when store.respond_to?(:write)
|
|
33
|
+
store.write key_for(url), value
|
|
30
34
|
end
|
|
31
35
|
end
|
|
32
36
|
|
|
@@ -78,7 +82,8 @@ module Geocoder
|
|
|
78
82
|
end
|
|
79
83
|
|
|
80
84
|
def expire_single_url(url)
|
|
81
|
-
|
|
85
|
+
key = key_for(url)
|
|
86
|
+
store.respond_to?(:del) ? store.del(key) : store.delete(key)
|
|
82
87
|
end
|
|
83
88
|
end
|
|
84
89
|
end
|