geocoder 1.4.3 → 1.6.1

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 (109) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +64 -0
  3. data/LICENSE +1 -1
  4. data/README.md +365 -883
  5. data/examples/autoexpire_cache_redis.rb +5 -3
  6. data/lib/generators/geocoder/config/templates/initializer.rb +3 -2
  7. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +2 -0
  8. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +2 -0
  9. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +1 -1
  10. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +1 -1
  11. data/lib/generators/geocoder/migration_version.rb +15 -0
  12. data/lib/geocoder/cache.rb +6 -2
  13. data/lib/geocoder/calculations.rb +1 -1
  14. data/lib/geocoder/cli.rb +2 -2
  15. data/lib/geocoder/configuration.rb +2 -2
  16. data/lib/geocoder/exceptions.rb +1 -1
  17. data/lib/geocoder/ip_address.rb +14 -1
  18. data/lib/geocoder/lookup.rb +13 -6
  19. data/lib/geocoder/lookups/amap.rb +63 -0
  20. data/lib/geocoder/lookups/baidu.rb +14 -10
  21. data/lib/geocoder/lookups/baidu_ip.rb +7 -36
  22. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +17 -4
  23. data/lib/geocoder/lookups/base.rb +28 -5
  24. data/lib/geocoder/lookups/bing.rb +15 -13
  25. data/lib/geocoder/lookups/db_ip_com.rb +52 -0
  26. data/lib/geocoder/lookups/dstk.rb +4 -2
  27. data/lib/geocoder/lookups/esri.rb +39 -29
  28. data/lib/geocoder/lookups/freegeoip.rb +16 -7
  29. data/lib/geocoder/lookups/geocoder_ca.rb +4 -4
  30. data/lib/geocoder/lookups/geocodio.rb +5 -5
  31. data/lib/geocoder/lookups/geoportail_lu.rb +7 -7
  32. data/lib/geocoder/lookups/google.rb +13 -9
  33. data/lib/geocoder/lookups/google_places_details.rb +4 -4
  34. data/lib/geocoder/lookups/google_places_search.rb +4 -4
  35. data/lib/geocoder/lookups/google_premier.rb +11 -1
  36. data/lib/geocoder/lookups/here.rb +29 -23
  37. data/lib/geocoder/lookups/ip2location.rb +67 -0
  38. data/lib/geocoder/lookups/ipapi_com.rb +9 -13
  39. data/lib/geocoder/lookups/ipdata_co.rb +62 -0
  40. data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
  41. data/lib/geocoder/lookups/ipinfo_io.rb +11 -29
  42. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  43. data/lib/geocoder/lookups/ipstack.rb +63 -0
  44. data/lib/geocoder/lookups/latlon.rb +4 -4
  45. data/lib/geocoder/lookups/location_iq.rb +26 -8
  46. data/lib/geocoder/lookups/mapbox.rb +12 -6
  47. data/lib/geocoder/lookups/mapquest.rb +4 -5
  48. data/lib/geocoder/lookups/maxmind.rb +6 -6
  49. data/lib/geocoder/lookups/maxmind_geoip2.rb +8 -7
  50. data/lib/geocoder/lookups/nominatim.rb +17 -5
  51. data/lib/geocoder/lookups/opencagedata.rb +7 -6
  52. data/lib/geocoder/lookups/osmnames.rb +57 -0
  53. data/lib/geocoder/lookups/pelias.rb +8 -9
  54. data/lib/geocoder/lookups/pickpoint.rb +41 -0
  55. data/lib/geocoder/lookups/pointpin.rb +10 -9
  56. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +4 -5
  57. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  58. data/lib/geocoder/lookups/smarty_streets.rb +20 -10
  59. data/lib/geocoder/lookups/telize.rb +26 -6
  60. data/lib/geocoder/lookups/tencent.rb +59 -0
  61. data/lib/geocoder/lookups/yandex.rb +12 -8
  62. data/lib/geocoder/models/active_record.rb +4 -3
  63. data/lib/geocoder/query.rb +14 -0
  64. data/lib/geocoder/railtie.rb +1 -1
  65. data/lib/geocoder/request.rb +32 -0
  66. data/lib/geocoder/results/amap.rb +87 -0
  67. data/lib/geocoder/results/baidu.rb +10 -14
  68. data/lib/geocoder/results/ban_data_gouv_fr.rb +1 -1
  69. data/lib/geocoder/results/base.rb +13 -1
  70. data/lib/geocoder/results/bing.rb +1 -1
  71. data/lib/geocoder/results/db_ip_com.rb +58 -0
  72. data/lib/geocoder/results/freegeoip.rb +0 -5
  73. data/lib/geocoder/results/geocoder_ca.rb +3 -3
  74. data/lib/geocoder/results/geoip2.rb +24 -10
  75. data/lib/geocoder/results/geoportail_lu.rb +5 -3
  76. data/lib/geocoder/results/google.rb +16 -5
  77. data/lib/geocoder/results/here.rb +12 -1
  78. data/lib/geocoder/results/ip2location.rb +22 -0
  79. data/lib/geocoder/results/ipdata_co.rb +40 -0
  80. data/lib/geocoder/results/ipgeolocation.rb +59 -0
  81. data/lib/geocoder/results/ipregistry.rb +308 -0
  82. data/lib/geocoder/results/ipstack.rb +60 -0
  83. data/lib/geocoder/results/maxmind.rb +0 -5
  84. data/lib/geocoder/results/maxmind_local.rb +0 -5
  85. data/lib/geocoder/results/nominatim.rb +12 -0
  86. data/lib/geocoder/results/opencagedata.rb +12 -2
  87. data/lib/geocoder/results/osmnames.rb +56 -0
  88. data/lib/geocoder/results/pickpoint.rb +6 -0
  89. data/lib/geocoder/results/postcodes_io.rb +40 -0
  90. data/lib/geocoder/results/smarty_streets.rb +55 -19
  91. data/lib/geocoder/results/telize.rb +0 -5
  92. data/lib/geocoder/results/tencent.rb +72 -0
  93. data/lib/geocoder/results/test.rb +1 -1
  94. data/lib/geocoder/sql.rb +4 -4
  95. data/lib/geocoder/stores/active_record.rb +16 -5
  96. data/lib/geocoder/stores/base.rb +1 -2
  97. data/lib/geocoder/version.rb +1 -1
  98. data/lib/hash_recursive_merge.rb +1 -2
  99. data/lib/maxmind_database.rb +3 -3
  100. data/lib/tasks/geocoder.rake +11 -3
  101. metadata +30 -14
  102. data/lib/geocoder/lookups/geocoder_us.rb +0 -43
  103. data/lib/geocoder/lookups/mapzen.rb +0 -15
  104. data/lib/geocoder/lookups/okf.rb +0 -44
  105. data/lib/geocoder/lookups/ovi.rb +0 -62
  106. data/lib/geocoder/results/geocoder_us.rb +0 -39
  107. data/lib/geocoder/results/mapzen.rb +0 -5
  108. data/lib/geocoder/results/okf.rb +0 -106
  109. data/lib/geocoder/results/ovi.rb +0 -71
@@ -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,20 +10,38 @@ module Geocoder::Lookup
10
10
  def required_api_key_parts
11
11
  ["api_key"]
12
12
  end
13
-
14
- def query_url(query)
15
- method = query.reverse_geocode? ? "reverse.php" : "search.php"
16
- host = configuration[:host] || "locationiq.org/v1"
17
- "#{protocol}://#{host}/#{method}?key=#{configuration.api_key}&" + url_query_string(query)
13
+
14
+ private # ----------------------------------------------------------------
15
+
16
+ def base_query_url(query)
17
+ method = query.reverse_geocode? ? "reverse" : "search"
18
+ "#{protocol}://#{configured_host}/v1/#{method}.php?"
18
19
  end
19
20
 
20
- private
21
+ def query_url_params(query)
22
+ {
23
+ key: configuration.api_key
24
+ }.merge(super)
25
+ end
26
+
27
+ def configured_host
28
+ configuration[:host] || "locationiq.org"
29
+ end
21
30
 
22
31
  def results(query)
23
32
  return [] unless doc = fetch_data(query)
24
33
 
25
- if !doc.is_a?(Array) && doc['error'] =~ /Invalid\skey/
26
- raise_error(Geocoder::InvalidApiKey, doc['error'])
34
+ if !doc.is_a?(Array)
35
+ case doc['error']
36
+ when "Invalid key"
37
+ raise_error(Geocoder::InvalidApiKey, doc['error'])
38
+ when "Key not active - Please write to contact@unwiredlabs.com"
39
+ raise_error(Geocoder::RequestDenied, doc['error'])
40
+ when "Rate Limited"
41
+ raise_error(Geocoder::OverQueryLimitError, doc['error'])
42
+ when "Unknown error - Please try again after some time"
43
+ raise_error(Geocoder::InvalidRequest, doc['error'])
44
+ end
27
45
  end
28
46
 
29
47
  doc.is_a?(Array) ? doc : [doc]
@@ -8,12 +8,12 @@ module Geocoder::Lookup
8
8
  "Mapbox"
9
9
  end
10
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
11
  private # ---------------------------------------------------------------
16
12
 
13
+ def base_query_url(query)
14
+ "#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{mapbox_search_term(query)}.json?"
15
+ end
16
+
17
17
  def results(query)
18
18
  return [] unless data = fetch_data(query)
19
19
  if data['features']
@@ -25,13 +25,19 @@ module Geocoder::Lookup
25
25
  end
26
26
  end
27
27
 
28
- def url_query_string(query)
28
+ def query_url_params(query)
29
+ {access_token: configuration.api_key}.merge(super(query))
30
+ end
31
+
32
+ def mapbox_search_term(query)
29
33
  require 'cgi' unless defined?(CGI) && defined?(CGI.escape)
30
34
  if query.reverse_geocode?
31
35
  lat,lon = query.coordinates
32
36
  "#{CGI.escape lon},#{CGI.escape lat}"
33
37
  else
34
- 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
35
41
  end
36
42
  end
37
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)
@@ -9,16 +9,28 @@ module Geocoder::Lookup
9
9
  end
10
10
 
11
11
  def map_link_url(coordinates)
12
- "http://www.openstreetmap.org/?lat=#{coordinates[0]}&lon=#{coordinates[1]}&zoom=15&layers=M"
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
- host = configuration[:host] || "nominatim.openstreetmap.org"
18
- "#{protocol}://#{host}/#{method}?" + url_query_string(query)
19
+ "#{protocol}://#{configured_host}/#{method}?"
19
20
  end
20
21
 
21
- private # ---------------------------------------------------------------
22
+ def configured_host
23
+ configuration[:host] || "nominatim.openstreetmap.org"
24
+ end
25
+
26
+ def use_ssl?
27
+ # nominatim.openstreetmap.org redirects HTTP requests to HTTPS
28
+ if configured_host == "nominatim.openstreetmap.org"
29
+ true
30
+ else
31
+ super
32
+ end
33
+ end
22
34
 
23
35
  def results(query)
24
36
  return [] unless doc = fetch_data(query)
@@ -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,10 +44,11 @@ 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
 
50
- [:countrycode, :min_confidence, :no_dedupe, :no_annotations, :no_record, :limit].each do |option|
51
+ [:abbrv, :countrycode, :min_confidence, :no_dedupe, :no_annotations, :no_record, :limit].each do |option|
51
52
  unless (option_value = query.options[option]).nil?
52
53
  params[option] = option_value
53
54
  end
@@ -0,0 +1,57 @@
1
+ require 'cgi'
2
+ require 'geocoder/lookups/base'
3
+ require 'geocoder/results/osmnames'
4
+
5
+ module Geocoder::Lookup
6
+ class Osmnames < Base
7
+
8
+ def name
9
+ 'OSM Names'
10
+ end
11
+
12
+ def required_api_key_parts
13
+ configuration[:host] ? [] : ['key']
14
+ end
15
+
16
+ def supported_protocols
17
+ [:https]
18
+ end
19
+
20
+ private
21
+
22
+ def base_query_url(query)
23
+ "#{base_url(query)}/#{params_url(query)}.js?"
24
+ end
25
+
26
+ def base_url(query)
27
+ host = configuration[:host] || 'geocoder.tilehosting.com'
28
+ "#{protocol}://#{host}"
29
+ end
30
+
31
+ def params_url(query)
32
+ method, args = 'q', CGI.escape(query.sanitized_text)
33
+ method, args = 'r', query.coordinates.join('/') if query.reverse_geocode?
34
+ "#{country_limited(query)}#{method}/#{args}"
35
+ end
36
+
37
+ def results(query)
38
+ return [] unless doc = fetch_data(query)
39
+ if (error = doc['message'])
40
+ raise_error(Geocoder::InvalidRequest, error) ||
41
+ Geocoder.log(:warn, "OSMNames Geocoding API error: #{error}")
42
+ else
43
+ return doc['results']
44
+ end
45
+ end
46
+
47
+ def query_url_params(query)
48
+ {
49
+ key: configuration.api_key
50
+ }.merge(super)
51
+ end
52
+
53
+ def country_limited(query)
54
+ "#{query.options[:country_code].downcase}/" if query.options[:country_code] && !query.reverse_geocode?
55
+ end
56
+ end
57
+ end
@@ -11,25 +11,24 @@ module Geocoder::Lookup
11
11
  configuration[:endpoint] || 'localhost'
12
12
  end
13
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
14
  def required_api_key_parts
20
15
  ['search-XXXX']
21
16
  end
22
17
 
23
- private
18
+ private # ----------------------------------------------------------------
19
+
20
+ def base_query_url(query)
21
+ query_type = query.reverse_geocode? ? 'reverse' : 'search'
22
+ "#{protocol}://#{endpoint}/v1/#{query_type}?"
23
+ end
24
24
 
25
25
  def query_url_params(query)
26
26
  params = {
27
- api_key: configuration.api_key,
28
- size: 1
27
+ api_key: configuration.api_key
29
28
  }.merge(super)
30
29
 
31
30
  if query.reverse_geocode?
32
- lat,lon = query.coordinates
31
+ lat, lon = query.coordinates
33
32
  params[:'point.lat'] = lat
34
33
  params[:'point.lon'] = lon
35
34
  else
@@ -0,0 +1,41 @@
1
+ require "geocoder/lookups/nominatim"
2
+ require "geocoder/results/pickpoint"
3
+
4
+ module Geocoder::Lookup
5
+ class Pickpoint < Nominatim
6
+ def name
7
+ "Pickpoint"
8
+ end
9
+
10
+ def supported_protocols
11
+ [:https]
12
+ end
13
+
14
+ def required_api_key_parts
15
+ ["api_key"]
16
+ end
17
+
18
+ private # ----------------------------------------------------------------
19
+
20
+ def base_query_url(query)
21
+ method = query.reverse_geocode? ? "reverse" : "forward"
22
+ "#{protocol}://api.pickpoint.io/v1/#{method}?"
23
+ end
24
+
25
+ def query_url_params(query)
26
+ {
27
+ key: configuration.api_key
28
+ }.merge(super)
29
+ end
30
+
31
+ def results(query)
32
+ return [] unless doc = fetch_data(query)
33
+
34
+ if !doc.is_a?(Array) && doc['message'] == 'Unauthorized'
35
+ raise_error(Geocoder::InvalidApiKey, 'Unauthorized')
36
+ end
37
+
38
+ doc.is_a?(Array) ? doc : [doc]
39
+ end
40
+ end
41
+ end