geocoder 1.1.9 → 1.8.0

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 (238) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +309 -0
  3. data/LICENSE +1 -1
  4. data/README.md +544 -540
  5. data/bin/console +13 -0
  6. data/examples/app_defined_lookup_services.rb +22 -0
  7. data/examples/reverse_geocode_job.rb +40 -0
  8. data/lib/easting_northing.rb +171 -0
  9. data/lib/generators/geocoder/config/templates/initializer.rb +22 -16
  10. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +30 -0
  11. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +30 -0
  12. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  13. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  14. data/lib/generators/geocoder/migration_version.rb +15 -0
  15. data/lib/geocoder/cache.rb +20 -32
  16. data/lib/geocoder/cache_stores/base.rb +40 -0
  17. data/lib/geocoder/cache_stores/generic.rb +35 -0
  18. data/lib/geocoder/cache_stores/redis.rb +34 -0
  19. data/lib/geocoder/calculations.rb +67 -36
  20. data/lib/geocoder/cli.rb +2 -2
  21. data/lib/geocoder/configuration.rb +33 -16
  22. data/lib/geocoder/configuration_hash.rb +4 -4
  23. data/lib/geocoder/esri_token.rb +38 -0
  24. data/lib/geocoder/exceptions.rb +19 -0
  25. data/lib/geocoder/ip_address.rb +33 -0
  26. data/lib/geocoder/kernel_logger.rb +25 -0
  27. data/lib/geocoder/logger.rb +47 -0
  28. data/lib/geocoder/lookup.rb +74 -11
  29. data/lib/geocoder/lookups/abstract_api.rb +46 -0
  30. data/lib/geocoder/lookups/amap.rb +63 -0
  31. data/lib/geocoder/lookups/amazon_location_service.rb +54 -0
  32. data/lib/geocoder/lookups/baidu.rb +24 -15
  33. data/lib/geocoder/lookups/baidu_ip.rb +30 -0
  34. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +143 -0
  35. data/lib/geocoder/lookups/base.rb +109 -23
  36. data/lib/geocoder/lookups/bing.rb +45 -10
  37. data/lib/geocoder/lookups/db_ip_com.rb +52 -0
  38. data/lib/geocoder/lookups/dstk.rb +4 -2
  39. data/lib/geocoder/lookups/esri.rb +61 -8
  40. data/lib/geocoder/lookups/freegeoip.rb +25 -6
  41. data/lib/geocoder/lookups/geoapify.rb +72 -0
  42. data/lib/geocoder/lookups/geocoder_ca.rb +5 -6
  43. data/lib/geocoder/lookups/geocodio.rb +42 -0
  44. data/lib/geocoder/lookups/geoip2.rb +49 -0
  45. data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
  46. data/lib/geocoder/lookups/google.rb +45 -12
  47. data/lib/geocoder/lookups/google_places_details.rb +64 -0
  48. data/lib/geocoder/lookups/google_places_search.rb +76 -0
  49. data/lib/geocoder/lookups/google_premier.rb +16 -2
  50. data/lib/geocoder/lookups/here.rb +73 -0
  51. data/lib/geocoder/lookups/ip2location.rb +71 -0
  52. data/lib/geocoder/lookups/ipapi_com.rb +82 -0
  53. data/lib/geocoder/lookups/ipdata_co.rb +62 -0
  54. data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
  55. data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
  56. data/lib/geocoder/lookups/ipqualityscore.rb +50 -0
  57. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  58. data/lib/geocoder/lookups/ipstack.rb +63 -0
  59. data/lib/geocoder/lookups/latlon.rb +58 -0
  60. data/lib/geocoder/lookups/location_iq.rb +54 -0
  61. data/lib/geocoder/lookups/mapbox.rb +59 -0
  62. data/lib/geocoder/lookups/mapquest.rb +9 -10
  63. data/lib/geocoder/lookups/maxmind.rb +10 -8
  64. data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
  65. data/lib/geocoder/lookups/maxmind_local.rb +71 -0
  66. data/lib/geocoder/lookups/melissa_street.rb +41 -0
  67. data/lib/geocoder/lookups/nationaal_georegister_nl.rb +38 -0
  68. data/lib/geocoder/lookups/nominatim.rb +26 -6
  69. data/lib/geocoder/lookups/opencagedata.rb +65 -0
  70. data/lib/geocoder/lookups/osmnames.rb +57 -0
  71. data/lib/geocoder/lookups/pelias.rb +63 -0
  72. data/lib/geocoder/lookups/photon.rb +89 -0
  73. data/lib/geocoder/lookups/pickpoint.rb +41 -0
  74. data/lib/geocoder/lookups/pointpin.rb +69 -0
  75. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +50 -0
  76. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  77. data/lib/geocoder/lookups/smarty_streets.rb +68 -0
  78. data/lib/geocoder/lookups/telize.rb +75 -0
  79. data/lib/geocoder/lookups/tencent.rb +59 -0
  80. data/lib/geocoder/lookups/test.rb +4 -0
  81. data/lib/geocoder/lookups/twogis.rb +58 -0
  82. data/lib/geocoder/lookups/uk_ordnance_survey_names.rb +59 -0
  83. data/lib/geocoder/lookups/yandex.rb +18 -11
  84. data/lib/geocoder/models/active_record.rb +9 -4
  85. data/lib/geocoder/models/base.rb +1 -4
  86. data/lib/geocoder/models/mongo_base.rb +6 -4
  87. data/lib/geocoder/query.rb +23 -5
  88. data/lib/geocoder/railtie.rb +2 -2
  89. data/lib/geocoder/request.rb +102 -11
  90. data/lib/geocoder/results/abstract_api.rb +146 -0
  91. data/lib/geocoder/results/amap.rb +87 -0
  92. data/lib/geocoder/results/amazon_location_service.rb +57 -0
  93. data/lib/geocoder/results/baidu.rb +10 -14
  94. data/lib/geocoder/results/baidu_ip.rb +62 -0
  95. data/lib/geocoder/results/ban_data_gouv_fr.rb +282 -0
  96. data/lib/geocoder/results/base.rb +13 -1
  97. data/lib/geocoder/results/bing.rb +5 -1
  98. data/lib/geocoder/results/db_ip_com.rb +58 -0
  99. data/lib/geocoder/results/esri.rb +35 -8
  100. data/lib/geocoder/results/freegeoip.rb +2 -7
  101. data/lib/geocoder/results/geoapify.rb +179 -0
  102. data/lib/geocoder/results/geocoder_ca.rb +3 -3
  103. data/lib/geocoder/results/geocodio.rb +78 -0
  104. data/lib/geocoder/results/geoip2.rb +76 -0
  105. data/lib/geocoder/results/geoportail_lu.rb +71 -0
  106. data/lib/geocoder/results/google.rb +26 -0
  107. data/lib/geocoder/results/google_places_details.rb +39 -0
  108. data/lib/geocoder/results/google_places_search.rb +52 -0
  109. data/lib/geocoder/results/here.rb +77 -0
  110. data/lib/geocoder/results/ip2location.rb +22 -0
  111. data/lib/geocoder/results/ipapi_com.rb +45 -0
  112. data/lib/geocoder/results/ipdata_co.rb +40 -0
  113. data/lib/geocoder/results/ipgeolocation.rb +59 -0
  114. data/lib/geocoder/results/ipinfo_io.rb +48 -0
  115. data/lib/geocoder/results/ipqualityscore.rb +54 -0
  116. data/lib/geocoder/results/ipregistry.rb +304 -0
  117. data/lib/geocoder/results/ipstack.rb +60 -0
  118. data/lib/geocoder/results/latlon.rb +71 -0
  119. data/lib/geocoder/results/location_iq.rb +6 -0
  120. data/lib/geocoder/results/mapbox.rb +63 -0
  121. data/lib/geocoder/results/mapquest.rb +5 -8
  122. data/lib/geocoder/results/maxmind.rb +0 -5
  123. data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
  124. data/lib/geocoder/results/maxmind_local.rb +44 -0
  125. data/lib/geocoder/results/melissa_street.rb +46 -0
  126. data/lib/geocoder/results/nationaal_georegister_nl.rb +62 -0
  127. data/lib/geocoder/results/nominatim.rb +41 -14
  128. data/lib/geocoder/results/opencagedata.rb +100 -0
  129. data/lib/geocoder/results/osmnames.rb +56 -0
  130. data/lib/geocoder/results/pelias.rb +58 -0
  131. data/lib/geocoder/results/photon.rb +119 -0
  132. data/lib/geocoder/results/pickpoint.rb +6 -0
  133. data/lib/geocoder/results/pointpin.rb +40 -0
  134. data/lib/geocoder/results/postcode_anywhere_uk.rb +42 -0
  135. data/lib/geocoder/results/postcodes_io.rb +40 -0
  136. data/lib/geocoder/results/smarty_streets.rb +142 -0
  137. data/lib/geocoder/results/telize.rb +40 -0
  138. data/lib/geocoder/results/tencent.rb +72 -0
  139. data/lib/geocoder/results/test.rb +20 -3
  140. data/lib/geocoder/results/twogis.rb +76 -0
  141. data/lib/geocoder/results/uk_ordnance_survey_names.rb +59 -0
  142. data/lib/geocoder/results/yandex.rb +244 -32
  143. data/lib/geocoder/sql.rb +25 -21
  144. data/lib/geocoder/stores/active_record.rb +82 -26
  145. data/lib/geocoder/stores/base.rb +9 -14
  146. data/lib/geocoder/stores/mongo_base.rb +0 -31
  147. data/lib/geocoder/util.rb +29 -0
  148. data/lib/geocoder/version.rb +1 -1
  149. data/lib/geocoder.rb +6 -13
  150. data/lib/maxmind_database.rb +109 -0
  151. data/lib/tasks/geocoder.rake +30 -3
  152. data/lib/tasks/maxmind.rake +73 -0
  153. metadata +115 -98
  154. data/.gitignore +0 -5
  155. data/.travis.yml +0 -27
  156. data/Rakefile +0 -25
  157. data/examples/autoexpire_cache_dalli.rb +0 -62
  158. data/examples/autoexpire_cache_redis.rb +0 -28
  159. data/gemfiles/Gemfile.mongoid-2.4.x +0 -15
  160. data/lib/geocoder/lookups/geocoder_us.rb +0 -39
  161. data/lib/geocoder/lookups/ovi.rb +0 -62
  162. data/lib/geocoder/lookups/yahoo.rb +0 -86
  163. data/lib/geocoder/results/geocoder_us.rb +0 -39
  164. data/lib/geocoder/results/ovi.rb +0 -62
  165. data/lib/geocoder/results/yahoo.rb +0 -55
  166. data/lib/hash_recursive_merge.rb +0 -74
  167. data/lib/oauth_util.rb +0 -112
  168. data/test/active_record_test.rb +0 -15
  169. data/test/cache_test.rb +0 -35
  170. data/test/calculations_test.rb +0 -211
  171. data/test/configuration_test.rb +0 -78
  172. data/test/custom_block_test.rb +0 -32
  173. data/test/error_handling_test.rb +0 -43
  174. data/test/fixtures/baidu_invalid_key +0 -1
  175. data/test/fixtures/baidu_no_results +0 -1
  176. data/test/fixtures/baidu_reverse +0 -1
  177. data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
  178. data/test/fixtures/bing_invalid_key +0 -1
  179. data/test/fixtures/bing_madison_square_garden +0 -40
  180. data/test/fixtures/bing_no_results +0 -16
  181. data/test/fixtures/bing_reverse +0 -42
  182. data/test/fixtures/esri_madison_square_garden +0 -59
  183. data/test/fixtures/esri_no_results +0 -8
  184. data/test/fixtures/esri_reverse +0 -21
  185. data/test/fixtures/freegeoip_74_200_247_59 +0 -12
  186. data/test/fixtures/freegeoip_no_results +0 -1
  187. data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
  188. data/test/fixtures/geocoder_ca_no_results +0 -1
  189. data/test/fixtures/geocoder_ca_reverse +0 -34
  190. data/test/fixtures/geocoder_us_madison_square_garden +0 -1
  191. data/test/fixtures/geocoder_us_no_results +0 -1
  192. data/test/fixtures/google_garbage +0 -456
  193. data/test/fixtures/google_madison_square_garden +0 -57
  194. data/test/fixtures/google_no_city_data +0 -44
  195. data/test/fixtures/google_no_locality +0 -51
  196. data/test/fixtures/google_no_results +0 -4
  197. data/test/fixtures/google_over_limit +0 -4
  198. data/test/fixtures/mapquest_error +0 -16
  199. data/test/fixtures/mapquest_invalid_api_key +0 -16
  200. data/test/fixtures/mapquest_invalid_request +0 -16
  201. data/test/fixtures/mapquest_madison_square_garden +0 -52
  202. data/test/fixtures/mapquest_no_results +0 -16
  203. data/test/fixtures/maxmind_24_24_24_21 +0 -1
  204. data/test/fixtures/maxmind_24_24_24_22 +0 -1
  205. data/test/fixtures/maxmind_24_24_24_23 +0 -1
  206. data/test/fixtures/maxmind_24_24_24_24 +0 -1
  207. data/test/fixtures/maxmind_74_200_247_59 +0 -1
  208. data/test/fixtures/maxmind_invalid_key +0 -1
  209. data/test/fixtures/maxmind_no_results +0 -1
  210. data/test/fixtures/nominatim_madison_square_garden +0 -150
  211. data/test/fixtures/nominatim_no_results +0 -1
  212. data/test/fixtures/ovi_madison_square_garden +0 -72
  213. data/test/fixtures/ovi_no_results +0 -8
  214. data/test/fixtures/yahoo_error +0 -1
  215. data/test/fixtures/yahoo_invalid_key +0 -2
  216. data/test/fixtures/yahoo_madison_square_garden +0 -52
  217. data/test/fixtures/yahoo_no_results +0 -10
  218. data/test/fixtures/yahoo_over_limit +0 -2
  219. data/test/fixtures/yandex_invalid_key +0 -1
  220. data/test/fixtures/yandex_kremlin +0 -48
  221. data/test/fixtures/yandex_no_city_and_town +0 -112
  222. data/test/fixtures/yandex_no_results +0 -16
  223. data/test/geocoder_test.rb +0 -59
  224. data/test/https_test.rb +0 -16
  225. data/test/integration/smoke_test.rb +0 -26
  226. data/test/lookup_test.rb +0 -117
  227. data/test/method_aliases_test.rb +0 -25
  228. data/test/mongoid_test.rb +0 -46
  229. data/test/mongoid_test_helper.rb +0 -43
  230. data/test/near_test.rb +0 -61
  231. data/test/oauth_util_test.rb +0 -30
  232. data/test/proxy_test.rb +0 -36
  233. data/test/query_test.rb +0 -52
  234. data/test/request_test.rb +0 -29
  235. data/test/result_test.rb +0 -42
  236. data/test/services_test.rb +0 -393
  237. data/test/test_helper.rb +0 -289
  238. data/test/test_mode_test.rb +0 -59
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require 'geocoder/lookups/base'
4
+ require 'geocoder/results/abstract_api'
5
+
6
+ module Geocoder::Lookup
7
+ class AbstractApi < Base
8
+
9
+ def name
10
+ "Abstract API"
11
+ end
12
+
13
+ def required_api_key_parts
14
+ ['api_key']
15
+ end
16
+
17
+ def supported_protocols
18
+ [:https]
19
+ end
20
+
21
+ private # ---------------------------------------------------------------
22
+
23
+ def base_query_url(query)
24
+ "#{protocol}://ipgeolocation.abstractapi.com/v1/?"
25
+ end
26
+
27
+ def query_url_params(query)
28
+ params = {api_key: configuration.api_key}
29
+
30
+ ip_address = query.sanitized_text
31
+ if ip_address.is_a?(String) && ip_address.length > 0
32
+ params[:ip_address] = ip_address
33
+ end
34
+
35
+ params.merge(super)
36
+ end
37
+
38
+ def results(query, reverse = false)
39
+ if doc = fetch_data(query)
40
+ [doc]
41
+ else
42
+ []
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,63 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/amap"
3
+
4
+ module Geocoder::Lookup
5
+ class Amap < Base
6
+
7
+ def name
8
+ "AMap"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def supported_protocols
16
+ [:http]
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def base_query_url(query)
22
+ path = query.reverse_geocode? ? 'regeo' : 'geo'
23
+ "http://restapi.amap.com/v3/geocode/#{path}?"
24
+ end
25
+
26
+ def results(query, reverse = false)
27
+ return [] unless doc = fetch_data(query)
28
+ case [doc['status'], doc['info']]
29
+ when ['1', 'OK']
30
+ return doc['regeocodes'] unless doc['regeocodes'].blank?
31
+ return [doc['regeocode']] unless doc['regeocode'].blank?
32
+ return doc['geocodes'] unless doc['geocodes'].blank?
33
+ when ['0', 'INVALID_USER_KEY']
34
+ raise_error(Geocoder::InvalidApiKey, "invalid api key") ||
35
+ warn("#{self.name} Geocoding API error: invalid api key.")
36
+ else
37
+ raise_error(Geocoder::Error, "server error.") ||
38
+ warn("#{self.name} Geocoding API error: server error - [#{doc['info']}]")
39
+ end
40
+ return []
41
+ end
42
+
43
+ def query_url_params(query)
44
+ params = {
45
+ :key => configuration.api_key,
46
+ :output => "json"
47
+ }
48
+ if query.reverse_geocode?
49
+ params[:location] = revert_coordinates(query.text)
50
+ params[:extensions] = "all"
51
+ params[:coordsys] = "gps"
52
+ else
53
+ params[:address] = query.sanitized_text
54
+ end
55
+ params.merge(super)
56
+ end
57
+
58
+ def revert_coordinates(text)
59
+ [text[1],text[0]].join(",")
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,54 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/amazon_location_service'
3
+
4
+ module Geocoder::Lookup
5
+ class AmazonLocationService < Base
6
+ def results(query)
7
+ params = query.options.dup
8
+
9
+ # index_name is required
10
+ # Aws::ParamValidator raises ArgumentError on missing required keys
11
+ params.merge!(index_name: configuration[:index_name])
12
+
13
+ # Aws::ParamValidator raises ArgumentError on unexpected keys
14
+ params.delete(:lookup)
15
+
16
+ resp = if query.reverse_geocode?
17
+ client.search_place_index_for_position(params.merge(position: query.coordinates.reverse))
18
+ else
19
+ client.search_place_index_for_text(params.merge(text: query.text))
20
+ end
21
+
22
+ resp.results.map(&:place)
23
+ end
24
+
25
+ private
26
+
27
+ def client
28
+ return @client if @client
29
+ require_sdk
30
+ keys = configuration.api_key
31
+ if keys
32
+ @client = Aws::LocationService::Client.new(
33
+ access_key_id: keys[:access_key_id],
34
+ secret_access_key: keys[:secret_access_key],
35
+ )
36
+ else
37
+ @client = Aws::LocationService::Client.new
38
+ end
39
+ end
40
+
41
+ def require_sdk
42
+ begin
43
+ require 'aws-sdk-locationservice'
44
+ rescue LoadError
45
+ raise_error(Geocoder::ConfigurationError) ||
46
+ Geocoder.log(
47
+ :error,
48
+ "Couldn't load the Amazon Location Service SDK. " +
49
+ "Install it with: gem install aws-sdk-locationservice -v '~> 1.4'"
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
@@ -12,31 +12,41 @@ module Geocoder::Lookup
12
12
  ["key"]
13
13
  end
14
14
 
15
- def query_url(query)
16
- "http://api.map.baidu.com/geocoder/v2/?" + url_query_string(query)
15
+ # HTTP only
16
+ def supported_protocols
17
+ [:http]
17
18
  end
18
19
 
19
20
  private # ---------------------------------------------------------------
20
21
 
22
+ def base_query_url(query)
23
+ "#{protocol}://api.map.baidu.com/geocoder/v2/?"
24
+ end
25
+
26
+ def content_key
27
+ 'result'
28
+ end
29
+
21
30
  def results(query, reverse = false)
22
31
  return [] unless doc = fetch_data(query)
23
- case doc['status']; when 0
24
- return [doc['result']] unless doc['result'].blank?
32
+ case doc['status']
33
+ when 0
34
+ return [doc[content_key]] unless doc[content_key].blank?
25
35
  when 1, 3, 4
26
- raise_error(Geocoder::Error, messages) ||
27
- warn("Baidu Geocoding API error: server error.")
36
+ raise_error(Geocoder::Error, "server error.") ||
37
+ Geocoder.log(:warn, "#{name} Geocoding API error: server error.")
28
38
  when 2
29
- raise_error(Geocoder::InvalidRequest, messages) ||
30
- warn("Baidu Geocoding API error: invalid request.")
39
+ raise_error(Geocoder::InvalidRequest, "invalid request.") ||
40
+ Geocoder.log(:warn, "#{name} Geocoding API error: invalid request.")
31
41
  when 5
32
- raise_error(Geocoder::InvalidApiKey, messages) ||
33
- warn("Baidu Geocoding API error: invalid api key.")
42
+ raise_error(Geocoder::InvalidApiKey, "invalid api key") ||
43
+ Geocoder.log(:warn, "#{name} Geocoding API error: invalid api key.")
34
44
  when 101, 102, 200..299
35
- raise_error(Geocoder::RequestDenied) ||
36
- warn("Baidu Geocoding API error: request denied.")
45
+ raise_error(Geocoder::RequestDenied, "request denied") ||
46
+ Geocoder.log(:warn, "#{name} Geocoding API error: request denied.")
37
47
  when 300..399
38
- raise_error(Geocoder::OverQueryLimitError) ||
39
- warn("Baidu Geocoding API error: over query limit.")
48
+ raise_error(Geocoder::OverQueryLimitError, "over query limit.") ||
49
+ Geocoder.log(:warn, "#{name} Geocoding API error: over query limit.")
40
50
  end
41
51
  return []
42
52
  end
@@ -51,4 +61,3 @@ module Geocoder::Lookup
51
61
 
52
62
  end
53
63
  end
54
-
@@ -0,0 +1,30 @@
1
+ require 'geocoder/lookups/baidu'
2
+ require 'geocoder/results/baidu_ip'
3
+
4
+ module Geocoder::Lookup
5
+ class BaiduIp < Baidu
6
+
7
+ def name
8
+ "Baidu IP"
9
+ end
10
+
11
+ private # ---------------------------------------------------------------
12
+
13
+ def base_query_url(query)
14
+ "#{protocol}://api.map.baidu.com/location/ip?"
15
+ end
16
+
17
+ def content_key
18
+ 'content'
19
+ end
20
+
21
+ def query_url_params(query)
22
+ {
23
+ :ip => query.sanitized_text,
24
+ :ak => configuration.api_key,
25
+ :coor => "bd09ll"
26
+ }.merge(super)
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+
3
+ require 'geocoder/lookups/base'
4
+ require 'geocoder/results/ban_data_gouv_fr'
5
+
6
+ module Geocoder::Lookup
7
+ class BanDataGouvFr < Base
8
+
9
+ def name
10
+ "Base Adresse Nationale Française"
11
+ end
12
+
13
+ def map_link_url(coordinates)
14
+ "https://www.openstreetmap.org/#map=19/#{coordinates.join('/')}"
15
+ end
16
+
17
+ private # ---------------------------------------------------------------
18
+
19
+ def base_query_url(query)
20
+ method = query.reverse_geocode? ? "reverse" : "search"
21
+ "#{protocol}://api-adresse.data.gouv.fr/#{method}/?"
22
+ end
23
+
24
+ def any_result?(doc)
25
+ doc['features'] and doc['features'].any?
26
+ end
27
+
28
+ def results(query)
29
+ if doc = fetch_data(query) and any_result?(doc)
30
+ [doc]
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+ #### PARAMS ####
37
+
38
+ def query_url_params(query)
39
+ query_ban_datagouv_fr_params(query).merge(super)
40
+ end
41
+
42
+ def query_ban_datagouv_fr_params(query)
43
+ query.reverse_geocode? ? reverse_geocode_ban_fr_params(query) : search_geocode_ban_fr_params(query)
44
+ end
45
+
46
+ #### SEARCH GEOCODING PARAMS ####
47
+ #
48
+ # :q => required, full text search param)
49
+
50
+ # :limit => force limit number of results returned by raw API
51
+ # (default = 5) note : only first result is taken
52
+ # in account in geocoder
53
+ #
54
+ # :autocomplete => pass 0 to disable autocomplete treatment of :q
55
+ # (default = 1)
56
+ #
57
+ # :lat => force filter results around specific lat/lon
58
+ #
59
+ # :lon => force filter results around specific lat/lon
60
+ #
61
+ # :type => force filter the returned result type
62
+ # (check results for a list of accepted types)
63
+ #
64
+ # :postcode => force filter results on a specific city post code
65
+ #
66
+ # :citycode => force filter results on a specific city UUID INSEE code
67
+ #
68
+ # For up to date doc (in french only) : https://adresse.data.gouv.fr/api/
69
+ #
70
+ def search_geocode_ban_fr_params(query)
71
+ params = {
72
+ q: query.sanitized_text
73
+ }
74
+ unless (limit = query.options[:limit]).nil? || !limit_param_is_valid?(limit)
75
+ params[:limit] = limit.to_i
76
+ end
77
+ unless (autocomplete = query.options[:autocomplete]).nil? || !autocomplete_param_is_valid?(autocomplete)
78
+ params[:autocomplete] = autocomplete.to_s
79
+ end
80
+ unless (type = query.options[:type]).nil? || !type_param_is_valid?(type)
81
+ params[:type] = type.downcase
82
+ end
83
+ unless (postcode = query.options[:postcode]).nil? || !code_param_is_valid?(postcode)
84
+ params[:postcode] = postcode.to_s
85
+ end
86
+ unless (citycode = query.options[:citycode]).nil? || !code_param_is_valid?(citycode)
87
+ params[:citycode] = citycode.to_s
88
+ end
89
+ unless (lat = query.options[:lat]).nil? || !latitude_is_valid?(lat)
90
+ params[:lat] = lat
91
+ end
92
+ unless (lon = query.options[:lon]).nil? || !longitude_is_valid?(lon)
93
+ params[:lon] = lon
94
+ end
95
+ params
96
+ end
97
+
98
+ #### REVERSE GEOCODING PARAMS ####
99
+ #
100
+ # :lat => required
101
+ #
102
+ # :lon => required
103
+ #
104
+ # :type => force returned results type
105
+ # (check results for a list of accepted types)
106
+ #
107
+ def reverse_geocode_ban_fr_params(query)
108
+ lat_lon = query.coordinates
109
+ params = {
110
+ lat: lat_lon.first,
111
+ lon: lat_lon.last
112
+ }
113
+ unless (type = query.options[:type]).nil? || !type_param_is_valid?(type)
114
+ params[:type] = type.downcase
115
+ end
116
+ params
117
+ end
118
+
119
+ def limit_param_is_valid?(param)
120
+ param.to_i.positive?
121
+ end
122
+
123
+ def autocomplete_param_is_valid?(param)
124
+ [0,1].include?(param.to_i)
125
+ end
126
+
127
+ def type_param_is_valid?(param)
128
+ %w(housenumber street locality village town city).include?(param.downcase)
129
+ end
130
+
131
+ def code_param_is_valid?(param)
132
+ (1..99999).include?(param.to_i)
133
+ end
134
+
135
+ def latitude_is_valid?(param)
136
+ param.to_f <= 90 && param.to_f >= -90
137
+ end
138
+
139
+ def longitude_is_valid?(param)
140
+ param.to_f <= 180 && param.to_f >= -180
141
+ end
142
+ end
143
+ end
@@ -4,7 +4,6 @@ require 'uri'
4
4
 
5
5
  unless defined?(ActiveSupport::JSON)
6
6
  begin
7
- require 'rubygems' # for Ruby 1.8
8
7
  require 'json'
9
8
  rescue LoadError
10
9
  raise LoadError, "Please install the 'json' or 'json_pure' gem to parse geocoder results."
@@ -15,6 +14,9 @@ module Geocoder
15
14
  module Lookup
16
15
 
17
16
  class Base
17
+ def initialize
18
+ @cache = nil
19
+ end
18
20
 
19
21
  ##
20
22
  # Human-readable name of the geocoding API.
@@ -69,8 +71,12 @@ module Geocoder
69
71
  ##
70
72
  # URL to use for querying the geocoding engine.
71
73
  #
74
+ # Subclasses should not modify this method. Instead they should define
75
+ # base_query_url and url_query_string. If absolutely necessary to
76
+ # subclss this method, they must also subclass #cache_key.
77
+ #
72
78
  def query_url(query)
73
- fail
79
+ base_query_url(query) + url_query_string(query)
74
80
  end
75
81
 
76
82
  ##
@@ -78,13 +84,31 @@ module Geocoder
78
84
  #
79
85
  def cache
80
86
  if @cache.nil? and store = configuration.cache
81
- @cache = Cache.new(store, configuration.cache_prefix)
87
+ cache_options = configuration.cache_options
88
+ @cache = Cache.new(store, cache_options)
82
89
  end
83
90
  @cache
84
91
  end
85
92
 
93
+ ##
94
+ # Array containing the protocols supported by the api.
95
+ # Should be set to [:http] if only HTTP is supported
96
+ # or [:https] if only HTTPS is supported.
97
+ #
98
+ def supported_protocols
99
+ [:http, :https]
100
+ end
101
+
86
102
  private # -------------------------------------------------------------
87
103
 
104
+ ##
105
+ # String which, when concatenated with url_query_string(query)
106
+ # produces the full query URL. Should include the "?" a the end.
107
+ #
108
+ def base_query_url(query)
109
+ fail
110
+ end
111
+
88
112
  ##
89
113
  # An object with configuration data for this particular lookup.
90
114
  #
@@ -96,7 +120,6 @@ module Geocoder
96
120
  # Object used to make HTTP requests.
97
121
  #
98
122
  def http_client
99
- protocol = "http#{'s' if configuration.use_https}"
100
123
  proxy_name = "#{protocol}_proxy"
101
124
  if proxy = configuration.send(proxy_name)
102
125
  proxy_url = !!(proxy =~ /^#{protocol}/) ? proxy : protocol + '://' + proxy
@@ -136,7 +159,14 @@ module Geocoder
136
159
  # something else (like the URL before OAuth encoding).
137
160
  #
138
161
  def cache_key(query)
139
- query_url(query)
162
+ base_query_url(query) + hash_to_query(cache_key_params(query))
163
+ end
164
+
165
+ def cache_key_params(query)
166
+ # omit api_key and token because they may vary among requests
167
+ query_url_params(query).reject do |key,value|
168
+ key.to_s.match(/(key|token)/)
169
+ end
140
170
  end
141
171
 
142
172
  ##
@@ -165,10 +195,14 @@ module Geocoder
165
195
  def fetch_data(query)
166
196
  parse_raw_data fetch_raw_data(query)
167
197
  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)."
198
+ raise_error(err) or Geocoder.log(:warn, "Geocoding API connection cannot be established.")
199
+ rescue Errno::ECONNREFUSED => err
200
+ raise_error(err) or Geocoder.log(:warn, "Geocoding API connection refused.")
201
+ rescue Geocoder::NetworkError => err
202
+ raise_error(err) or Geocoder.log(:warn, "Geocoding API connection is either unreacheable or reset by the peer")
203
+ rescue Timeout::Error => err
204
+ raise_error(err) or Geocoder.log(:warn, "Geocoding API not responding fast enough " +
205
+ "(use Geocoder.configure(:timeout => ...) to set limit).")
172
206
  end
173
207
 
174
208
  def parse_json(data)
@@ -177,6 +211,11 @@ module Geocoder
177
211
  else
178
212
  JSON.parse(data)
179
213
  end
214
+ rescue
215
+ unless raise_error(ResponseParseError.new(data))
216
+ Geocoder.log(:warn, "Geocoding API's response was not valid JSON")
217
+ Geocoder.log(:debug, "Raw response: #{data}")
218
+ end
180
219
  end
181
220
 
182
221
  ##
@@ -184,8 +223,6 @@ module Geocoder
184
223
  #
185
224
  def parse_raw_data(raw_data)
186
225
  parse_json(raw_data)
187
- rescue
188
- warn "Geocoding API's response was not valid JSON."
189
226
  end
190
227
 
191
228
  ##
@@ -193,7 +230,7 @@ module Geocoder
193
230
  # Set in configuration but not available for every service.
194
231
  #
195
232
  def protocol
196
- "http" + (configuration.use_https ? "s" : "")
233
+ "http" + (use_ssl? ? "s" : "")
197
234
  end
198
235
 
199
236
  def valid_response?(response)
@@ -211,7 +248,21 @@ module Geocoder
211
248
  else
212
249
  check_api_key_configuration!(query)
213
250
  response = make_api_request(query)
251
+ check_response_for_errors!(response)
214
252
  body = response.body
253
+
254
+ # apply the charset from the Content-Type header, if possible
255
+ ct = response['content-type']
256
+
257
+ if ct && ct['charset']
258
+ charset = ct.split(';').select do |s|
259
+ s['charset']
260
+ end.first.to_s.split('=')
261
+ if charset.length == 2
262
+ body.force_encoding(charset.last) rescue ArgumentError
263
+ end
264
+ end
265
+
215
266
  if cache and valid_response?(response)
216
267
  cache[key] = body
217
268
  end
@@ -220,26 +271,61 @@ module Geocoder
220
271
  body
221
272
  end
222
273
 
274
+ def check_response_for_errors!(response)
275
+ if response.code.to_i == 400
276
+ raise_error(Geocoder::InvalidRequest) ||
277
+ Geocoder.log(:warn, "Geocoding API error: 400 Bad Request")
278
+ elsif response.code.to_i == 401
279
+ raise_error(Geocoder::RequestDenied) ||
280
+ Geocoder.log(:warn, "Geocoding API error: 401 Unauthorized")
281
+ elsif response.code.to_i == 402
282
+ raise_error(Geocoder::OverQueryLimitError) ||
283
+ Geocoder.log(:warn, "Geocoding API error: 402 Payment Required")
284
+ elsif response.code.to_i == 429
285
+ raise_error(Geocoder::OverQueryLimitError) ||
286
+ Geocoder.log(:warn, "Geocoding API error: 429 Too Many Requests")
287
+ elsif response.code.to_i == 503
288
+ raise_error(Geocoder::ServiceUnavailable) ||
289
+ Geocoder.log(:warn, "Geocoding API error: 503 Service Unavailable")
290
+ end
291
+ end
292
+
223
293
  ##
224
294
  # Make an HTTP(S) request to a geocoding API and
225
295
  # return the response object.
226
296
  #
227
297
  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)
298
+ uri = URI.parse(query_url(query))
299
+ Geocoder.log(:debug, "Geocoder: HTTP request being made for #{uri.to_s}")
300
+ http_client.start(uri.host, uri.port, use_ssl: use_ssl?, open_timeout: configuration.timeout, read_timeout: configuration.timeout) do |client|
301
+ configure_ssl!(client) if use_ssl?
302
+ req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers)
303
+ if configuration.basic_auth[:user] and configuration.basic_auth[:password]
304
+ req.basic_auth(
305
+ configuration.basic_auth[:user],
306
+ configuration.basic_auth[:password]
307
+ )
239
308
  end
309
+ client.request(req)
240
310
  end
311
+ rescue Timeout::Error
312
+ raise Geocoder::LookupTimeout
313
+ rescue Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ENETUNREACH, Errno::ECONNRESET
314
+ raise Geocoder::NetworkError
241
315
  end
242
316
 
317
+ def use_ssl?
318
+ if supported_protocols == [:https]
319
+ true
320
+ elsif supported_protocols == [:http]
321
+ false
322
+ else
323
+ configuration.use_https
324
+ end
325
+ end
326
+
327
+ def configure_ssl!(client); end
328
+
243
329
  def check_api_key_configuration!(query)
244
330
  key_parts = query.lookup.required_api_key_parts
245
331
  if key_parts.size > Array(configuration.api_key).size