geocoder 1.3.1 → 1.3.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +70 -16
- data/examples/reverse_geocode_job.rb +1 -1
- data/lib/generators/geocoder/config/templates/initializer.rb +1 -1
- data/lib/geocoder/configuration.rb +1 -5
- data/lib/geocoder/lookup.rb +6 -3
- data/lib/geocoder/lookups/base.rb +1 -1
- data/lib/geocoder/lookups/freegeoip.rb +4 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +55 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/mapzen.rb +15 -0
- data/lib/geocoder/lookups/pelias.rb +56 -0
- data/lib/geocoder/results/ipinfo_io.rb +56 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/mapzen.rb +5 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/version.rb +1 -1
- metadata +11 -6
- data/lib/geocoder/lookups/yahoo.rb +0 -89
- data/lib/geocoder/results/yahoo.rb +0 -60
- data/lib/oauth_util.rb +0 -112
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 586633875af5786e76c1f815ca613d2dbe658298
|
4
|
+
data.tar.gz: 1aae8533ea6744c231c912c767dcf56e12ff087a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0a636c5d6b2f40b72c0af679a9fe246ef484b99b6be301f9102d202f0bc5d71f51000b3ae714299c5575b3812b3eae00c787b221fbe7baa23ce6d4c9b1a8f65
|
7
|
+
data.tar.gz: e5598a6dd62abe28f60307104a483e3c67e9e161d56a382c37009a89c83a6912ebf526bb94dfcecbcfcba154e7f8b2dd33cb352997b7b3752e694ecd56ce2a91
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,13 @@ Changelog
|
|
3
3
|
|
4
4
|
Major changes to Geocoder for each release. Please see the Git log for complete list of changes.
|
5
5
|
|
6
|
+
1.3.2 (2016 Apr 1)
|
7
|
+
------------------
|
8
|
+
* Remove :yahoo lookup (service was discontinued Mar 31) (thanks github.com/galiat).
|
9
|
+
* Add support for LatLon.io service (thanks github.com/evanmarks).
|
10
|
+
* Add support for IpInfo.io service (thanks github.com/rehan, akostyuk).
|
11
|
+
* Add support for Pelias/Mapzen service (thanks github.com/RealScout).
|
12
|
+
|
6
13
|
1.3.1 (2016 Feb 20)
|
7
14
|
-------------------
|
8
15
|
* Warn about upcoming discontinuation of :yahoo lookup (thanks github.com/galiat).
|
data/README.md
CHANGED
@@ -434,19 +434,6 @@ The [Google Places Details API](https://developers.google.com/places/documentati
|
|
434
434
|
* **Terms of Service**: https://developers.google.com/places/policies
|
435
435
|
* **Limitations**: "If your application displays Places API data on a page or view that does not also display a Google Map, you must show a "Powered by Google" logo with that data."
|
436
436
|
|
437
|
-
#### Yahoo BOSS (`:yahoo`)
|
438
|
-
**Warning - [this API will be discontinued on March 31.](https://developer.yahoo.com/boss/placefinder/)**
|
439
|
-
|
440
|
-
* **API key**: requires OAuth consumer key and secret (set `Geocoder.configure(:api_key => [key, secret])`)
|
441
|
-
* **Key signup**: http://developer.yahoo.com/boss/geo/
|
442
|
-
* **Quota**: unlimited, but subject to usage fees
|
443
|
-
* **Region**: world
|
444
|
-
* **SSL support**: no
|
445
|
-
* **Languages**: en, fr, de, it, es, pt, nl, zh, ja, ko
|
446
|
-
* **Documentation**: http://developer.yahoo.com/boss/geo/docs/index.html
|
447
|
-
* **Terms of Service**: http://info.yahoo.com/legal/us/yahoo/boss/tou/?pir=ucJPcJ1ibUn.h.d.lVmlcbcEkoHjwJ_PvxG9SLK9VIbIQAw1XFrnDqY-
|
448
|
-
* **Limitations**: No mass downloads, no commercial map production based on the data, no storage of data except for caching.
|
449
|
-
|
450
437
|
#### Bing (`:bing`)
|
451
438
|
|
452
439
|
* **API key**: required (set `Geocoder.configure(:lookup => :bing, :api_key => key)`)
|
@@ -578,6 +565,31 @@ The [Google Places Details API](https://developers.google.com/places/documentati
|
|
578
565
|
* **Limitations**: ?
|
579
566
|
* **Notes**: You can specify which projection you want to use by setting, for example: `Geocoder.configure(:esri => {:outSR => 102100})`.
|
580
567
|
|
568
|
+
#### Mapzen (`:mapzen`)
|
569
|
+
|
570
|
+
* **About**: Mapzen is the primary author of pelias and offers Pelias-as-a-service in free and paid versions https://mapzen.com/pelias.
|
571
|
+
* **API key**: required
|
572
|
+
* **Quota**: 6/sec, up to 30k per day, paid plan info at https://mapzen.com/documentation/search/api-keys-rate-limits/#rate-limits
|
573
|
+
* **Region**: World
|
574
|
+
* **SSL support**: yes
|
575
|
+
* **Languages**: en
|
576
|
+
* **Documentation**: https://mapzen.com/documentation/search/search/
|
577
|
+
* **Terms of Service**: http://mapzen.com/terms
|
578
|
+
* **Limitations**: See terms
|
579
|
+
|
580
|
+
#### Pelias (`:pelias`)
|
581
|
+
|
582
|
+
* **About**: Pelias is a modular open-source geocoder using ElasticSearch for fast geocoding https://github.com/pelias/pelias.
|
583
|
+
* **API key**: required
|
584
|
+
* **Quota**: None, self-hosted service.
|
585
|
+
* **Region**: World
|
586
|
+
* **SSL support**: yes
|
587
|
+
* **Languages**: en
|
588
|
+
* **Documentation**: https://mapzen.com/documentation/search/search/
|
589
|
+
* **Terms of Service**: http://mapzen.com/terms
|
590
|
+
* **Limitations**: See terms
|
591
|
+
* **Notes**: Configure your self-hosted pelias with the `endpoint` option: `Geocoder.configure(:lookup => :pelias, :api_key => 'your_api_key', :pelias => {:endpoint => 'self.hosted/pelias'})`. Defaults to `localhost`.
|
592
|
+
|
581
593
|
#### Data Science Toolkit (`:dstk`)
|
582
594
|
|
583
595
|
Data Science Toolkit provides an API whose reponse format is like Google's but which can be set up as a privately hosted service.
|
@@ -663,6 +675,17 @@ This uses the PostcodeAnywhere UK Geocode service, this will geocode any string
|
|
663
675
|
* **Limitations**: ?
|
664
676
|
* **Notes**: To use PostcodeAnywhere you must include an API key: `Geocoder.configure(:lookup => :postcode_anywhere_uk, :api_key => 'your_api_key')`.
|
665
677
|
|
678
|
+
#### LatLon.io (`:latlon`)
|
679
|
+
|
680
|
+
* **API key**: required
|
681
|
+
* **Quota**: Depends on the user's plan (free and paid plans available)
|
682
|
+
* **Region**: US
|
683
|
+
* **SSL support**: yes
|
684
|
+
* **Languages**: en
|
685
|
+
* **Documentation**: https://latlon.io/documentation
|
686
|
+
* **Terms of Service**: ?
|
687
|
+
* **Limitations**: No restrictions on use
|
688
|
+
|
666
689
|
|
667
690
|
### IP Address Services
|
668
691
|
|
@@ -726,6 +749,27 @@ This uses the PostcodeAnywhere UK Geocode service, this will geocode any string
|
|
726
749
|
* **Limitations**: Only good for non-commercial use. For commercial usage please check http://developer.baidu.com/map/question.htm#qa0013
|
727
750
|
* **Notes**: To use Baidu set `Geocoder.configure(:lookup => :baidu_ip, :api_key => "your_api_key")`.
|
728
751
|
|
752
|
+
#### MaxMind GeoIP2 Precision Web Services (`:maxmind_geoip2`)
|
753
|
+
|
754
|
+
* **API key**: required
|
755
|
+
* **Quota**: Request Packs can be purchased
|
756
|
+
* **Region**: world
|
757
|
+
* **SSL support**: yes
|
758
|
+
* **Languages**: English
|
759
|
+
* **Documentation**: http://dev.maxmind.com/geoip/geoip2/web-services/
|
760
|
+
* **Terms of Service**: ?
|
761
|
+
* **Limitations**: ?
|
762
|
+
* **Notes**: You must specify which MaxMind service you are using in your configuration, and also basic authentication. For example: `Geocoder.configure(:maxmind_geoip2 => {:service => :country, :basic_auth => {:user => '', :password => ''}})`.
|
763
|
+
|
764
|
+
#### IPInfo.io (`:ipinfo_io`)
|
765
|
+
|
766
|
+
* **API key**: optional - see http://ipinfo.io/pricing
|
767
|
+
* **Quota**: 1,000/day - more with api key
|
768
|
+
* **Region**: world
|
769
|
+
* **SSL support**: no (not without access key - see http://ipinfo.io/pricing)
|
770
|
+
* **Languages**: English
|
771
|
+
* **Documentation**: http://ipinfo.io/developers
|
772
|
+
* **Terms of Service**: http://ipinfo.io/developers
|
729
773
|
|
730
774
|
### IP Address Local Database Services
|
731
775
|
|
@@ -1033,7 +1077,7 @@ Error Handling
|
|
1033
1077
|
|
1034
1078
|
By default Geocoder will rescue any exceptions raised by calls to a geocoding service and return an empty array. 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:
|
1035
1079
|
|
1036
|
-
Geocoder.configure(:always_raise => [SocketError,
|
1080
|
+
Geocoder.configure(:always_raise => [SocketError, Timeout::Error])
|
1037
1081
|
|
1038
1082
|
You can also do this to raise all exceptions:
|
1039
1083
|
|
@@ -1042,14 +1086,14 @@ You can also do this to raise all exceptions:
|
|
1042
1086
|
The raise-able exceptions are:
|
1043
1087
|
|
1044
1088
|
SocketError
|
1045
|
-
|
1089
|
+
Timeout::Error
|
1046
1090
|
Geocoder::OverQueryLimitError
|
1047
1091
|
Geocoder::RequestDenied
|
1048
1092
|
Geocoder::InvalidRequest
|
1049
1093
|
Geocoder::InvalidApiKey
|
1050
1094
|
Geocoder::ServiceUnavailable
|
1051
1095
|
|
1052
|
-
Note that only a few of the above exceptions are raised by any given lookup, so there's no guarantee if you configure Geocoder to raise `ServiceUnavailable` that it will actually be raised under those conditions (because most APIs don't return 503 when they should; you may get a `
|
1096
|
+
Note that only a few of the above exceptions are raised by any given lookup, so there's no guarantee if you configure Geocoder to raise `ServiceUnavailable` that it will actually be raised under those conditions (because most APIs don't return 503 when they should; you may get a `Timeout::Error` instead). Please see the source code for your particular lookup for details.
|
1053
1097
|
|
1054
1098
|
|
1055
1099
|
Troubleshooting
|
@@ -1071,6 +1115,16 @@ A lot of debugging time can be saved by understanding how Geocoder works with Ac
|
|
1071
1115
|
* using the `pluck` method (selects only a single column)
|
1072
1116
|
* specifying another model through `includes` (selects columns from other tables)
|
1073
1117
|
|
1118
|
+
### Geocoding is Slow
|
1119
|
+
|
1120
|
+
With most lookups, addresses are translated into coordinates via an API that must be accessed through the Internet. These requests are subject to the same bandwidth constraints as every other HTTP request, and will vary in speed depending on network conditions. Furthermore, many of the services supported by Geocoder are free and thus very popular. Often they cannot keep up with demand and their response times become quite bad.
|
1121
|
+
|
1122
|
+
If your application requires quick geocoding responses you will probably need to pay for a non-free service, or--if you're doing IP address geocoding--use a lookup that doesn't require an external (network-accessed) service.
|
1123
|
+
|
1124
|
+
For IP address lookups in Rails applications, it is generally NOT a good idea to run `request.location` during a synchronous page load without understanding the speed/behavior of your configured lookup. If the lookup becomes slow, so will your website.
|
1125
|
+
|
1126
|
+
For the most part, the speed of geocoding requests has little to do with the Geocoder gem. Please take the time to learn about your configured lookup (links to documentation are provided above) before posting performance-related issues.
|
1127
|
+
|
1074
1128
|
### Unexpected Responses from Geocoding Services
|
1075
1129
|
|
1076
1130
|
Take a look at the server's raw response. You can do this by getting the request URL in an app console:
|
@@ -12,7 +12,7 @@ Geocoder.configure(
|
|
12
12
|
|
13
13
|
# Exceptions that should not be rescued by default
|
14
14
|
# (if you want to implement custom error handling);
|
15
|
-
# supports SocketError and
|
15
|
+
# supports SocketError and Timeout::Error
|
16
16
|
# always_raise: [],
|
17
17
|
|
18
18
|
# Calculation options
|
@@ -17,10 +17,6 @@ module Geocoder
|
|
17
17
|
if !options.nil?
|
18
18
|
Configuration.instance.configure(options)
|
19
19
|
end
|
20
|
-
|
21
|
-
if config.lookup == :yahoo
|
22
|
-
Geocoder.log(:warn, "Yahoo BOSS Placefinder API will be discontinued March 31, 2016.")
|
23
|
-
end
|
24
20
|
end
|
25
21
|
|
26
22
|
##
|
@@ -109,7 +105,7 @@ module Geocoder
|
|
109
105
|
|
110
106
|
# exceptions that should not be rescued by default
|
111
107
|
# (if you want to implement custom error handling);
|
112
|
-
# supports SocketError and
|
108
|
+
# supports SocketError and Timeout::Error
|
113
109
|
@data[:always_raise] = []
|
114
110
|
|
115
111
|
# calculation options
|
data/lib/geocoder/lookup.rb
CHANGED
@@ -28,7 +28,6 @@ module Geocoder
|
|
28
28
|
:google,
|
29
29
|
:google_premier,
|
30
30
|
:google_places_details,
|
31
|
-
:yahoo,
|
32
31
|
:bing,
|
33
32
|
:geocoder_ca,
|
34
33
|
:geocoder_us,
|
@@ -36,8 +35,10 @@ module Geocoder
|
|
36
35
|
:nominatim,
|
37
36
|
:mapbox,
|
38
37
|
:mapquest,
|
38
|
+
:mapzen,
|
39
39
|
:opencagedata,
|
40
40
|
:ovi,
|
41
|
+
:pelias,
|
41
42
|
:here,
|
42
43
|
:baidu,
|
43
44
|
:geocodio,
|
@@ -45,7 +46,8 @@ module Geocoder
|
|
45
46
|
:okf,
|
46
47
|
:postcode_anywhere_uk,
|
47
48
|
:geoportail_lu,
|
48
|
-
:test
|
49
|
+
:test,
|
50
|
+
:latlon
|
49
51
|
]
|
50
52
|
end
|
51
53
|
|
@@ -61,7 +63,8 @@ module Geocoder
|
|
61
63
|
:maxmind_local,
|
62
64
|
:telize,
|
63
65
|
:pointpin,
|
64
|
-
:maxmind_geoip2
|
66
|
+
:maxmind_geoip2,
|
67
|
+
:ipinfo_io
|
65
68
|
]
|
66
69
|
end
|
67
70
|
|
@@ -179,7 +179,7 @@ module Geocoder
|
|
179
179
|
raise_error(err) or Geocoder.log(:warn, "Geocoding API connection cannot be established.")
|
180
180
|
rescue Errno::ECONNREFUSED => err
|
181
181
|
raise_error(err) or Geocoder.log(:warn, "Geocoding API connection refused.")
|
182
|
-
rescue
|
182
|
+
rescue Timeout::Error => err
|
183
183
|
raise_error(err) or Geocoder.log(:warn, "Geocoding API not responding fast enough " +
|
184
184
|
"(use Geocoder.configure(:timeout => ...) to set limit).")
|
185
185
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/ipinfo_io'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class IpinfoIo < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Ipinfo.io"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
if configuration.api_key
|
13
|
+
"#{protocol}://ipinfo.io/#{query.sanitized_text}/geo?" + url_query_string(query)
|
14
|
+
else
|
15
|
+
"#{protocol}://ipinfo.io/#{query.sanitized_text}/geo"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# HTTPS available only for paid plans
|
20
|
+
def supported_protocols
|
21
|
+
if configuration.api_key
|
22
|
+
[:http, :https]
|
23
|
+
else
|
24
|
+
[:http]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private # ---------------------------------------------------------------
|
29
|
+
|
30
|
+
def results(query)
|
31
|
+
# don't look up a loopback address, just return the stored result
|
32
|
+
return [reserved_result(query.text)] if query.loopback_ip_address?
|
33
|
+
if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc)
|
34
|
+
[]
|
35
|
+
else
|
36
|
+
[doc]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def empty_result?(doc)
|
41
|
+
!doc.is_a?(Hash) or doc.keys == ["ip"]
|
42
|
+
end
|
43
|
+
|
44
|
+
def reserved_result(ip)
|
45
|
+
{"message" => "Input string is not a valid IP address", "code" => 401}
|
46
|
+
end
|
47
|
+
|
48
|
+
def query_url_params(query)
|
49
|
+
{
|
50
|
+
token: configuration.api_key
|
51
|
+
}.merge(super)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/latlon'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Latlon < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"LatLon.io"
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_api_key_parts
|
12
|
+
["api_key"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
"#{protocol}://latlon.io/api/v1/#{'reverse_' if query.reverse_geocode?}geocode?#{url_query_string(query)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ---------------------------------------------------------------
|
20
|
+
|
21
|
+
def results(query)
|
22
|
+
return [] unless doc = fetch_data(query)
|
23
|
+
if doc['error'].nil?
|
24
|
+
[doc]
|
25
|
+
# The API returned a 404 response, which indicates no results found
|
26
|
+
elsif doc['error']['type'] == 'api_error'
|
27
|
+
[]
|
28
|
+
elsif
|
29
|
+
doc['error']['type'] == 'authentication_error'
|
30
|
+
raise_error(Geocoder::InvalidApiKey) ||
|
31
|
+
Geocoder.log(:warn, "LatLon.io service error: invalid API key.")
|
32
|
+
else
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def supported_protocols
|
38
|
+
[:https]
|
39
|
+
end
|
40
|
+
|
41
|
+
private # ---------------------------------------------------------------
|
42
|
+
|
43
|
+
def query_url_params(query)
|
44
|
+
if query.reverse_geocode?
|
45
|
+
{
|
46
|
+
:token => configuration.api_key,
|
47
|
+
:lat => query.coordinates[0],
|
48
|
+
:lon => query.coordinates[1]
|
49
|
+
}.merge(super)
|
50
|
+
else
|
51
|
+
{
|
52
|
+
:token => configuration.api_key,
|
53
|
+
:address => query.sanitized_text
|
54
|
+
}.merge(super)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'geocoder/lookups/pelias'
|
2
|
+
require 'geocoder/results/mapzen'
|
3
|
+
|
4
|
+
# https://mapzen.com/documentation/search/search/ for more information
|
5
|
+
module Geocoder::Lookup
|
6
|
+
class Mapzen < Pelias
|
7
|
+
def name
|
8
|
+
'Mapzen'
|
9
|
+
end
|
10
|
+
|
11
|
+
def endpoint
|
12
|
+
configuration[:endpoint] || 'search.mapzen.com'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/pelias'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Pelias < Base
|
6
|
+
def name
|
7
|
+
'Pelias'
|
8
|
+
end
|
9
|
+
|
10
|
+
def endpoint
|
11
|
+
configuration[:endpoint] || 'localhost'
|
12
|
+
end
|
13
|
+
|
14
|
+
def query_url(query)
|
15
|
+
query_type = query.reverse_geocode? ? 'reverse' : 'search'
|
16
|
+
"#{protocol}://#{endpoint}/v1/#{query_type}?" + url_query_string(query)
|
17
|
+
end
|
18
|
+
|
19
|
+
def required_api_key_parts
|
20
|
+
['search-XXXX']
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def query_url_params(query)
|
26
|
+
{
|
27
|
+
api_key: configuration.api_key,
|
28
|
+
text: query.text,
|
29
|
+
size: 1
|
30
|
+
}.merge(super(query))
|
31
|
+
end
|
32
|
+
|
33
|
+
def results(query)
|
34
|
+
return [] unless doc = fetch_data(query)
|
35
|
+
|
36
|
+
# not all responses include a meta
|
37
|
+
if doc['meta']
|
38
|
+
error = doc.fetch('results', {}).fetch('error', {})
|
39
|
+
message = error.fetch('type', 'Unknown Error') + ': ' + error.fetch('message', 'No message')
|
40
|
+
log_message = 'Pelias Geocoding API error - ' + message
|
41
|
+
case doc['meta']['status_code']
|
42
|
+
when '200'
|
43
|
+
# nothing to see here
|
44
|
+
when '403'
|
45
|
+
raise_error(Geocoder::RequestDenied, message) || Geocoder.log(:warn, log_message)
|
46
|
+
when '429'
|
47
|
+
raise_error(Geocoder::OverQueryLimitError, message) || Geocoder.log(:warn, log_message)
|
48
|
+
else
|
49
|
+
raise_error(Geocoder::Error, message) || Geocoder.log(:warn, log_message)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
doc['features'] || []
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class IpinfoIo < Base
|
5
|
+
|
6
|
+
def address(format = :full)
|
7
|
+
"#{city} #{postal_code}, #{country}".sub(/^[ ,]*/, "")
|
8
|
+
end
|
9
|
+
|
10
|
+
def latitude
|
11
|
+
@data['loc'].split(',')[0].to_f
|
12
|
+
end
|
13
|
+
|
14
|
+
def longitude
|
15
|
+
@data['loc'].split(',')[1].to_f
|
16
|
+
end
|
17
|
+
|
18
|
+
def coordinates
|
19
|
+
[@data['loc'].split(',')[0].to_f, @data['loc'].split(',')[1].to_f]
|
20
|
+
end
|
21
|
+
|
22
|
+
def city
|
23
|
+
@data['city']
|
24
|
+
end
|
25
|
+
|
26
|
+
def state
|
27
|
+
@data['region']
|
28
|
+
end
|
29
|
+
|
30
|
+
def country
|
31
|
+
@data['country']
|
32
|
+
end
|
33
|
+
|
34
|
+
def postal_code
|
35
|
+
@data['postal']
|
36
|
+
end
|
37
|
+
|
38
|
+
def country_code
|
39
|
+
@data.fetch('country', '')
|
40
|
+
end
|
41
|
+
|
42
|
+
def state_code
|
43
|
+
@data.fetch('region_code', '')
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.response_attributes
|
47
|
+
%w['ip', 'city', 'region', 'country', 'latitude', 'longitude', 'postal_code']
|
48
|
+
end
|
49
|
+
|
50
|
+
response_attributes.each do |a|
|
51
|
+
define_method a do
|
52
|
+
@data[a]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class Latlon < Base
|
5
|
+
|
6
|
+
def city
|
7
|
+
address_components["city"]
|
8
|
+
end
|
9
|
+
|
10
|
+
def coordinates
|
11
|
+
[@data['lat'].to_f, @data['lon'].to_f]
|
12
|
+
end
|
13
|
+
|
14
|
+
def country
|
15
|
+
"United States" # LatLon.io only supports the US
|
16
|
+
end
|
17
|
+
|
18
|
+
def country_code
|
19
|
+
"US" # LatLon.io only supports the US
|
20
|
+
end
|
21
|
+
|
22
|
+
def formatted_address(format = :full)
|
23
|
+
address_components["address"]
|
24
|
+
end
|
25
|
+
alias_method :address, :formatted_address
|
26
|
+
|
27
|
+
def number
|
28
|
+
address_components["number"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def prefix
|
32
|
+
address_components["prefix"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def state
|
36
|
+
address_components["state"]
|
37
|
+
end
|
38
|
+
alias_method :state_code, :state
|
39
|
+
|
40
|
+
def street
|
41
|
+
[street_name, street_type].compact.join(' ')
|
42
|
+
end
|
43
|
+
|
44
|
+
def street_name
|
45
|
+
address_components["street_name"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def street_type
|
49
|
+
address_components["street_type"]
|
50
|
+
end
|
51
|
+
|
52
|
+
def suffix
|
53
|
+
address_components["suffix"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def unit
|
57
|
+
address_components["unit"]
|
58
|
+
end
|
59
|
+
|
60
|
+
def zip
|
61
|
+
address_components["zip"]
|
62
|
+
end
|
63
|
+
alias_method :postal_code, :zip
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def address_components
|
68
|
+
@data["address"] || {}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'geocoder/results/base'
|
2
|
+
|
3
|
+
module Geocoder::Result
|
4
|
+
class Pelias < Base
|
5
|
+
def address(format = :full)
|
6
|
+
properties['label']
|
7
|
+
end
|
8
|
+
|
9
|
+
def city
|
10
|
+
locality
|
11
|
+
end
|
12
|
+
|
13
|
+
def coordinates
|
14
|
+
geometry['coordinates'].reverse
|
15
|
+
end
|
16
|
+
|
17
|
+
def country_code
|
18
|
+
properties['country_a']
|
19
|
+
end
|
20
|
+
|
21
|
+
def postal_code
|
22
|
+
properties['postalcode'].to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def province
|
26
|
+
state
|
27
|
+
end
|
28
|
+
|
29
|
+
def state
|
30
|
+
properties['region']
|
31
|
+
end
|
32
|
+
|
33
|
+
def state_code
|
34
|
+
properties['region_a']
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.response_attributes
|
38
|
+
%w[county confidence country gid id layer localadmin locality neighborhood]
|
39
|
+
end
|
40
|
+
|
41
|
+
response_attributes.each do |a|
|
42
|
+
define_method a do
|
43
|
+
properties[a]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def geometry
|
50
|
+
@data.fetch('geometry', {})
|
51
|
+
end
|
52
|
+
|
53
|
+
def properties
|
54
|
+
@data.fetch('properties', {})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
data/lib/geocoder/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geocoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Reisner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Provides object geocoding (by street or IP address), reverse geocoding
|
14
14
|
(coordinates to street address), distance queries for ActiveRecord and Mongoid,
|
@@ -62,8 +62,11 @@ files:
|
|
62
62
|
- lib/geocoder/lookups/google_places_details.rb
|
63
63
|
- lib/geocoder/lookups/google_premier.rb
|
64
64
|
- lib/geocoder/lookups/here.rb
|
65
|
+
- lib/geocoder/lookups/ipinfo_io.rb
|
66
|
+
- lib/geocoder/lookups/latlon.rb
|
65
67
|
- lib/geocoder/lookups/mapbox.rb
|
66
68
|
- lib/geocoder/lookups/mapquest.rb
|
69
|
+
- lib/geocoder/lookups/mapzen.rb
|
67
70
|
- lib/geocoder/lookups/maxmind.rb
|
68
71
|
- lib/geocoder/lookups/maxmind_geoip2.rb
|
69
72
|
- lib/geocoder/lookups/maxmind_local.rb
|
@@ -71,12 +74,12 @@ files:
|
|
71
74
|
- lib/geocoder/lookups/okf.rb
|
72
75
|
- lib/geocoder/lookups/opencagedata.rb
|
73
76
|
- lib/geocoder/lookups/ovi.rb
|
77
|
+
- lib/geocoder/lookups/pelias.rb
|
74
78
|
- lib/geocoder/lookups/pointpin.rb
|
75
79
|
- lib/geocoder/lookups/postcode_anywhere_uk.rb
|
76
80
|
- lib/geocoder/lookups/smarty_streets.rb
|
77
81
|
- lib/geocoder/lookups/telize.rb
|
78
82
|
- lib/geocoder/lookups/test.rb
|
79
|
-
- lib/geocoder/lookups/yahoo.rb
|
80
83
|
- lib/geocoder/lookups/yandex.rb
|
81
84
|
- lib/geocoder/models/active_record.rb
|
82
85
|
- lib/geocoder/models/base.rb
|
@@ -102,8 +105,11 @@ files:
|
|
102
105
|
- lib/geocoder/results/google_places_details.rb
|
103
106
|
- lib/geocoder/results/google_premier.rb
|
104
107
|
- lib/geocoder/results/here.rb
|
108
|
+
- lib/geocoder/results/ipinfo_io.rb
|
109
|
+
- lib/geocoder/results/latlon.rb
|
105
110
|
- lib/geocoder/results/mapbox.rb
|
106
111
|
- lib/geocoder/results/mapquest.rb
|
112
|
+
- lib/geocoder/results/mapzen.rb
|
107
113
|
- lib/geocoder/results/maxmind.rb
|
108
114
|
- lib/geocoder/results/maxmind_geoip2.rb
|
109
115
|
- lib/geocoder/results/maxmind_local.rb
|
@@ -111,12 +117,12 @@ files:
|
|
111
117
|
- lib/geocoder/results/okf.rb
|
112
118
|
- lib/geocoder/results/opencagedata.rb
|
113
119
|
- lib/geocoder/results/ovi.rb
|
120
|
+
- lib/geocoder/results/pelias.rb
|
114
121
|
- lib/geocoder/results/pointpin.rb
|
115
122
|
- lib/geocoder/results/postcode_anywhere_uk.rb
|
116
123
|
- lib/geocoder/results/smarty_streets.rb
|
117
124
|
- lib/geocoder/results/telize.rb
|
118
125
|
- lib/geocoder/results/test.rb
|
119
|
-
- lib/geocoder/results/yahoo.rb
|
120
126
|
- lib/geocoder/results/yandex.rb
|
121
127
|
- lib/geocoder/sql.rb
|
122
128
|
- lib/geocoder/stores/active_record.rb
|
@@ -127,7 +133,6 @@ files:
|
|
127
133
|
- lib/geocoder/version.rb
|
128
134
|
- lib/hash_recursive_merge.rb
|
129
135
|
- lib/maxmind_database.rb
|
130
|
-
- lib/oauth_util.rb
|
131
136
|
- lib/tasks/geocoder.rake
|
132
137
|
- lib/tasks/maxmind.rake
|
133
138
|
homepage: http://www.rubygeocoder.com
|
@@ -150,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
155
|
version: '0'
|
151
156
|
requirements: []
|
152
157
|
rubyforge_project:
|
153
|
-
rubygems_version: 2.5
|
158
|
+
rubygems_version: 2.4.5
|
154
159
|
signing_key:
|
155
160
|
specification_version: 4
|
156
161
|
summary: Complete geocoding solution for Ruby.
|
@@ -1,89 +0,0 @@
|
|
1
|
-
require 'geocoder/lookups/base'
|
2
|
-
require "geocoder/results/yahoo"
|
3
|
-
require 'oauth_util'
|
4
|
-
|
5
|
-
module Geocoder::Lookup
|
6
|
-
class Yahoo < Base
|
7
|
-
|
8
|
-
def name
|
9
|
-
"Yahoo BOSS"
|
10
|
-
end
|
11
|
-
|
12
|
-
def map_link_url(coordinates)
|
13
|
-
"http://maps.yahoo.com/#lat=#{coordinates[0]}&lon=#{coordinates[1]}"
|
14
|
-
end
|
15
|
-
|
16
|
-
def required_api_key_parts
|
17
|
-
["consumer key", "consumer secret"]
|
18
|
-
end
|
19
|
-
|
20
|
-
def query_url(query)
|
21
|
-
parsed_url = URI.parse(raw_url(query))
|
22
|
-
o = OauthUtil.new
|
23
|
-
o.consumer_key = configuration.api_key[0]
|
24
|
-
o.consumer_secret = configuration.api_key[1]
|
25
|
-
base_url + o.sign(parsed_url).query_string
|
26
|
-
end
|
27
|
-
|
28
|
-
private # ---------------------------------------------------------------
|
29
|
-
|
30
|
-
def results(query)
|
31
|
-
Geocoder.log(:warn, "Yahoo BOSS Placefinder API will be discontinued March 31, 2016.")
|
32
|
-
return [] unless doc = fetch_data(query)
|
33
|
-
doc = doc['bossresponse']
|
34
|
-
if doc['responsecode'].to_i == 200
|
35
|
-
if doc['placefinder']['count'].to_i > 0
|
36
|
-
return doc['placefinder']['results']
|
37
|
-
else
|
38
|
-
return []
|
39
|
-
end
|
40
|
-
else
|
41
|
-
Geocoder.log(:warn, "Yahoo Geocoding API error: #{doc['responsecode']} (#{doc['reason']}).")
|
42
|
-
return []
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
##
|
47
|
-
# Yahoo returns errors as XML even when JSON format is specified.
|
48
|
-
# Handle that here, without parsing the XML
|
49
|
-
# (which would add unnecessary complexity).
|
50
|
-
# Yahoo auth errors can also be cryptic, so add raw error desc
|
51
|
-
# to warning message.
|
52
|
-
#
|
53
|
-
def parse_raw_data(raw_data)
|
54
|
-
if raw_data.match(/^<\?xml/)
|
55
|
-
if raw_data.include?("Rate Limit Exceeded")
|
56
|
-
raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.")
|
57
|
-
elsif raw_data =~ /<yahoo:description>(Please provide valid credentials.*)<\/yahoo:description>/i
|
58
|
-
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key. Error response: #{$1}")
|
59
|
-
end
|
60
|
-
else
|
61
|
-
super(raw_data)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def query_url_params(query)
|
66
|
-
lang = (query.language || configuration.language).to_s
|
67
|
-
lang += '_US' if lang == 'en'
|
68
|
-
{
|
69
|
-
:location => query.sanitized_text,
|
70
|
-
:flags => "JXTSR",
|
71
|
-
:gflags => "AC#{'R' if query.reverse_geocode?}",
|
72
|
-
:locale => lang,
|
73
|
-
:appid => configuration.api_key
|
74
|
-
}.merge(super)
|
75
|
-
end
|
76
|
-
|
77
|
-
def cache_key(query)
|
78
|
-
raw_url(query)
|
79
|
-
end
|
80
|
-
|
81
|
-
def base_url
|
82
|
-
"#{protocol}://yboss.yahooapis.com/geo/placefinder?"
|
83
|
-
end
|
84
|
-
|
85
|
-
def raw_url(query)
|
86
|
-
base_url + url_query_string(query)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'geocoder/results/base'
|
2
|
-
|
3
|
-
module Geocoder::Result
|
4
|
-
class Yahoo < Base
|
5
|
-
|
6
|
-
def address(format = :full)
|
7
|
-
(1..4).to_a.map{ |i| @data["line#{i}"] }.reject{ |i| i.nil? or i == "" }.join(", ")
|
8
|
-
end
|
9
|
-
|
10
|
-
def city
|
11
|
-
@data['city']
|
12
|
-
end
|
13
|
-
|
14
|
-
def state
|
15
|
-
@data['state']
|
16
|
-
end
|
17
|
-
|
18
|
-
def state_code
|
19
|
-
@data['statecode']
|
20
|
-
end
|
21
|
-
|
22
|
-
def country
|
23
|
-
@data['country']
|
24
|
-
end
|
25
|
-
|
26
|
-
def country_code
|
27
|
-
@data['countrycode']
|
28
|
-
end
|
29
|
-
|
30
|
-
def postal_code
|
31
|
-
@data['postal']
|
32
|
-
end
|
33
|
-
|
34
|
-
def address_hash
|
35
|
-
@data['hash']
|
36
|
-
end
|
37
|
-
|
38
|
-
def viewport
|
39
|
-
boundingbox = @data['boundingbox'] || fail
|
40
|
-
%w(south west north east).map{ |i| boundingbox[i].to_f }
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.response_attributes
|
44
|
-
%w[quality offsetlat offsetlon radius boundingbox name
|
45
|
-
line1 line2 line3 line4 cross house street xstreet unittype unit
|
46
|
-
city state statecode country countrycode postal
|
47
|
-
neighborhood county countycode
|
48
|
-
level0 level1 level2 level3 level4 level0code level1code level2code
|
49
|
-
timezone areacode uzip hash woeid woetype]
|
50
|
-
end
|
51
|
-
|
52
|
-
response_attributes.each do |a|
|
53
|
-
unless method_defined?(a)
|
54
|
-
define_method a do
|
55
|
-
@data[a]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
data/lib/oauth_util.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# A utility for signing an url using OAuth in a way that's convenient for debugging
|
2
|
-
# Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
|
3
|
-
# Source: http://gist.github.com/383159
|
4
|
-
# License: http://gist.github.com/375593
|
5
|
-
# Usage: see example.rb below
|
6
|
-
#
|
7
|
-
# NOTE: This file has been modified from the original Gist:
|
8
|
-
#
|
9
|
-
# 1. Fix to prevent param-array conversion, as mentioned in Gist comment.
|
10
|
-
# 2. Query string escaping has been changed. See:
|
11
|
-
# https://github.com/alexreisner/geocoder/pull/360
|
12
|
-
#
|
13
|
-
|
14
|
-
require 'uri'
|
15
|
-
require 'cgi'
|
16
|
-
require 'openssl'
|
17
|
-
require 'base64'
|
18
|
-
|
19
|
-
class OauthUtil
|
20
|
-
|
21
|
-
attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
|
22
|
-
:sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
|
23
|
-
|
24
|
-
def initialize
|
25
|
-
@consumer_key = ''
|
26
|
-
@consumer_secret = ''
|
27
|
-
@token = ''
|
28
|
-
@token_secret = ''
|
29
|
-
@req_method = 'GET'
|
30
|
-
@sig_method = 'HMAC-SHA1'
|
31
|
-
@oauth_version = '1.0'
|
32
|
-
@callback_url = ''
|
33
|
-
end
|
34
|
-
|
35
|
-
# openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
|
36
|
-
# ref http://snippets.dzone.com/posts/show/491
|
37
|
-
def nonce
|
38
|
-
Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
|
39
|
-
end
|
40
|
-
|
41
|
-
def percent_encode( string )
|
42
|
-
|
43
|
-
# ref http://snippets.dzone.com/posts/show/1260
|
44
|
-
return URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
|
45
|
-
end
|
46
|
-
|
47
|
-
# @ref http://oauth.net/core/1.0/#rfc.section.9.2
|
48
|
-
def signature
|
49
|
-
key = percent_encode( @consumer_secret ) + '&' + percent_encode( @token_secret )
|
50
|
-
|
51
|
-
# ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
|
52
|
-
digest = OpenSSL::Digest.new( 'sha1' )
|
53
|
-
hmac = OpenSSL::HMAC.digest( digest, key, @base_str )
|
54
|
-
|
55
|
-
# ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
|
56
|
-
Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
|
57
|
-
end
|
58
|
-
|
59
|
-
# sort (very important as it affects the signature), concat, and percent encode
|
60
|
-
# @ref http://oauth.net/core/1.0/#rfc.section.9.1.1
|
61
|
-
# @ref http://oauth.net/core/1.0/#9.2.1
|
62
|
-
# @ref http://oauth.net/core/1.0/#rfc.section.A.5.1
|
63
|
-
def query_string
|
64
|
-
pairs = []
|
65
|
-
@params.sort.each { | key, val |
|
66
|
-
pairs.push( "#{ CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') } }=#{ CGI.escape(val.to_s) }" )
|
67
|
-
}
|
68
|
-
pairs.join '&'
|
69
|
-
end
|
70
|
-
|
71
|
-
# organize params & create signature
|
72
|
-
def sign( parsed_url )
|
73
|
-
|
74
|
-
@params = {
|
75
|
-
'oauth_consumer_key' => @consumer_key,
|
76
|
-
'oauth_nonce' => nonce,
|
77
|
-
'oauth_signature_method' => @sig_method,
|
78
|
-
'oauth_timestamp' => Time.now.to_i.to_s,
|
79
|
-
'oauth_version' => @oauth_version
|
80
|
-
}
|
81
|
-
|
82
|
-
# if url has query, merge key/values into params obj overwriting defaults
|
83
|
-
if parsed_url.query
|
84
|
-
CGI.parse( parsed_url.query ).each do |k,v|
|
85
|
-
if v.is_a?(Array) && v.count == 1
|
86
|
-
@params[k] = v.first
|
87
|
-
else
|
88
|
-
@params[k] = v
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# @ref http://oauth.net/core/1.0/#rfc.section.9.1.2
|
94
|
-
@req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
|
95
|
-
|
96
|
-
# create base str. make it an object attr for ez debugging
|
97
|
-
# ref http://oauth.net/core/1.0/#anchor14
|
98
|
-
@base_str = [
|
99
|
-
@req_method,
|
100
|
-
percent_encode( req_url ),
|
101
|
-
|
102
|
-
# normalization is just x-www-form-urlencoded
|
103
|
-
percent_encode( query_string )
|
104
|
-
|
105
|
-
].join( '&' )
|
106
|
-
|
107
|
-
# add signature
|
108
|
-
@params[ 'oauth_signature' ] = signature
|
109
|
-
|
110
|
-
return self
|
111
|
-
end
|
112
|
-
end
|