geocoder 1.4.7 → 1.5.2

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.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +352 -935
  4. data/examples/autoexpire_cache_redis.rb +3 -3
  5. data/lib/generators/geocoder/config/templates/initializer.rb +2 -2
  6. data/lib/geocoder/cache.rb +1 -1
  7. data/lib/geocoder/calculations.rb +1 -1
  8. data/lib/geocoder/cli.rb +2 -2
  9. data/lib/geocoder/configuration.rb +2 -2
  10. data/lib/geocoder/exceptions.rb +1 -1
  11. data/lib/geocoder/ip_address.rb +14 -1
  12. data/lib/geocoder/lookup.rb +6 -4
  13. data/lib/geocoder/lookups/amap.rb +7 -3
  14. data/lib/geocoder/lookups/baidu.rb +14 -10
  15. data/lib/geocoder/lookups/baidu_ip.rb +7 -36
  16. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +4 -4
  17. data/lib/geocoder/lookups/base.rb +27 -4
  18. data/lib/geocoder/lookups/bing.rb +10 -13
  19. data/lib/geocoder/lookups/db_ip_com.rb +9 -6
  20. data/lib/geocoder/lookups/dstk.rb +4 -2
  21. data/lib/geocoder/lookups/esri.rb +39 -29
  22. data/lib/geocoder/lookups/freegeoip.rb +11 -7
  23. data/lib/geocoder/lookups/geocoder_ca.rb +4 -4
  24. data/lib/geocoder/lookups/geocoder_us.rb +17 -9
  25. data/lib/geocoder/lookups/geocodio.rb +5 -5
  26. data/lib/geocoder/lookups/geoportail_lu.rb +7 -7
  27. data/lib/geocoder/lookups/google.rb +8 -8
  28. data/lib/geocoder/lookups/google_places_details.rb +4 -4
  29. data/lib/geocoder/lookups/google_places_search.rb +4 -4
  30. data/lib/geocoder/lookups/google_premier.rb +10 -0
  31. data/lib/geocoder/lookups/here.rb +30 -15
  32. data/lib/geocoder/lookups/ip2location.rb +75 -0
  33. data/lib/geocoder/lookups/ipapi_com.rb +9 -13
  34. data/lib/geocoder/lookups/ipdata_co.rb +9 -4
  35. data/lib/geocoder/lookups/ipinfo_io.rb +11 -29
  36. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  37. data/lib/geocoder/lookups/ipstack.rb +63 -0
  38. data/lib/geocoder/lookups/latlon.rb +4 -4
  39. data/lib/geocoder/lookups/location_iq.rb +10 -4
  40. data/lib/geocoder/lookups/mapbox.rb +7 -6
  41. data/lib/geocoder/lookups/mapquest.rb +4 -5
  42. data/lib/geocoder/lookups/maxmind.rb +6 -6
  43. data/lib/geocoder/lookups/maxmind_geoip2.rb +8 -7
  44. data/lib/geocoder/lookups/nominatim.rb +4 -4
  45. data/lib/geocoder/lookups/opencagedata.rb +6 -5
  46. data/lib/geocoder/lookups/pelias.rb +8 -9
  47. data/lib/geocoder/lookups/pickpoint.rb +9 -3
  48. data/lib/geocoder/lookups/pointpin.rb +10 -9
  49. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +4 -5
  50. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  51. data/lib/geocoder/lookups/smarty_streets.rb +20 -10
  52. data/lib/geocoder/lookups/telize.rb +26 -6
  53. data/lib/geocoder/lookups/tencent.rb +59 -0
  54. data/lib/geocoder/lookups/yandex.rb +6 -6
  55. data/lib/geocoder/query.rb +14 -0
  56. data/lib/geocoder/railtie.rb +1 -1
  57. data/lib/geocoder/results/baidu.rb +10 -10
  58. data/lib/geocoder/results/base.rb +13 -1
  59. data/lib/geocoder/results/bing.rb +1 -1
  60. data/lib/geocoder/results/db_ip_com.rb +0 -5
  61. data/lib/geocoder/results/freegeoip.rb +0 -5
  62. data/lib/geocoder/results/geocoder_ca.rb +3 -3
  63. data/lib/geocoder/results/geoip2.rb +2 -6
  64. data/lib/geocoder/results/geoportail_lu.rb +5 -3
  65. data/lib/geocoder/results/here.rb +4 -1
  66. data/lib/geocoder/results/ip2location.rb +22 -0
  67. data/lib/geocoder/results/ipdata_co.rb +0 -5
  68. data/lib/geocoder/results/ipregistry.rb +308 -0
  69. data/lib/geocoder/results/ipstack.rb +60 -0
  70. data/lib/geocoder/results/maxmind.rb +0 -5
  71. data/lib/geocoder/results/maxmind_local.rb +0 -5
  72. data/lib/geocoder/results/postcodes_io.rb +40 -0
  73. data/lib/geocoder/results/smarty_streets.rb +48 -18
  74. data/lib/geocoder/results/telize.rb +0 -5
  75. data/lib/geocoder/results/tencent.rb +72 -0
  76. data/lib/geocoder/results/test.rb +1 -1
  77. data/lib/geocoder/stores/active_record.rb +1 -3
  78. data/lib/geocoder/stores/base.rb +1 -1
  79. data/lib/geocoder/version.rb +1 -1
  80. metadata +14 -10
  81. data/lib/geocoder/lookups/mapzen.rb +0 -15
  82. data/lib/geocoder/lookups/okf.rb +0 -44
  83. data/lib/geocoder/lookups/ovi.rb +0 -62
  84. data/lib/geocoder/results/mapzen.rb +0 -5
  85. data/lib/geocoder/results/okf.rb +0 -106
  86. data/lib/geocoder/results/ovi.rb +0 -71
@@ -8,17 +8,6 @@ module Geocoder::Lookup
8
8
  "ip-api.com"
9
9
  end
10
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
11
  def supported_protocols
23
12
  if configuration.api_key
24
13
  [:http, :https]
@@ -27,8 +16,14 @@ module Geocoder::Lookup
27
16
  end
28
17
  end
29
18
 
19
+ private # ----------------------------------------------------------------
30
20
 
31
- private
21
+ def base_query_url(query)
22
+ domain = configuration.api_key ? "pro.ip-api.com" : "ip-api.com"
23
+ url = "#{protocol}://#{domain}/json/#{query.sanitized_text}"
24
+ url << "?" if not url_query_string(query).empty?
25
+ url
26
+ end
32
27
 
33
28
  def parse_raw_data(raw_data)
34
29
  if raw_data.chomp == "invalid key"
@@ -39,7 +34,8 @@ module Geocoder::Lookup
39
34
  end
40
35
 
41
36
  def results(query)
42
- return [reserved_result(query.text)] if query.loopback_ip_address?
37
+ # don't look up a loopback or private address, just return the stored result
38
+ return [reserved_result(query.text)] if query.internal_ip_address?
43
39
 
44
40
  return [] unless doc = fetch_data(query)
45
41
 
@@ -13,15 +13,20 @@ module Geocoder::Lookup
13
13
  end
14
14
 
15
15
  def query_url(query)
16
- "#{protocol}://#{host}/#{query.sanitized_text}"
16
+ # Ipdata.co requires that the API key be sent as a query parameter.
17
+ # It no longer supports API keys sent as headers.
18
+ "#{protocol}://#{host}/#{query.sanitized_text}?api-key=#{configuration.api_key}"
17
19
  end
18
20
 
19
21
  private # ---------------------------------------------------------------
20
22
 
23
+ def cache_key(query)
24
+ query_url(query)
25
+ end
26
+
21
27
  def results(query)
22
- Geocoder.configure(:ipdata_co => {:http_headers => { "api-key" => configuration.api_key }}) if configuration.api_key
23
- # don't look up a loopback address, just return the stored result
24
- return [reserved_result(query.text)] if query.loopback_ip_address?
28
+ # don't look up a loopback or private address, just return the stored result
29
+ return [reserved_result(query.text)] if query.internal_ip_address?
25
30
  # note: Ipdata.co returns plain text on bad request
26
31
  (doc = fetch_data(query)) ? [doc] : []
27
32
  end
@@ -8,47 +8,29 @@ module Geocoder::Lookup
8
8
  "Ipinfo.io"
9
9
  end
10
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
11
+ private # ---------------------------------------------------------------
18
12
 
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
13
+ def base_query_url(query)
14
+ url = "#{protocol}://ipinfo.io/#{query.sanitized_text}/geo"
15
+ url << "?" if configuration.api_key
16
+ url
26
17
  end
27
18
 
28
- private # ---------------------------------------------------------------
29
-
30
19
  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)
20
+ # don't look up a loopback or private address, just return the stored result
21
+ return [reserved_result(query.text)] if query.internal_ip_address?
22
+
23
+ if !(doc = fetch_data(query)).is_a?(Hash) or doc['error']
34
24
  []
35
25
  else
36
26
  [doc]
37
27
  end
38
28
  end
39
29
 
40
- def empty_result?(doc)
41
- !doc.is_a?(Hash) or doc.keys == ["ip"]
42
- end
43
-
44
30
  def reserved_result(ip)
45
31
  {
46
- "ip" => ip,
47
- "city" => "",
48
- "region" => "",
49
- "country" => "",
50
- "loc" => "0,0",
51
- "postal" => ""
32
+ "ip" => ip,
33
+ "bogon" => true
52
34
  }
53
35
  end
54
36
 
@@ -0,0 +1,68 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ipregistry'
3
+
4
+ module Geocoder::Lookup
5
+ class Ipregistry < Base
6
+
7
+ ERROR_CODES = {
8
+ 400 => Geocoder::InvalidRequest,
9
+ 401 => Geocoder::InvalidRequest,
10
+ 402 => Geocoder::OverQueryLimitError,
11
+ 403 => Geocoder::InvalidApiKey,
12
+ 451 => Geocoder::RequestDenied,
13
+ 500 => Geocoder::Error
14
+ }
15
+ ERROR_CODES.default = Geocoder::Error
16
+
17
+ def name
18
+ "Ipregistry"
19
+ end
20
+
21
+ def supported_protocols
22
+ [:https, :http]
23
+ end
24
+
25
+ private
26
+
27
+ def base_query_url(query)
28
+ "#{protocol}://#{host}/#{query.sanitized_text}?"
29
+ end
30
+
31
+ def cache_key(query)
32
+ query_url(query)
33
+ end
34
+
35
+ def host
36
+ configuration[:host] || "api.ipregistry.co"
37
+ end
38
+
39
+ def query_url_params(query)
40
+ {
41
+ key: configuration.api_key
42
+ }.merge(super)
43
+ end
44
+
45
+ def results(query)
46
+ # don't look up a loopback or private address, just return the stored result
47
+ return [reserved_result(query.text)] if query.internal_ip_address?
48
+
49
+ return [] unless (doc = fetch_data(query))
50
+
51
+ if (error = doc['error'])
52
+ code = error['code']
53
+ msg = error['message']
54
+ raise_error(ERROR_CODES[code], msg ) || Geocoder.log(:warn, "Ipregistry API error: #{msg}")
55
+ return []
56
+ end
57
+ [doc]
58
+ end
59
+
60
+ def reserved_result(ip)
61
+ {
62
+ "ip" => ip,
63
+ "country_name" => "Reserved",
64
+ "country_code" => "RD"
65
+ }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,63 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ipstack'
3
+
4
+ module Geocoder::Lookup
5
+ class Ipstack < Base
6
+
7
+ ERROR_CODES = {
8
+ 404 => Geocoder::InvalidRequest,
9
+ 101 => Geocoder::InvalidApiKey,
10
+ 102 => Geocoder::Error,
11
+ 103 => Geocoder::InvalidRequest,
12
+ 104 => Geocoder::OverQueryLimitError,
13
+ 105 => Geocoder::RequestDenied,
14
+ 301 => Geocoder::InvalidRequest,
15
+ 302 => Geocoder::InvalidRequest,
16
+ 303 => Geocoder::RequestDenied,
17
+ }
18
+ ERROR_CODES.default = Geocoder::Error
19
+
20
+ def name
21
+ "Ipstack"
22
+ end
23
+
24
+ private # ----------------------------------------------------------------
25
+
26
+ def base_query_url(query)
27
+ "#{protocol}://#{host}/#{query.sanitized_text}?"
28
+ end
29
+
30
+ def query_url_params(query)
31
+ {
32
+ access_key: configuration.api_key
33
+ }.merge(super)
34
+ end
35
+
36
+ def results(query)
37
+ # don't look up a loopback or private address, just return the stored result
38
+ return [reserved_result(query.text)] if query.internal_ip_address?
39
+
40
+ return [] unless doc = fetch_data(query)
41
+
42
+ if error = doc['error']
43
+ code = error['code']
44
+ msg = error['info']
45
+ raise_error(ERROR_CODES[code], msg ) || Geocoder.log(:warn, "Ipstack Geocoding API error: #{msg}")
46
+ return []
47
+ end
48
+ [doc]
49
+ end
50
+
51
+ def reserved_result(ip)
52
+ {
53
+ "ip" => ip,
54
+ "country_name" => "Reserved",
55
+ "country_code" => "RD"
56
+ }
57
+ end
58
+
59
+ def host
60
+ configuration[:host] || "api.ipstack.com"
61
+ end
62
+ end
63
+ end
@@ -12,12 +12,12 @@ module Geocoder::Lookup
12
12
  ["api_key"]
13
13
  end
14
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
15
  private # ---------------------------------------------------------------
20
16
 
17
+ def base_query_url(query)
18
+ "#{protocol}://latlon.io/api/v1/#{'reverse_' if query.reverse_geocode?}geocode?"
19
+ end
20
+
21
21
  def results(query)
22
22
  return [] unless doc = fetch_data(query)
23
23
  if doc['error'].nil?
@@ -10,13 +10,19 @@ module Geocoder::Lookup
10
10
  def required_api_key_parts
11
11
  ["api_key"]
12
12
  end
13
-
14
- def query_url(query)
13
+
14
+ private # ----------------------------------------------------------------
15
+
16
+ def base_query_url(query)
15
17
  method = query.reverse_geocode? ? "reverse" : "search"
16
- "#{protocol}://#{configured_host}/v1/#{method}.php?key=#{configuration.api_key}&" + url_query_string(query)
18
+ "#{protocol}://#{configured_host}/v1/#{method}.php?"
17
19
  end
18
20
 
19
- private
21
+ def query_url_params(query)
22
+ {
23
+ key: configuration.api_key
24
+ }.merge(super)
25
+ end
20
26
 
21
27
  def configured_host
22
28
  configuration[:host] || "locationiq.org"
@@ -8,13 +8,12 @@ module Geocoder::Lookup
8
8
  "Mapbox"
9
9
  end
10
10
 
11
- def query_url(query)
12
- path = "#{mapbox_search_term(query)}.json?#{url_query_string(query)}"
13
- "#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{path}"
14
- end
15
-
16
11
  private # ---------------------------------------------------------------
17
12
 
13
+ def base_query_url(query)
14
+ "#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{mapbox_search_term(query)}.json?"
15
+ end
16
+
18
17
  def results(query)
19
18
  return [] unless data = fetch_data(query)
20
19
  if data['features']
@@ -36,7 +35,9 @@ module Geocoder::Lookup
36
35
  lat,lon = query.coordinates
37
36
  "#{CGI.escape lon},#{CGI.escape lat}"
38
37
  else
39
- CGI.escape query.text.to_s
38
+ # truncate at first semicolon so Mapbox doesn't go into batch mode
39
+ # (see Github issue #1299)
40
+ CGI.escape query.text.to_s.split(';').first.to_s
40
41
  end
41
42
  end
42
43
 
@@ -13,14 +13,13 @@ module Geocoder::Lookup
13
13
  ["key"]
14
14
  end
15
15
 
16
- def query_url(query)
16
+ private # ---------------------------------------------------------------
17
+
18
+ def base_query_url(query)
17
19
  domain = configuration[:open] ? "open" : "www"
18
- url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
19
- url + url_query_string(query)
20
+ "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
20
21
  end
21
22
 
22
- private # ---------------------------------------------------------------
23
-
24
23
  def search_type(query)
25
24
  query.reverse_geocode? ? "reverse" : "address"
26
25
  end
@@ -9,12 +9,12 @@ module Geocoder::Lookup
9
9
  "MaxMind"
10
10
  end
11
11
 
12
- def query_url(query)
13
- "#{protocol}://geoip.maxmind.com/#{service_code}?" + url_query_string(query)
14
- end
15
-
16
12
  private # ---------------------------------------------------------------
17
13
 
14
+ def base_query_url(query)
15
+ "#{protocol}://geoip.maxmind.com/#{service_code}?"
16
+ end
17
+
18
18
  ##
19
19
  # Return the name of the configured service, or raise an exception.
20
20
  #
@@ -57,8 +57,8 @@ module Geocoder::Lookup
57
57
  end
58
58
 
59
59
  def results(query)
60
- # don't look up a loopback address, just return the stored result
61
- return [reserved_result] if query.loopback_ip_address?
60
+ # don't look up a loopback or private address, just return the stored result
61
+ return [reserved_result] if query.internal_ip_address?
62
62
  doc = fetch_data(query)
63
63
  if doc and doc.is_a?(Array)
64
64
  if !data_contains_error?(doc)
@@ -20,6 +20,10 @@ module Geocoder::Lookup
20
20
 
21
21
  private # ---------------------------------------------------------------
22
22
 
23
+ def cache_key(query)
24
+ query_url(query)
25
+ end
26
+
23
27
  ##
24
28
  # Return the name of the configured service, or raise an exception.
25
29
  #
@@ -28,11 +32,7 @@ module Geocoder::Lookup
28
32
  return s
29
33
  else
30
34
  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}"
35
+ Geocoder::ConfigurationError, "When using MaxMind GeoIP2 you must specify a service and credentials: Geocoder.configure(maxmind_geoip2: {service: ..., basic_auth: {user: ..., password: ...}}), where service is one of: #{services.inspect}"
36
36
  )
37
37
  end
38
38
  end
@@ -53,8 +53,9 @@ module Geocoder::Lookup
53
53
  end
54
54
 
55
55
  def results(query)
56
- # don't look up a loopback address
57
- return [] if query.loopback_ip_address?
56
+ # don't look up a loopback or private address, just return the stored result
57
+ return [] if query.internal_ip_address?
58
+
58
59
  doc = fetch_data(query)
59
60
  if doc
60
61
  if !data_contains_error?(doc)
@@ -12,13 +12,13 @@ module Geocoder::Lookup
12
12
  "https://www.openstreetmap.org/?lat=#{coordinates[0]}&lon=#{coordinates[1]}&zoom=15&layers=M"
13
13
  end
14
14
 
15
- def query_url(query)
15
+ private # ---------------------------------------------------------------
16
+
17
+ def base_query_url(query)
16
18
  method = query.reverse_geocode? ? "reverse" : "search"
17
- "#{protocol}://#{configured_host}/#{method}?" + url_query_string(query)
19
+ "#{protocol}://#{configured_host}/#{method}?"
18
20
  end
19
21
 
20
- private # ---------------------------------------------------------------
21
-
22
22
  def configured_host
23
23
  configuration[:host] || "nominatim.openstreetmap.org"
24
24
  end
@@ -8,15 +8,15 @@ module Geocoder::Lookup
8
8
  "OpenCageData"
9
9
  end
10
10
 
11
- def query_url(query)
12
- "#{protocol}://api.opencagedata.com/geocode/v1/json?key=#{configuration.api_key}&#{url_query_string(query)}"
13
- end
14
-
15
11
  def required_api_key_parts
16
12
  ["key"]
17
13
  end
18
14
 
19
- private
15
+ private # ----------------------------------------------------------------
16
+
17
+ def base_query_url(query)
18
+ "#{protocol}://api.opencagedata.com/geocode/v1/json?"
19
+ end
20
20
 
21
21
  def results(query)
22
22
  return [] unless doc = fetch_data(query)
@@ -44,6 +44,7 @@ module Geocoder::Lookup
44
44
  def query_url_params(query)
45
45
  params = {
46
46
  :q => query.sanitized_text,
47
+ :key => configuration.api_key,
47
48
  :language => (query.language || configuration.language)
48
49
  }.merge(super)
49
50