geocoder 1.1.9 → 1.3.7
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 +157 -0
- data/README.md +467 -70
- data/examples/reverse_geocode_job.rb +40 -0
- data/lib/generators/geocoder/config/templates/initializer.rb +16 -16
- data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
- data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
- data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
- data/lib/geocoder/cache.rb +3 -2
- data/lib/geocoder/calculations.rb +44 -2
- data/lib/geocoder/configuration.rb +17 -10
- data/lib/geocoder/esri_token.rb +38 -0
- data/lib/geocoder/exceptions.rb +19 -0
- data/lib/geocoder/ip_address.rb +13 -0
- data/lib/geocoder/kernel_logger.rb +25 -0
- data/lib/geocoder/logger.rb +47 -0
- data/lib/geocoder/lookup.rb +32 -8
- data/lib/geocoder/lookups/baidu.rb +18 -13
- data/lib/geocoder/lookups/baidu_ip.rb +59 -0
- data/lib/geocoder/lookups/base.rb +81 -19
- data/lib/geocoder/lookups/bing.rb +40 -7
- data/lib/geocoder/lookups/esri.rb +42 -5
- data/lib/geocoder/lookups/freegeoip.rb +9 -1
- data/lib/geocoder/lookups/geocoder_ca.rb +1 -2
- data/lib/geocoder/lookups/geocoder_us.rb +6 -2
- data/lib/geocoder/lookups/geocodio.rb +42 -0
- data/lib/geocoder/lookups/geoip2.rb +45 -0
- data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
- data/lib/geocoder/lookups/google.rb +29 -5
- data/lib/geocoder/lookups/google_places_details.rb +50 -0
- data/lib/geocoder/lookups/google_premier.rb +1 -1
- data/lib/geocoder/lookups/here.rb +62 -0
- data/lib/geocoder/lookups/ipapi_com.rb +86 -0
- data/lib/geocoder/lookups/ipinfo_io.rb +55 -0
- data/lib/geocoder/lookups/latlon.rb +59 -0
- data/lib/geocoder/lookups/mapbox.rb +53 -0
- data/lib/geocoder/lookups/mapquest.rb +6 -6
- data/lib/geocoder/lookups/mapzen.rb +15 -0
- data/lib/geocoder/lookups/maxmind.rb +4 -2
- data/lib/geocoder/lookups/maxmind_geoip2.rb +69 -0
- data/lib/geocoder/lookups/maxmind_local.rb +65 -0
- data/lib/geocoder/lookups/nominatim.rb +9 -1
- data/lib/geocoder/lookups/okf.rb +44 -0
- data/lib/geocoder/lookups/opencagedata.rb +58 -0
- data/lib/geocoder/lookups/pelias.rb +64 -0
- data/lib/geocoder/lookups/pointpin.rb +68 -0
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +51 -0
- data/lib/geocoder/lookups/smarty_streets.rb +53 -0
- data/lib/geocoder/lookups/telize.rb +55 -0
- data/lib/geocoder/lookups/yandex.rb +8 -4
- data/lib/geocoder/models/active_record.rb +7 -3
- data/lib/geocoder/models/base.rb +1 -4
- data/lib/geocoder/models/mongo_base.rb +6 -4
- data/lib/geocoder/query.rb +9 -5
- data/lib/geocoder/railtie.rb +1 -1
- data/lib/geocoder/request.rb +74 -12
- data/lib/geocoder/results/baidu_ip.rb +62 -0
- data/lib/geocoder/results/bing.rb +4 -0
- data/lib/geocoder/results/esri.rb +30 -6
- data/lib/geocoder/results/freegeoip.rb +2 -2
- data/lib/geocoder/results/geocodio.rb +70 -0
- data/lib/geocoder/results/geoip2.rb +62 -0
- data/lib/geocoder/results/geoportail_lu.rb +69 -0
- data/lib/geocoder/results/google.rb +15 -0
- data/lib/geocoder/results/google_places_details.rb +35 -0
- data/lib/geocoder/results/here.rb +71 -0
- data/lib/geocoder/results/ipapi_com.rb +45 -0
- data/lib/geocoder/results/ipinfo_io.rb +48 -0
- data/lib/geocoder/results/latlon.rb +71 -0
- data/lib/geocoder/results/mapbox.rb +47 -0
- data/lib/geocoder/results/mapquest.rb +5 -8
- data/lib/geocoder/results/mapzen.rb +5 -0
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/maxmind_local.rb +49 -0
- data/lib/geocoder/results/nominatim.rb +6 -1
- data/lib/geocoder/results/okf.rb +106 -0
- data/lib/geocoder/results/opencagedata.rb +90 -0
- data/lib/geocoder/results/ovi.rb +9 -0
- data/lib/geocoder/results/pelias.rb +58 -0
- data/lib/geocoder/results/pointpin.rb +40 -0
- data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
- data/lib/geocoder/results/smarty_streets.rb +106 -0
- data/lib/geocoder/results/telize.rb +45 -0
- data/lib/geocoder/results/test.rb +20 -3
- data/lib/geocoder/results/yandex.rb +18 -6
- data/lib/geocoder/sql.rb +16 -15
- data/lib/geocoder/stores/active_record.rb +51 -18
- data/lib/geocoder/stores/base.rb +8 -12
- data/lib/geocoder/stores/mongo_base.rb +0 -31
- data/lib/geocoder/version.rb +1 -1
- data/lib/geocoder.rb +6 -13
- data/lib/maxmind_database.rb +109 -0
- data/lib/tasks/geocoder.rake +14 -3
- data/lib/tasks/maxmind.rake +73 -0
- metadata +59 -85
- data/.gitignore +0 -5
- data/.travis.yml +0 -27
- data/Rakefile +0 -25
- data/gemfiles/Gemfile.mongoid-2.4.x +0 -15
- data/lib/geocoder/lookups/yahoo.rb +0 -86
- data/lib/geocoder/results/yahoo.rb +0 -55
- data/lib/oauth_util.rb +0 -112
- data/test/active_record_test.rb +0 -15
- data/test/cache_test.rb +0 -35
- data/test/calculations_test.rb +0 -211
- data/test/configuration_test.rb +0 -78
- data/test/custom_block_test.rb +0 -32
- data/test/error_handling_test.rb +0 -43
- data/test/fixtures/baidu_invalid_key +0 -1
- data/test/fixtures/baidu_no_results +0 -1
- data/test/fixtures/baidu_reverse +0 -1
- data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
- data/test/fixtures/bing_invalid_key +0 -1
- data/test/fixtures/bing_madison_square_garden +0 -40
- data/test/fixtures/bing_no_results +0 -16
- data/test/fixtures/bing_reverse +0 -42
- data/test/fixtures/esri_madison_square_garden +0 -59
- data/test/fixtures/esri_no_results +0 -8
- data/test/fixtures/esri_reverse +0 -21
- data/test/fixtures/freegeoip_74_200_247_59 +0 -12
- data/test/fixtures/freegeoip_no_results +0 -1
- data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
- data/test/fixtures/geocoder_ca_no_results +0 -1
- data/test/fixtures/geocoder_ca_reverse +0 -34
- data/test/fixtures/geocoder_us_madison_square_garden +0 -1
- data/test/fixtures/geocoder_us_no_results +0 -1
- data/test/fixtures/google_garbage +0 -456
- data/test/fixtures/google_madison_square_garden +0 -57
- data/test/fixtures/google_no_city_data +0 -44
- data/test/fixtures/google_no_locality +0 -51
- data/test/fixtures/google_no_results +0 -4
- data/test/fixtures/google_over_limit +0 -4
- data/test/fixtures/mapquest_error +0 -16
- data/test/fixtures/mapquest_invalid_api_key +0 -16
- data/test/fixtures/mapquest_invalid_request +0 -16
- data/test/fixtures/mapquest_madison_square_garden +0 -52
- data/test/fixtures/mapquest_no_results +0 -16
- data/test/fixtures/maxmind_24_24_24_21 +0 -1
- data/test/fixtures/maxmind_24_24_24_22 +0 -1
- data/test/fixtures/maxmind_24_24_24_23 +0 -1
- data/test/fixtures/maxmind_24_24_24_24 +0 -1
- data/test/fixtures/maxmind_74_200_247_59 +0 -1
- data/test/fixtures/maxmind_invalid_key +0 -1
- data/test/fixtures/maxmind_no_results +0 -1
- data/test/fixtures/nominatim_madison_square_garden +0 -150
- data/test/fixtures/nominatim_no_results +0 -1
- data/test/fixtures/ovi_madison_square_garden +0 -72
- data/test/fixtures/ovi_no_results +0 -8
- data/test/fixtures/yahoo_error +0 -1
- data/test/fixtures/yahoo_invalid_key +0 -2
- data/test/fixtures/yahoo_madison_square_garden +0 -52
- data/test/fixtures/yahoo_no_results +0 -10
- data/test/fixtures/yahoo_over_limit +0 -2
- data/test/fixtures/yandex_invalid_key +0 -1
- data/test/fixtures/yandex_kremlin +0 -48
- data/test/fixtures/yandex_no_city_and_town +0 -112
- data/test/fixtures/yandex_no_results +0 -16
- data/test/geocoder_test.rb +0 -59
- data/test/https_test.rb +0 -16
- data/test/integration/smoke_test.rb +0 -26
- data/test/lookup_test.rb +0 -117
- data/test/method_aliases_test.rb +0 -25
- data/test/mongoid_test.rb +0 -46
- data/test/mongoid_test_helper.rb +0 -43
- data/test/near_test.rb +0 -61
- data/test/oauth_util_test.rb +0 -30
- data/test/proxy_test.rb +0 -36
- data/test/query_test.rb +0 -52
- data/test/request_test.rb +0 -29
- data/test/result_test.rb +0 -42
- data/test/services_test.rb +0 -393
- data/test/test_helper.rb +0 -289
- data/test/test_mode_test.rb +0 -59
@@ -0,0 +1,50 @@
|
|
1
|
+
require "geocoder/lookups/google"
|
2
|
+
require "geocoder/results/google_places_details"
|
3
|
+
|
4
|
+
module Geocoder
|
5
|
+
module Lookup
|
6
|
+
class GooglePlacesDetails < Google
|
7
|
+
def name
|
8
|
+
"Google Places Details"
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_api_key_parts
|
12
|
+
["key"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def supported_protocols
|
16
|
+
[:https]
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_url(query)
|
20
|
+
"#{protocol}://maps.googleapis.com/maps/api/place/details/json?#{url_query_string(query)}"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def results(query)
|
26
|
+
return [] unless doc = fetch_data(query)
|
27
|
+
|
28
|
+
case doc["status"]
|
29
|
+
when "OK"
|
30
|
+
return [doc["result"]]
|
31
|
+
when "OVER_QUERY_LIMIT"
|
32
|
+
raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Google Places Details API error: over query limit.")
|
33
|
+
when "REQUEST_DENIED"
|
34
|
+
raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Google Places Details API error: request denied.")
|
35
|
+
when "INVALID_REQUEST"
|
36
|
+
raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Google Places Details API error: invalid request.")
|
37
|
+
end
|
38
|
+
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
|
42
|
+
def query_url_google_params(query)
|
43
|
+
{
|
44
|
+
placeid: query.text,
|
45
|
+
language: query.language || configuration.language
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -31,7 +31,7 @@ module Geocoder::Lookup
|
|
31
31
|
|
32
32
|
def sign(string)
|
33
33
|
raw_private_key = url_safe_base64_decode(configuration.api_key[0])
|
34
|
-
digest = OpenSSL::Digest
|
34
|
+
digest = OpenSSL::Digest.new('sha1')
|
35
35
|
raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string)
|
36
36
|
url_safe_base64_encode(raw_signature)
|
37
37
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/here'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Here < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Here"
|
9
|
+
end
|
10
|
+
|
11
|
+
def required_api_key_parts
|
12
|
+
["app_id", "app_code"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_url(query)
|
16
|
+
"#{protocol}://#{if query.reverse_geocode? then 'reverse.' end}geocoder.api.here.com/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
|
17
|
+
end
|
18
|
+
|
19
|
+
private # ---------------------------------------------------------------
|
20
|
+
|
21
|
+
def results(query)
|
22
|
+
return [] unless doc = fetch_data(query)
|
23
|
+
return [] unless doc['Response'] && doc['Response']['View']
|
24
|
+
if r=doc['Response']['View']
|
25
|
+
return [] if r.nil? || !r.is_a?(Array) || r.empty?
|
26
|
+
return r.first['Result']
|
27
|
+
end
|
28
|
+
[]
|
29
|
+
end
|
30
|
+
|
31
|
+
def query_url_params(query)
|
32
|
+
options = {
|
33
|
+
:gen=>4,
|
34
|
+
:app_id=>api_key,
|
35
|
+
:app_code=>api_code
|
36
|
+
}
|
37
|
+
|
38
|
+
if query.reverse_geocode?
|
39
|
+
super.merge(options).merge(
|
40
|
+
:prox=>query.sanitized_text,
|
41
|
+
:mode=>:retrieveAddresses
|
42
|
+
)
|
43
|
+
else
|
44
|
+
super.merge(options).merge(
|
45
|
+
:searchtext=>query.sanitized_text
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def api_key
|
51
|
+
if a=configuration.api_key
|
52
|
+
return a.first if a.is_a?(Array)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def api_code
|
57
|
+
if a=configuration.api_key
|
58
|
+
return a.last if a.is_a?(Array)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/ipapi_com'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class IpapiCom < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"ip-api.com"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
domain = configuration.api_key ? "pro.ip-api.com" : "ip-api.com"
|
13
|
+
url_ = "#{protocol}://#{domain}/json/#{query.sanitized_text}"
|
14
|
+
|
15
|
+
if (params = url_query_string(query)) && !params.empty?
|
16
|
+
url_ + "?" + params
|
17
|
+
else
|
18
|
+
url_
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def supported_protocols
|
23
|
+
if configuration.api_key
|
24
|
+
[:http, :https]
|
25
|
+
else
|
26
|
+
[:http]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parse_raw_data(raw_data)
|
34
|
+
if raw_data.chomp == "invalid key"
|
35
|
+
invalid_key_result
|
36
|
+
else
|
37
|
+
super(raw_data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def results(query)
|
42
|
+
return [reserved_result(query.text)] if query.loopback_ip_address?
|
43
|
+
|
44
|
+
return [] unless doc = fetch_data(query)
|
45
|
+
|
46
|
+
if doc["message"] == "invalid key"
|
47
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.")
|
48
|
+
return []
|
49
|
+
else
|
50
|
+
return [doc]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def reserved_result(query)
|
55
|
+
{
|
56
|
+
"message" => "reserved range",
|
57
|
+
"query" => query,
|
58
|
+
"status" => "fail",
|
59
|
+
"ip" => query,
|
60
|
+
"city" => "",
|
61
|
+
"region_code" => "",
|
62
|
+
"region_name" => "",
|
63
|
+
"metrocode" => "",
|
64
|
+
"zipcode" => "",
|
65
|
+
"latitude" => "0",
|
66
|
+
"longitude" => "0",
|
67
|
+
"country_name" => "Reserved",
|
68
|
+
"country_code" => "RD"
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def invalid_key_result
|
73
|
+
{
|
74
|
+
"message" => "invalid key"
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def query_url_params(query)
|
79
|
+
params = {}
|
80
|
+
params.merge!(fields: configuration[:fields]) if configuration.has_key?(:fields)
|
81
|
+
params.merge!(key: configuration.api_key) if configuration.api_key
|
82
|
+
params.merge(super)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
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,53 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/mapbox"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Mapbox < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Mapbox"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
"#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{url_query_string(query)}.json?access_token=#{configuration.api_key}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private # ---------------------------------------------------------------
|
16
|
+
|
17
|
+
def results(query)
|
18
|
+
return [] unless data = fetch_data(query)
|
19
|
+
if data['features']
|
20
|
+
sort_relevant_feature(data['features'])
|
21
|
+
elsif data['message'] =~ /Invalid\sToken/
|
22
|
+
raise_error(Geocoder::InvalidApiKey, data['message'])
|
23
|
+
else
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def url_query_string(query)
|
29
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI.escape)
|
30
|
+
if query.reverse_geocode?
|
31
|
+
lat,lon = query.coordinates
|
32
|
+
"#{CGI.escape lon},#{CGI.escape lat}"
|
33
|
+
else
|
34
|
+
CGI.escape query.text.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def dataset
|
39
|
+
configuration[:dataset] || "mapbox.places"
|
40
|
+
end
|
41
|
+
|
42
|
+
def supported_protocols
|
43
|
+
[:https]
|
44
|
+
end
|
45
|
+
|
46
|
+
def sort_relevant_feature(features)
|
47
|
+
# Sort by descending relevance; Favor original order for equal relevance (eg occurs for reverse geocoding)
|
48
|
+
features.sort_by do |feature|
|
49
|
+
[feature["relevance"],-features.index(feature)]
|
50
|
+
end.reverse
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -14,9 +14,8 @@ module Geocoder::Lookup
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def query_url(query)
|
17
|
-
domain = configuration[:
|
18
|
-
|
19
|
-
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v#{version}/#{search_type(query)}?"
|
17
|
+
domain = configuration[:open] ? "open" : "www"
|
18
|
+
url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
|
20
19
|
url + url_query_string(query)
|
21
20
|
end
|
22
21
|
|
@@ -45,14 +44,15 @@ module Geocoder::Lookup
|
|
45
44
|
case doc['info']['statuscode']
|
46
45
|
when 400 # Error with input
|
47
46
|
raise_error(Geocoder::InvalidRequest, messages) ||
|
48
|
-
warn
|
47
|
+
Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
|
49
48
|
when 403 # Key related error
|
50
49
|
raise_error(Geocoder::InvalidApiKey, messages) ||
|
51
|
-
warn
|
50
|
+
Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
|
52
51
|
when 500 # Unknown error
|
53
52
|
raise_error(Geocoder::Error, messages) ||
|
54
|
-
warn
|
53
|
+
Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
|
55
54
|
end
|
55
|
+
[]
|
56
56
|
end
|
57
57
|
|
58
58
|
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
|
@@ -64,14 +64,16 @@ module Geocoder::Lookup
|
|
64
64
|
if !data_contains_error?(doc)
|
65
65
|
return [doc]
|
66
66
|
elsif doc.last == "INVALID_LICENSE_KEY"
|
67
|
-
raise_error(Geocoder::InvalidApiKey) || warn
|
67
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid MaxMind API key.")
|
68
68
|
end
|
69
69
|
end
|
70
70
|
return []
|
71
71
|
end
|
72
72
|
|
73
73
|
def parse_raw_data(raw_data)
|
74
|
-
|
74
|
+
# Maxmind just returns text/plain as csv format but according to documentation,
|
75
|
+
# we get ISO-8859-1 encoded string. We need to convert it.
|
76
|
+
CSV.parse_line raw_data.force_encoding("ISO-8859-1").encode("UTF-8")
|
75
77
|
end
|
76
78
|
|
77
79
|
def reserved_result
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/maxmind_geoip2'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class MaxmindGeoip2 < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"MaxMind GeoIP2"
|
9
|
+
end
|
10
|
+
|
11
|
+
# Maxmind's GeoIP2 Precision Services only supports HTTPS,
|
12
|
+
# otherwise a `404 Not Found` HTTP response will be returned
|
13
|
+
def supported_protocols
|
14
|
+
[:https]
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_url(query)
|
18
|
+
"#{protocol}://geoip.maxmind.com/geoip/v2.1/#{configured_service!}/#{URI.escape(query.sanitized_text.strip)}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private # ---------------------------------------------------------------
|
22
|
+
|
23
|
+
##
|
24
|
+
# Return the name of the configured service, or raise an exception.
|
25
|
+
#
|
26
|
+
def configured_service!
|
27
|
+
if s = configuration[:service] and services.include?(s) and configuration[:basic_auth][:user] and configuration[:basic_auth][:password]
|
28
|
+
return s
|
29
|
+
else
|
30
|
+
raise(
|
31
|
+
Geocoder::ConfigurationError,
|
32
|
+
"When using MaxMind GeoIP2 you MUST specify a service name and basic_auth: " +
|
33
|
+
"Geocoder.configure(:maxmind_geoip2 => {:service => ...}, " +
|
34
|
+
":basic_auth => {:user ..., :password => ...}), " +
|
35
|
+
"where service is one of: #{services.inspect}"
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def data_contains_error?(doc)
|
41
|
+
(["code", "error"] - doc.keys).empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Service names used in URL.
|
46
|
+
#
|
47
|
+
def services
|
48
|
+
[
|
49
|
+
:country,
|
50
|
+
:city,
|
51
|
+
:insights,
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
def results(query)
|
56
|
+
# don't look up a loopback address
|
57
|
+
return [] if query.loopback_ip_address?
|
58
|
+
doc = fetch_data(query)
|
59
|
+
if doc
|
60
|
+
if !data_contains_error?(doc)
|
61
|
+
return [doc]
|
62
|
+
else
|
63
|
+
Geocoder.log(:warn, "MaxMind GeoIP2 Geocoding API error: #{doc['code']} (#{doc['error']}).")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
return []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'geocoder/lookups/base'
|
3
|
+
require 'geocoder/results/maxmind_local'
|
4
|
+
|
5
|
+
module Geocoder::Lookup
|
6
|
+
class MaxmindLocal < Base
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
if !configuration[:file].nil?
|
10
|
+
begin
|
11
|
+
gem = RUBY_PLATFORM == 'java' ? 'jgeoip' : 'geoip'
|
12
|
+
require gem
|
13
|
+
rescue LoadError
|
14
|
+
raise "Could not load geoip dependency. To use MaxMind Local lookup you must add the #{gem} gem to your Gemfile or have it installed in your system."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
"MaxMind Local"
|
22
|
+
end
|
23
|
+
|
24
|
+
def required_api_key_parts
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def results(query)
|
31
|
+
if configuration[:file]
|
32
|
+
geoip_class = RUBY_PLATFORM == "java" ? JGeoIP : GeoIP
|
33
|
+
result = geoip_class.new(configuration[:file]).city(query.to_s)
|
34
|
+
result.nil? ? [] : [encode_hash(result.to_hash)]
|
35
|
+
elsif configuration[:package] == :city
|
36
|
+
addr = IPAddr.new(query.text).to_i
|
37
|
+
q = "SELECT l.country, l.region, l.city, l.latitude, l.longitude
|
38
|
+
FROM maxmind_geolite_city_location l WHERE l.loc_id = (SELECT b.loc_id FROM maxmind_geolite_city_blocks b
|
39
|
+
WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num)"
|
40
|
+
format_result(q, [:country_name, :region_name, :city_name, :latitude, :longitude])
|
41
|
+
elsif configuration[:package] == :country
|
42
|
+
addr = IPAddr.new(query.text).to_i
|
43
|
+
q = "SELECT country, country_code FROM maxmind_geolite_country
|
44
|
+
WHERE start_ip_num <= #{addr} AND #{addr} <= end_ip_num"
|
45
|
+
format_result(q, [:country_name, :country_code2])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def encode_hash(hash, encoding = "UTF-8")
|
50
|
+
hash.inject({}) do |h,i|
|
51
|
+
h[i[0]] = i[1].is_a?(String) ? i[1].encode(encoding) : i[1]
|
52
|
+
h
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def format_result(query, attr_names)
|
57
|
+
if r = ActiveRecord::Base.connection.execute(query).first
|
58
|
+
r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array
|
59
|
+
[Hash[*attr_names.zip(r).flatten]]
|
60
|
+
else
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -25,11 +25,19 @@ module Geocoder::Lookup
|
|
25
25
|
doc.is_a?(Array) ? doc : [doc]
|
26
26
|
end
|
27
27
|
|
28
|
+
def parse_raw_data(raw_data)
|
29
|
+
if raw_data.include?("Bandwidth limit exceeded")
|
30
|
+
raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.")
|
31
|
+
else
|
32
|
+
super(raw_data)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
28
36
|
def query_url_params(query)
|
29
37
|
params = {
|
30
38
|
:format => "json",
|
31
39
|
:addressdetails => "1",
|
32
|
-
:"accept-language" => configuration.language
|
40
|
+
:"accept-language" => (query.language || configuration.language)
|
33
41
|
}.merge(super)
|
34
42
|
if query.reverse_geocode?
|
35
43
|
lat,lon = query.coordinates
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require "geocoder/results/okf"
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class Okf < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Okf"
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_url(query)
|
12
|
+
"#{protocol}://data.okf.fi/gis/1/geocode/json?" + url_query_string(query)
|
13
|
+
end
|
14
|
+
|
15
|
+
private # ---------------------------------------------------------------
|
16
|
+
|
17
|
+
def valid_response?(response)
|
18
|
+
json = parse_json(response.body)
|
19
|
+
status = json["status"] if json
|
20
|
+
super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
|
21
|
+
end
|
22
|
+
|
23
|
+
def results(query)
|
24
|
+
return [] unless doc = fetch_data(query)
|
25
|
+
case doc['status']; when "OK" # OK status implies >0 results
|
26
|
+
return doc['results']
|
27
|
+
end
|
28
|
+
return []
|
29
|
+
end
|
30
|
+
|
31
|
+
def query_url_okf_params(query)
|
32
|
+
params = {
|
33
|
+
(query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
|
34
|
+
:sensor => "false",
|
35
|
+
:language => (query.language || configuration.language)
|
36
|
+
}
|
37
|
+
params
|
38
|
+
end
|
39
|
+
|
40
|
+
def query_url_params(query)
|
41
|
+
query_url_okf_params(query).merge(super)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|