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.

Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -0
  3. data/README.md +467 -70
  4. data/examples/reverse_geocode_job.rb +40 -0
  5. data/lib/generators/geocoder/config/templates/initializer.rb +16 -16
  6. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  7. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  8. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  9. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  10. data/lib/geocoder/cache.rb +3 -2
  11. data/lib/geocoder/calculations.rb +44 -2
  12. data/lib/geocoder/configuration.rb +17 -10
  13. data/lib/geocoder/esri_token.rb +38 -0
  14. data/lib/geocoder/exceptions.rb +19 -0
  15. data/lib/geocoder/ip_address.rb +13 -0
  16. data/lib/geocoder/kernel_logger.rb +25 -0
  17. data/lib/geocoder/logger.rb +47 -0
  18. data/lib/geocoder/lookup.rb +32 -8
  19. data/lib/geocoder/lookups/baidu.rb +18 -13
  20. data/lib/geocoder/lookups/baidu_ip.rb +59 -0
  21. data/lib/geocoder/lookups/base.rb +81 -19
  22. data/lib/geocoder/lookups/bing.rb +40 -7
  23. data/lib/geocoder/lookups/esri.rb +42 -5
  24. data/lib/geocoder/lookups/freegeoip.rb +9 -1
  25. data/lib/geocoder/lookups/geocoder_ca.rb +1 -2
  26. data/lib/geocoder/lookups/geocoder_us.rb +6 -2
  27. data/lib/geocoder/lookups/geocodio.rb +42 -0
  28. data/lib/geocoder/lookups/geoip2.rb +45 -0
  29. data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
  30. data/lib/geocoder/lookups/google.rb +29 -5
  31. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  32. data/lib/geocoder/lookups/google_premier.rb +1 -1
  33. data/lib/geocoder/lookups/here.rb +62 -0
  34. data/lib/geocoder/lookups/ipapi_com.rb +86 -0
  35. data/lib/geocoder/lookups/ipinfo_io.rb +55 -0
  36. data/lib/geocoder/lookups/latlon.rb +59 -0
  37. data/lib/geocoder/lookups/mapbox.rb +53 -0
  38. data/lib/geocoder/lookups/mapquest.rb +6 -6
  39. data/lib/geocoder/lookups/mapzen.rb +15 -0
  40. data/lib/geocoder/lookups/maxmind.rb +4 -2
  41. data/lib/geocoder/lookups/maxmind_geoip2.rb +69 -0
  42. data/lib/geocoder/lookups/maxmind_local.rb +65 -0
  43. data/lib/geocoder/lookups/nominatim.rb +9 -1
  44. data/lib/geocoder/lookups/okf.rb +44 -0
  45. data/lib/geocoder/lookups/opencagedata.rb +58 -0
  46. data/lib/geocoder/lookups/pelias.rb +64 -0
  47. data/lib/geocoder/lookups/pointpin.rb +68 -0
  48. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +51 -0
  49. data/lib/geocoder/lookups/smarty_streets.rb +53 -0
  50. data/lib/geocoder/lookups/telize.rb +55 -0
  51. data/lib/geocoder/lookups/yandex.rb +8 -4
  52. data/lib/geocoder/models/active_record.rb +7 -3
  53. data/lib/geocoder/models/base.rb +1 -4
  54. data/lib/geocoder/models/mongo_base.rb +6 -4
  55. data/lib/geocoder/query.rb +9 -5
  56. data/lib/geocoder/railtie.rb +1 -1
  57. data/lib/geocoder/request.rb +74 -12
  58. data/lib/geocoder/results/baidu_ip.rb +62 -0
  59. data/lib/geocoder/results/bing.rb +4 -0
  60. data/lib/geocoder/results/esri.rb +30 -6
  61. data/lib/geocoder/results/freegeoip.rb +2 -2
  62. data/lib/geocoder/results/geocodio.rb +70 -0
  63. data/lib/geocoder/results/geoip2.rb +62 -0
  64. data/lib/geocoder/results/geoportail_lu.rb +69 -0
  65. data/lib/geocoder/results/google.rb +15 -0
  66. data/lib/geocoder/results/google_places_details.rb +35 -0
  67. data/lib/geocoder/results/here.rb +71 -0
  68. data/lib/geocoder/results/ipapi_com.rb +45 -0
  69. data/lib/geocoder/results/ipinfo_io.rb +48 -0
  70. data/lib/geocoder/results/latlon.rb +71 -0
  71. data/lib/geocoder/results/mapbox.rb +47 -0
  72. data/lib/geocoder/results/mapquest.rb +5 -8
  73. data/lib/geocoder/results/mapzen.rb +5 -0
  74. data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
  75. data/lib/geocoder/results/maxmind_local.rb +49 -0
  76. data/lib/geocoder/results/nominatim.rb +6 -1
  77. data/lib/geocoder/results/okf.rb +106 -0
  78. data/lib/geocoder/results/opencagedata.rb +90 -0
  79. data/lib/geocoder/results/ovi.rb +9 -0
  80. data/lib/geocoder/results/pelias.rb +58 -0
  81. data/lib/geocoder/results/pointpin.rb +40 -0
  82. data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
  83. data/lib/geocoder/results/smarty_streets.rb +106 -0
  84. data/lib/geocoder/results/telize.rb +45 -0
  85. data/lib/geocoder/results/test.rb +20 -3
  86. data/lib/geocoder/results/yandex.rb +18 -6
  87. data/lib/geocoder/sql.rb +16 -15
  88. data/lib/geocoder/stores/active_record.rb +51 -18
  89. data/lib/geocoder/stores/base.rb +8 -12
  90. data/lib/geocoder/stores/mongo_base.rb +0 -31
  91. data/lib/geocoder/version.rb +1 -1
  92. data/lib/geocoder.rb +6 -13
  93. data/lib/maxmind_database.rb +109 -0
  94. data/lib/tasks/geocoder.rake +14 -3
  95. data/lib/tasks/maxmind.rake +73 -0
  96. metadata +59 -85
  97. data/.gitignore +0 -5
  98. data/.travis.yml +0 -27
  99. data/Rakefile +0 -25
  100. data/gemfiles/Gemfile.mongoid-2.4.x +0 -15
  101. data/lib/geocoder/lookups/yahoo.rb +0 -86
  102. data/lib/geocoder/results/yahoo.rb +0 -55
  103. data/lib/oauth_util.rb +0 -112
  104. data/test/active_record_test.rb +0 -15
  105. data/test/cache_test.rb +0 -35
  106. data/test/calculations_test.rb +0 -211
  107. data/test/configuration_test.rb +0 -78
  108. data/test/custom_block_test.rb +0 -32
  109. data/test/error_handling_test.rb +0 -43
  110. data/test/fixtures/baidu_invalid_key +0 -1
  111. data/test/fixtures/baidu_no_results +0 -1
  112. data/test/fixtures/baidu_reverse +0 -1
  113. data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
  114. data/test/fixtures/bing_invalid_key +0 -1
  115. data/test/fixtures/bing_madison_square_garden +0 -40
  116. data/test/fixtures/bing_no_results +0 -16
  117. data/test/fixtures/bing_reverse +0 -42
  118. data/test/fixtures/esri_madison_square_garden +0 -59
  119. data/test/fixtures/esri_no_results +0 -8
  120. data/test/fixtures/esri_reverse +0 -21
  121. data/test/fixtures/freegeoip_74_200_247_59 +0 -12
  122. data/test/fixtures/freegeoip_no_results +0 -1
  123. data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
  124. data/test/fixtures/geocoder_ca_no_results +0 -1
  125. data/test/fixtures/geocoder_ca_reverse +0 -34
  126. data/test/fixtures/geocoder_us_madison_square_garden +0 -1
  127. data/test/fixtures/geocoder_us_no_results +0 -1
  128. data/test/fixtures/google_garbage +0 -456
  129. data/test/fixtures/google_madison_square_garden +0 -57
  130. data/test/fixtures/google_no_city_data +0 -44
  131. data/test/fixtures/google_no_locality +0 -51
  132. data/test/fixtures/google_no_results +0 -4
  133. data/test/fixtures/google_over_limit +0 -4
  134. data/test/fixtures/mapquest_error +0 -16
  135. data/test/fixtures/mapquest_invalid_api_key +0 -16
  136. data/test/fixtures/mapquest_invalid_request +0 -16
  137. data/test/fixtures/mapquest_madison_square_garden +0 -52
  138. data/test/fixtures/mapquest_no_results +0 -16
  139. data/test/fixtures/maxmind_24_24_24_21 +0 -1
  140. data/test/fixtures/maxmind_24_24_24_22 +0 -1
  141. data/test/fixtures/maxmind_24_24_24_23 +0 -1
  142. data/test/fixtures/maxmind_24_24_24_24 +0 -1
  143. data/test/fixtures/maxmind_74_200_247_59 +0 -1
  144. data/test/fixtures/maxmind_invalid_key +0 -1
  145. data/test/fixtures/maxmind_no_results +0 -1
  146. data/test/fixtures/nominatim_madison_square_garden +0 -150
  147. data/test/fixtures/nominatim_no_results +0 -1
  148. data/test/fixtures/ovi_madison_square_garden +0 -72
  149. data/test/fixtures/ovi_no_results +0 -8
  150. data/test/fixtures/yahoo_error +0 -1
  151. data/test/fixtures/yahoo_invalid_key +0 -2
  152. data/test/fixtures/yahoo_madison_square_garden +0 -52
  153. data/test/fixtures/yahoo_no_results +0 -10
  154. data/test/fixtures/yahoo_over_limit +0 -2
  155. data/test/fixtures/yandex_invalid_key +0 -1
  156. data/test/fixtures/yandex_kremlin +0 -48
  157. data/test/fixtures/yandex_no_city_and_town +0 -112
  158. data/test/fixtures/yandex_no_results +0 -16
  159. data/test/geocoder_test.rb +0 -59
  160. data/test/https_test.rb +0 -16
  161. data/test/integration/smoke_test.rb +0 -26
  162. data/test/lookup_test.rb +0 -117
  163. data/test/method_aliases_test.rb +0 -25
  164. data/test/mongoid_test.rb +0 -46
  165. data/test/mongoid_test_helper.rb +0 -43
  166. data/test/near_test.rb +0 -61
  167. data/test/oauth_util_test.rb +0 -30
  168. data/test/proxy_test.rb +0 -36
  169. data/test/query_test.rb +0 -52
  170. data/test/request_test.rb +0 -29
  171. data/test/result_test.rb +0 -42
  172. data/test/services_test.rb +0 -393
  173. data/test/test_helper.rb +0 -289
  174. data/test/test_mode_test.rb +0 -59
@@ -0,0 +1,59 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/baidu_ip'
3
+
4
+ module Geocoder::Lookup
5
+ class BaiduIp < Base
6
+
7
+ def name
8
+ "Baidu IP"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{protocol}://api.map.baidu.com/location/ip?" + url_query_string(query)
17
+ end
18
+
19
+ # HTTP only
20
+ def supported_protocols
21
+ [:http]
22
+ end
23
+
24
+ private # ---------------------------------------------------------------
25
+
26
+ def results(query, reverse = false)
27
+ return [] unless doc = fetch_data(query)
28
+ case doc['status']
29
+ when 0
30
+ return [doc['content']] unless doc['content'].blank?
31
+ when 1, 3, 4
32
+ raise_error(Geocoder::Error, "server error.") ||
33
+ Geocoder.log(:warn, "Baidu IP Geocoding API error: server error.")
34
+ when 2
35
+ raise_error(Geocoder::InvalidRequest, "invalid request.") ||
36
+ Geocoder.log(:warn, "Baidu IP Geocoding API error: invalid request.")
37
+ when 5
38
+ raise_error(Geocoder::InvalidApiKey, "invalid api key.") ||
39
+ Geocoder.log(:warn, "Baidu IP Geocoding API error: invalid api key.")
40
+ when 101, 102, 200..299
41
+ raise_error(Geocoder::RequestDenied, "request denied.") ||
42
+ Geocoder.log(:warn, "Baidu IP Geocoding API error: request denied.")
43
+ when 300..399
44
+ raise_error(Geocoder::OverQueryLimitError, "over query limit") ||
45
+ Geocoder.log(:warn, "Baidu IP Geocoding API error: over query limit.")
46
+ end
47
+ return []
48
+ end
49
+
50
+ def query_url_params(query)
51
+ {
52
+ :ip => query.sanitized_text,
53
+ :ak => configuration.api_key,
54
+ :coor => "bd09ll"
55
+ }.merge(super)
56
+ end
57
+
58
+ end
59
+ end
@@ -15,6 +15,9 @@ module Geocoder
15
15
  module Lookup
16
16
 
17
17
  class Base
18
+ def initialize
19
+ @cache = nil
20
+ end
18
21
 
19
22
  ##
20
23
  # Human-readable name of the geocoding API.
@@ -83,6 +86,15 @@ module Geocoder
83
86
  @cache
84
87
  end
85
88
 
89
+ ##
90
+ # Array containing the protocols supported by the api.
91
+ # Should be set to [:http] if only HTTP is supported
92
+ # or [:https] if only HTTPS is supported.
93
+ #
94
+ def supported_protocols
95
+ [:http, :https]
96
+ end
97
+
86
98
  private # -------------------------------------------------------------
87
99
 
88
100
  ##
@@ -96,7 +108,6 @@ module Geocoder
96
108
  # Object used to make HTTP requests.
97
109
  #
98
110
  def http_client
99
- protocol = "http#{'s' if configuration.use_https}"
100
111
  proxy_name = "#{protocol}_proxy"
101
112
  if proxy = configuration.send(proxy_name)
102
113
  proxy_url = !!(proxy =~ /^#{protocol}/) ? proxy : protocol + '://' + proxy
@@ -165,10 +176,12 @@ module Geocoder
165
176
  def fetch_data(query)
166
177
  parse_raw_data fetch_raw_data(query)
167
178
  rescue SocketError => err
168
- raise_error(err) or warn "Geocoding API connection cannot be established."
169
- rescue TimeoutError => err
170
- raise_error(err) or warn "Geocoding API not responding fast enough " +
171
- "(use Geocoder.configure(:timeout => ...) to set limit)."
179
+ raise_error(err) or Geocoder.log(:warn, "Geocoding API connection cannot be established.")
180
+ rescue Errno::ECONNREFUSED => err
181
+ raise_error(err) or Geocoder.log(:warn, "Geocoding API connection refused.")
182
+ rescue Timeout::Error => err
183
+ raise_error(err) or Geocoder.log(:warn, "Geocoding API not responding fast enough " +
184
+ "(use Geocoder.configure(:timeout => ...) to set limit).")
172
185
  end
173
186
 
174
187
  def parse_json(data)
@@ -177,6 +190,8 @@ module Geocoder
177
190
  else
178
191
  JSON.parse(data)
179
192
  end
193
+ rescue
194
+ raise_error(ResponseParseError.new(data)) or Geocoder.log(:warn, "Geocoding API's response was not valid JSON: #{data}")
180
195
  end
181
196
 
182
197
  ##
@@ -184,8 +199,6 @@ module Geocoder
184
199
  #
185
200
  def parse_raw_data(raw_data)
186
201
  parse_json(raw_data)
187
- rescue
188
- warn "Geocoding API's response was not valid JSON."
189
202
  end
190
203
 
191
204
  ##
@@ -193,7 +206,7 @@ module Geocoder
193
206
  # Set in configuration but not available for every service.
194
207
  #
195
208
  def protocol
196
- "http" + (configuration.use_https ? "s" : "")
209
+ "http" + (use_ssl? ? "s" : "")
197
210
  end
198
211
 
199
212
  def valid_response?(response)
@@ -211,7 +224,21 @@ module Geocoder
211
224
  else
212
225
  check_api_key_configuration!(query)
213
226
  response = make_api_request(query)
227
+ check_response_for_errors!(response)
214
228
  body = response.body
229
+
230
+ # apply the charset from the Content-Type header, if possible
231
+ ct = response['content-type']
232
+
233
+ if ct && ct['charset']
234
+ charset = ct.split(';').select do |s|
235
+ s['charset']
236
+ end.first.to_s.split('=')
237
+ if charset.length == 2
238
+ body.force_encoding(charset.last) rescue ArgumentError
239
+ end
240
+ end
241
+
215
242
  if cache and valid_response?(response)
216
243
  cache[key] = body
217
244
  end
@@ -220,26 +247,61 @@ module Geocoder
220
247
  body
221
248
  end
222
249
 
250
+ def check_response_for_errors!(response)
251
+ if response.code.to_i == 400
252
+ raise_error(Geocoder::InvalidRequest) ||
253
+ Geocoder.log(:warn, "Geocoding API error: 400 Bad Request")
254
+ elsif response.code.to_i == 401
255
+ raise_error(Geocoder::RequestDenied) ||
256
+ Geocoder.log(:warn, "Geocoding API error: 401 Unauthorized")
257
+ elsif response.code.to_i == 402
258
+ raise_error(Geocoder::OverQueryLimitError) ||
259
+ Geocoder.log(:warn, "Geocoding API error: 402 Payment Required")
260
+ elsif response.code.to_i == 429
261
+ raise_error(Geocoder::OverQueryLimitError) ||
262
+ Geocoder.log(:warn, "Geocoding API error: 429 Too Many Requests")
263
+ elsif response.code.to_i == 503
264
+ raise_error(Geocoder::ServiceUnavailable) ||
265
+ Geocoder.log(:warn, "Geocoding API error: 503 Service Unavailable")
266
+ end
267
+ end
268
+
223
269
  ##
224
270
  # Make an HTTP(S) request to a geocoding API and
225
271
  # return the response object.
226
272
  #
227
273
  def make_api_request(query)
228
- timeout(configuration.timeout) do
229
- uri = URI.parse(query_url(query))
230
- # client = http_client.new(uri.host, uri.port)
231
- # client.use_ssl = true if configuration.use_https
232
- # client.get(uri.request_uri, configuration.http_headers)
233
-
234
- http_client.start(uri.host, uri.port) do |client|
235
- client.use_ssl = true if configuration.use_https
236
- req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers)
237
- req.basic_auth(uri.user, uri.password) if uri.user and uri.password
238
- client.request(req)
274
+ uri = URI.parse(query_url(query))
275
+ Geocoder.log(:debug, "Geocoder: HTTP request being made for #{uri.to_s}")
276
+ http_client.start(uri.host, uri.port, use_ssl: use_ssl?, open_timeout: configuration.timeout, read_timeout: configuration.timeout) do |client|
277
+ configure_ssl!(client) if use_ssl?
278
+ req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers)
279
+ if configuration.basic_auth[:user] and configuration.basic_auth[:password]
280
+ req.basic_auth(
281
+ configuration.basic_auth[:user],
282
+ configuration.basic_auth[:password]
283
+ )
239
284
  end
285
+ client.request(req)
286
+ end
287
+ rescue Timeout::Error
288
+ raise Geocoder::LookupTimeout
289
+ rescue Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ENETUNREACH
290
+ raise Geocoder::NetworkError
291
+ end
292
+
293
+ def use_ssl?
294
+ if supported_protocols == [:https]
295
+ true
296
+ elsif supported_protocols == [:http]
297
+ false
298
+ else
299
+ configuration.use_https
240
300
  end
241
301
  end
242
302
 
303
+ def configure_ssl!(client); end
304
+
243
305
  def check_api_key_configuration!(query)
244
306
  key_parts = query.lookup.required_api_key_parts
245
307
  if key_parts.size > Array(configuration.api_key).size
@@ -17,31 +17,64 @@ module Geocoder::Lookup
17
17
  end
18
18
 
19
19
  def query_url(query)
20
- "#{protocol}://dev.virtualearth.net/REST/v1/Locations" +
21
- (query.reverse_geocode? ? "/#{query.sanitized_text}?" : "?") +
22
- url_query_string(query)
20
+ base_url(query) + url_query_string(query)
23
21
  end
24
22
 
25
23
  private # ---------------------------------------------------------------
26
24
 
25
+ def base_url(query)
26
+ url = "#{protocol}://dev.virtualearth.net/REST/v1/Locations"
27
+
28
+ if !query.reverse_geocode?
29
+ if r = query.options[:region]
30
+ url << "/#{r}"
31
+ end
32
+ # use the more forgiving 'unstructured' query format to allow special
33
+ # chars, newlines, brackets, typos.
34
+ url + "?q=" + URI.escape(query.sanitized_text.strip) + "&"
35
+ else
36
+ url + "/#{URI.escape(query.sanitized_text.strip)}?"
37
+ end
38
+ end
39
+
27
40
  def results(query)
28
41
  return [] unless doc = fetch_data(query)
29
42
 
30
43
  if doc['statusCode'] == 200
31
44
  return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : []
32
45
  elsif doc['statusCode'] == 401 and doc["authenticationResultCode"] == "InvalidCredentials"
33
- raise_error(Geocoder::InvalidApiKey) || warn("Invalid Bing API key.")
46
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid Bing API key.")
34
47
  else
35
- warn "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']})."
48
+ Geocoder.log(:warn, "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']}).")
36
49
  end
37
50
  return []
38
51
  end
39
52
 
40
53
  def query_url_params(query)
41
54
  {
42
- :key => configuration.api_key,
43
- :query => query.reverse_geocode? ? nil : query.sanitized_text
55
+ key: configuration.api_key
44
56
  }.merge(super)
45
57
  end
58
+
59
+ def check_response_for_errors!(response)
60
+ super
61
+ if server_overloaded?(response)
62
+ raise_error(Geocoder::ServiceUnavailable) ||
63
+ Geocoder.log(:warn, "Bing Geocoding API error: Service Unavailable")
64
+ end
65
+ end
66
+
67
+ def valid_response?(response)
68
+ super(response) and not server_overloaded?(response)
69
+ end
70
+
71
+ def server_overloaded?(response)
72
+ # Occasionally, the servers processing service requests can be overloaded,
73
+ # and you may receive some responses that contain no results for queries that
74
+ # you would normally receive a result. To identify this situation,
75
+ # check the HTTP headers of the response. If the HTTP header X-MS-BM-WS-INFO is set to 1,
76
+ # it is best to wait a few seconds and try again.
77
+ response['x-ms-bm-ws-info'].to_i == 1
78
+ end
46
79
  end
47
80
  end
@@ -1,5 +1,6 @@
1
1
  require 'geocoder/lookups/base'
2
2
  require "geocoder/results/esri"
3
+ require 'geocoder/esri_token'
3
4
 
4
5
  module Geocoder::Lookup
5
6
  class Esri < Base
@@ -9,19 +10,21 @@ module Geocoder::Lookup
9
10
  end
10
11
 
11
12
  def query_url(query)
12
- search_keyword = query.reverse_geocode? ? "reverseGeocode" : "find"
13
-
14
- "#{protocol}://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/#{search_keyword}?" +
15
- url_query_string(query)
13
+ base_query_url(query) + url_query_string(query)
16
14
  end
17
15
 
18
16
  private # ---------------------------------------------------------------
19
17
 
18
+ def base_query_url(query)
19
+ action = query.reverse_geocode? ? "reverseGeocode" : "find"
20
+ "#{protocol}://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/#{action}?"
21
+ end
22
+
20
23
  def results(query)
21
24
  return [] unless doc = fetch_data(query)
22
25
 
23
26
  if (!query.reverse_geocode?)
24
- return [] if doc['locations'].empty?
27
+ return [] if !doc['locations'] || doc['locations'].empty?
25
28
  end
26
29
 
27
30
  if (doc['error'].nil?)
@@ -31,6 +34,17 @@ module Geocoder::Lookup
31
34
  end
32
35
  end
33
36
 
37
+ def cache_key(query)
38
+ base_query_url(query) + hash_to_query(cache_key_params(query))
39
+ end
40
+
41
+ def cache_key_params(query)
42
+ # omit api_key and token because they may vary among requests
43
+ query_url_params(query).reject do |key,value|
44
+ [:api_key, :token].include?(key)
45
+ end
46
+ end
47
+
34
48
  def query_url_params(query)
35
49
  params = {
36
50
  :f => "pjson",
@@ -41,8 +55,31 @@ module Geocoder::Lookup
41
55
  else
42
56
  params[:text] = query.sanitized_text
43
57
  end
58
+ params[:token] = token
59
+ params[:forStorage] = configuration[:for_storage] if configuration[:for_storage]
60
+ params[:sourceCountry] = configuration[:source_country] if configuration[:source_country]
44
61
  params.merge(super)
45
62
  end
46
63
 
64
+ def token
65
+ create_and_save_token! if !valid_token_configured? and configuration.api_key
66
+ configuration[:token].to_s unless configuration[:token].nil?
67
+ end
68
+
69
+ def valid_token_configured?
70
+ !configuration[:token].nil? and configuration[:token].active?
71
+ end
72
+
73
+ def create_and_save_token!
74
+ save_token!(create_token)
75
+ end
76
+
77
+ def create_token
78
+ Geocoder::EsriToken.generate_token(*configuration.api_key)
79
+ end
80
+
81
+ def save_token!(token_instance)
82
+ Geocoder.merge_into_lookup_config(:esri, token: token_instance)
83
+ end
47
84
  end
48
85
  end
@@ -7,9 +7,13 @@ module Geocoder::Lookup
7
7
  def name
8
8
  "FreeGeoIP"
9
9
  end
10
+
11
+ def supported_protocols
12
+ [:http]
13
+ end
10
14
 
11
15
  def query_url(query)
12
- "#{protocol}://freegeoip.net/json/#{query.sanitized_text}"
16
+ "#{protocol}://#{host}/json/#{query.sanitized_text}"
13
17
  end
14
18
 
15
19
  private # ---------------------------------------------------------------
@@ -39,5 +43,9 @@ module Geocoder::Lookup
39
43
  "country_code" => "RD"
40
44
  }
41
45
  end
46
+
47
+ def host
48
+ configuration[:host] || "freegeoip.io"
49
+ end
42
50
  end
43
51
  end
@@ -21,7 +21,7 @@ module Geocoder::Lookup
21
21
  elsif doc['error']['code'] == "005"
22
22
  # "Postal Code is not in the proper Format" => no results, just shut up
23
23
  else
24
- warn "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']})."
24
+ Geocoder.log(:warn, "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']}).")
25
25
  end
26
26
  return []
27
27
  end
@@ -51,4 +51,3 @@ module Geocoder::Lookup
51
51
  end
52
52
  end
53
53
  end
54
-
@@ -8,11 +8,15 @@ module Geocoder::Lookup
8
8
  "Geocoder.us"
9
9
  end
10
10
 
11
+ def supported_protocols
12
+ [:http]
13
+ end
14
+
11
15
  def query_url(query)
12
16
  if configuration.api_key
13
- "http://#{configuration.api_key}@geocoder.us/member/service/csv/geocode?" + url_query_string(query)
17
+ "#{protocol}://#{configuration.api_key}@geocoder.us/member/service/csv/geocode?" + url_query_string(query)
14
18
  else
15
- "http://geocoder.us/service/csv/geocode?" + url_query_string(query)
19
+ "#{protocol}://geocoder.us/service/csv/geocode?" + url_query_string(query)
16
20
  end
17
21
  end
18
22
 
@@ -0,0 +1,42 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/geocodio"
3
+
4
+ module Geocoder::Lookup
5
+ class Geocodio < Base
6
+
7
+ def name
8
+ "Geocodio"
9
+ end
10
+
11
+ def query_url(query)
12
+ path = query.reverse_geocode? ? "reverse" : "geocode"
13
+ "#{protocol}://api.geocod.io/v1/#{path}?#{url_query_string(query)}"
14
+ end
15
+
16
+ def results(query)
17
+ return [] unless doc = fetch_data(query)
18
+ return doc["results"] if doc['error'].nil?
19
+
20
+ if doc['error'] == 'Invalid API key'
21
+ raise_error(Geocoder::InvalidApiKey) ||
22
+ Geocoder.log(:warn, "Geocodio service error: invalid API key.")
23
+ elsif doc['error'].match(/You have reached your daily maximum/)
24
+ raise_error(Geocoder::OverQueryLimitError, doc['error']) ||
25
+ Geocoder.log(:warn, "Geocodio service error: #{doc['error']}.")
26
+ else
27
+ raise_error(Geocoder::InvalidRequest, doc['error']) ||
28
+ Geocoder.log(:warn, "Geocodio service error: #{doc['error']}.")
29
+ end
30
+ []
31
+ end
32
+
33
+ private # ---------------------------------------------------------------
34
+
35
+ def query_url_params(query)
36
+ {
37
+ :api_key => configuration.api_key,
38
+ :q => query.sanitized_text
39
+ }.merge(super)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/geoip2'
3
+
4
+ module Geocoder
5
+ module Lookup
6
+ class Geoip2 < Base
7
+ attr_reader :gem_name
8
+
9
+ def initialize
10
+ unless configuration[:file].nil?
11
+ begin
12
+ @gem_name = configuration[:lib] || 'maxminddb'
13
+ require @gem_name
14
+ rescue LoadError
15
+ raise "Could not load Maxmind DB dependency. To use the GeoIP2 lookup you must add the #{@gem_name} gem to your Gemfile or have it installed in your system."
16
+ end
17
+
18
+ @mmdb = db_class.new(configuration[:file].to_s)
19
+ end
20
+ super
21
+ end
22
+
23
+ def name
24
+ 'GeoIP2'
25
+ end
26
+
27
+ def required_api_key_parts
28
+ []
29
+ end
30
+
31
+ private
32
+
33
+ def db_class
34
+ gem_name == 'hive_geoip2' ? Hive::GeoIP2 : MaxMindDB
35
+ end
36
+
37
+ def results(query)
38
+ return [] unless configuration[:file]
39
+
40
+ result = @mmdb.lookup(query.to_s)
41
+ result.nil? ? [] : [result]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,65 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/geoportail_lu"
3
+
4
+ module Geocoder
5
+ module Lookup
6
+ class GeoportailLu < Base
7
+
8
+ def name
9
+ "Geoportail.lu"
10
+ end
11
+
12
+ def query_url(query)
13
+ url_base_path(query) + url_query_string(query)
14
+ end
15
+
16
+ private
17
+
18
+ def url_base_path(query)
19
+ query.reverse_geocode? ? reverse_geocode_url_base_path : search_url_base_path
20
+ end
21
+
22
+ def search_url_base_path
23
+ "#{protocol}://api.geoportail.lu/geocoder/search?"
24
+ end
25
+
26
+ def reverse_geocode_url_base_path
27
+ "#{protocol}://api.geoportail.lu/geocoder/reverseGeocode?"
28
+ end
29
+
30
+ def query_url_geoportail_lu_params(query)
31
+ query.reverse_geocode? ? reverse_geocode_params(query) : search_params(query)
32
+ end
33
+
34
+ def search_params(query)
35
+ {
36
+ queryString: query.sanitized_text
37
+ }
38
+ end
39
+
40
+ def reverse_geocode_params(query)
41
+ lat_lon = query.coordinates
42
+ {
43
+ lat: lat_lon.first,
44
+ lon: lat_lon.last
45
+ }
46
+ end
47
+
48
+ def query_url_params(query)
49
+ query_url_geoportail_lu_params(query).merge(super)
50
+ end
51
+
52
+ def results(query)
53
+ return [] unless doc = fetch_data(query)
54
+ if doc['success'] == true
55
+ result = doc['results']
56
+ else
57
+ result = []
58
+ raise_error(Geocoder::Error) ||
59
+ warn("Geportail.lu Geocoding API error")
60
+ end
61
+ result
62
+ end
63
+ end
64
+ end
65
+ end
@@ -12,14 +12,35 @@ module Geocoder::Lookup
12
12
  "http://maps.google.com/maps?q=#{coordinates.join(',')}"
13
13
  end
14
14
 
15
+ def supported_protocols
16
+ # Google requires HTTPS if an API key is used.
17
+ if configuration.api_key
18
+ [:https]
19
+ else
20
+ [:http, :https]
21
+ end
22
+ end
23
+
15
24
  def query_url(query)
16
25
  "#{protocol}://maps.googleapis.com/maps/api/geocode/json?" + url_query_string(query)
17
26
  end
18
27
 
19
28
  private # ---------------------------------------------------------------
20
29
 
30
+ def configure_ssl!(client)
31
+ client.instance_eval {
32
+ @ssl_context = OpenSSL::SSL::SSLContext.new
33
+ options = OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
34
+ if OpenSSL::SSL.const_defined?('OP_NO_COMPRESSION')
35
+ options |= OpenSSL::SSL::OP_NO_COMPRESSION
36
+ end
37
+ @ssl_context.set_params({options: options})
38
+ }
39
+ end
40
+
21
41
  def valid_response?(response)
22
- status = parse_json(response.body)["status"]
42
+ json = parse_json(response.body)
43
+ status = json["status"] if json
23
44
  super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
24
45
  end
25
46
 
@@ -29,13 +50,13 @@ module Geocoder::Lookup
29
50
  return doc['results']
30
51
  when "OVER_QUERY_LIMIT"
31
52
  raise_error(Geocoder::OverQueryLimitError) ||
32
- warn("Google Geocoding API error: over query limit.")
53
+ Geocoder.log(:warn, "Google Geocoding API error: over query limit.")
33
54
  when "REQUEST_DENIED"
34
55
  raise_error(Geocoder::RequestDenied) ||
35
- warn("Google Geocoding API error: request denied.")
56
+ Geocoder.log(:warn, "Google Geocoding API error: request denied.")
36
57
  when "INVALID_REQUEST"
37
58
  raise_error(Geocoder::InvalidRequest) ||
38
- warn("Google Geocoding API error: invalid request.")
59
+ Geocoder.log(:warn, "Google Geocoding API error: invalid request.")
39
60
  end
40
61
  return []
41
62
  end
@@ -44,7 +65,7 @@ module Geocoder::Lookup
44
65
  params = {
45
66
  (query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
46
67
  :sensor => "false",
47
- :language => configuration.language
68
+ :language => (query.language || configuration.language)
48
69
  }
49
70
  unless (bounds = query.options[:bounds]).nil?
50
71
  params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join('|')
@@ -55,6 +76,9 @@ module Geocoder::Lookup
55
76
  unless (components = query.options[:components]).nil?
56
77
  params[:components] = components.is_a?(Array) ? components.join("|") : components
57
78
  end
79
+ unless (result_type = query.options[:result_type]).nil?
80
+ params[:result_type] = result_type.is_a?(Array) ? result_type.join("|") : result_type
81
+ end
58
82
  params
59
83
  end
60
84