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,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
@@ -0,0 +1,58 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/latlon'
3
+
4
+ module Geocoder::Lookup
5
+ class Latlon < Base
6
+
7
+ def name
8
+ "LatLon.io"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["api_key"]
13
+ end
14
+
15
+ private # ---------------------------------------------------------------
16
+
17
+ def base_query_url(query)
18
+ "#{protocol}://latlon.io/api/v1/#{'reverse_' if query.reverse_geocode?}geocode?"
19
+ end
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ if doc['error'].nil?
24
+ [doc]
25
+ # The API returned a 404 response, which indicates no results found
26
+ elsif doc['error']['type'] == 'api_error'
27
+ []
28
+ elsif doc['error']['type'] == 'authentication_error'
29
+ raise_error(Geocoder::InvalidApiKey) ||
30
+ Geocoder.log(:warn, "LatLon.io service error: invalid API key.")
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+ def supported_protocols
37
+ [:https]
38
+ end
39
+
40
+ private # ---------------------------------------------------------------
41
+
42
+ def query_url_params(query)
43
+ if query.reverse_geocode?
44
+ {
45
+ :token => configuration.api_key,
46
+ :lat => query.coordinates[0],
47
+ :lon => query.coordinates[1]
48
+ }.merge(super)
49
+ else
50
+ {
51
+ :token => configuration.api_key,
52
+ :address => query.sanitized_text
53
+ }.merge(super)
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ require 'geocoder/lookups/nominatim'
2
+ require "geocoder/results/location_iq"
3
+
4
+ module Geocoder::Lookup
5
+ class LocationIq < Nominatim
6
+ def name
7
+ "LocationIq"
8
+ end
9
+
10
+ def required_api_key_parts
11
+ ["api_key"]
12
+ end
13
+
14
+ def supported_protocols
15
+ [:https]
16
+ end
17
+
18
+ private # ----------------------------------------------------------------
19
+
20
+ def base_query_url(query)
21
+ method = query.reverse_geocode? ? "reverse" : "search"
22
+ "#{protocol}://#{configured_host}/v1/#{method}.php?"
23
+ end
24
+
25
+ def query_url_params(query)
26
+ {
27
+ key: configuration.api_key
28
+ }.merge(super)
29
+ end
30
+
31
+ def configured_host
32
+ configuration[:host] || "us1.locationiq.com"
33
+ end
34
+
35
+ def results(query)
36
+ return [] unless doc = fetch_data(query)
37
+
38
+ if !doc.is_a?(Array)
39
+ case doc['error']
40
+ when "Invalid key"
41
+ raise_error(Geocoder::InvalidApiKey, doc['error'])
42
+ when "Key not active - Please write to contact@unwiredlabs.com"
43
+ raise_error(Geocoder::RequestDenied, doc['error'])
44
+ when "Rate Limited"
45
+ raise_error(Geocoder::OverQueryLimitError, doc['error'])
46
+ when "Unknown error - Please try again after some time"
47
+ raise_error(Geocoder::InvalidRequest, doc['error'])
48
+ end
49
+ end
50
+
51
+ doc.is_a?(Array) ? doc : [doc]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,59 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/mapbox"
3
+
4
+ module Geocoder::Lookup
5
+ class Mapbox < Base
6
+
7
+ def name
8
+ "Mapbox"
9
+ end
10
+
11
+ private # ---------------------------------------------------------------
12
+
13
+ def base_query_url(query)
14
+ "#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{mapbox_search_term(query)}.json?"
15
+ end
16
+
17
+ def results(query)
18
+ return [] unless data = fetch_data(query)
19
+ if data['features']
20
+ sort_relevant_feature(data['features'])
21
+ elsif data['message'] =~ /Invalid\sToken/
22
+ raise_error(Geocoder::InvalidApiKey, data['message'])
23
+ else
24
+ []
25
+ end
26
+ end
27
+
28
+ def query_url_params(query)
29
+ {access_token: configuration.api_key}.merge(super(query))
30
+ end
31
+
32
+ def mapbox_search_term(query)
33
+ require 'cgi' unless defined?(CGI) && defined?(CGI.escape)
34
+ if query.reverse_geocode?
35
+ lat,lon = query.coordinates
36
+ "#{CGI.escape lon},#{CGI.escape lat}"
37
+ else
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
41
+ end
42
+ end
43
+
44
+ def dataset
45
+ configuration[:dataset] || "mapbox.places"
46
+ end
47
+
48
+ def supported_protocols
49
+ [:https]
50
+ end
51
+
52
+ def sort_relevant_feature(features)
53
+ # Sort by descending relevance; Favor original order for equal relevance (eg occurs for reverse geocoding)
54
+ features.sort_by do |feature|
55
+ [feature["relevance"],-features.index(feature)]
56
+ end.reverse
57
+ end
58
+ end
59
+ end
@@ -13,15 +13,13 @@ module Geocoder::Lookup
13
13
  ["key"]
14
14
  end
15
15
 
16
- def query_url(query)
17
- domain = configuration[:licensed] ? "www" : "open"
18
- version = configuration[:version] || 1
19
- url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v#{version}/#{search_type(query)}?"
20
- url + url_query_string(query)
21
- end
22
-
23
16
  private # ---------------------------------------------------------------
24
17
 
18
+ def base_query_url(query)
19
+ domain = configuration[:open] ? "open" : "www"
20
+ "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
21
+ end
22
+
25
23
  def search_type(query)
26
24
  query.reverse_geocode? ? "reverse" : "address"
27
25
  end
@@ -45,14 +43,15 @@ module Geocoder::Lookup
45
43
  case doc['info']['statuscode']
46
44
  when 400 # Error with input
47
45
  raise_error(Geocoder::InvalidRequest, messages) ||
48
- warn("Mapquest Geocoding API error: #{messages}")
46
+ Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
49
47
  when 403 # Key related error
50
48
  raise_error(Geocoder::InvalidApiKey, messages) ||
51
- warn("Mapquest Geocoding API error: #{messages}")
49
+ Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
52
50
  when 500 # Unknown error
53
51
  raise_error(Geocoder::Error, messages) ||
54
- warn("Mapquest Geocoding API error: #{messages}")
52
+ Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
55
53
  end
54
+ []
56
55
  end
57
56
 
58
57
  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,21 +57,23 @@ 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)
65
65
  return [doc]
66
66
  elsif doc.last == "INVALID_LICENSE_KEY"
67
- raise_error(Geocoder::InvalidApiKey) || warn("Invalid MaxMind API key.")
67
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid MaxMind API key.")
68
68
  end
69
69
  end
70
70
  return []
71
71
  end
72
72
 
73
73
  def parse_raw_data(raw_data)
74
- CSV.parse_line raw_data
74
+ # Maxmind just returns text/plain as csv format but according to documentation,
75
+ # we get ISO-8859-1 encoded string. We need to convert it.
76
+ CSV.parse_line raw_data.force_encoding("ISO-8859-1").encode("UTF-8")
75
77
  end
76
78
 
77
79
  def reserved_result
@@ -0,0 +1,70 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/maxmind_geoip2'
3
+
4
+ module Geocoder::Lookup
5
+ class MaxmindGeoip2 < Base
6
+
7
+ def name
8
+ "MaxMind GeoIP2"
9
+ end
10
+
11
+ # Maxmind's GeoIP2 Precision Services only supports HTTPS,
12
+ # otherwise a `404 Not Found` HTTP response will be returned
13
+ def supported_protocols
14
+ [:https]
15
+ end
16
+
17
+ def query_url(query)
18
+ "#{protocol}://geoip.maxmind.com/geoip/v2.1/#{configured_service!}/#{query.sanitized_text.strip}"
19
+ end
20
+
21
+ private # ---------------------------------------------------------------
22
+
23
+ def cache_key(query)
24
+ query_url(query)
25
+ end
26
+
27
+ ##
28
+ # Return the name of the configured service, or raise an exception.
29
+ #
30
+ def configured_service!
31
+ if s = configuration[:service] and services.include?(s) and configuration[:basic_auth][:user] and configuration[:basic_auth][:password]
32
+ return s
33
+ else
34
+ raise(
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
+ )
37
+ end
38
+ end
39
+
40
+ def data_contains_error?(doc)
41
+ (["code", "error"] - doc.keys).empty?
42
+ end
43
+
44
+ ##
45
+ # Service names used in URL.
46
+ #
47
+ def services
48
+ [
49
+ :country,
50
+ :city,
51
+ :insights,
52
+ ]
53
+ end
54
+
55
+ def results(query)
56
+ # don't look up a loopback or private address, just return the stored result
57
+ return [] if query.internal_ip_address?
58
+
59
+ doc = fetch_data(query)
60
+ if doc
61
+ if !data_contains_error?(doc)
62
+ return [doc]
63
+ else
64
+ Geocoder.log(:warn, "MaxMind GeoIP2 Geocoding API error: #{doc['code']} (#{doc['error']}).")
65
+ end
66
+ end
67
+ return []
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,71 @@
1
+ require 'ipaddr'
2
+ require 'geocoder/lookups/base'
3
+ require 'geocoder/results/maxmind_local'
4
+
5
+ module Geocoder::Lookup
6
+ class MaxmindLocal < Base
7
+
8
+ def initialize
9
+ if !configuration[:file].nil?
10
+ begin
11
+ gem = RUBY_PLATFORM == 'java' ? 'jgeoip' : 'geoip'
12
+ require gem
13
+ rescue LoadError
14
+ raise "Could not load geoip dependency. To use MaxMind Local lookup you must add the #{gem} gem to your Gemfile or have it installed in your system."
15
+ end
16
+ end
17
+ super
18
+ end
19
+
20
+ def name
21
+ "MaxMind Local"
22
+ end
23
+
24
+ def required_api_key_parts
25
+ []
26
+ end
27
+
28
+ private
29
+
30
+ def results(query)
31
+ if configuration[:file]
32
+ geoip_class = RUBY_PLATFORM == "java" ? JGeoIP : GeoIP
33
+ geoip_instance = geoip_class.new(configuration[:file])
34
+ result =
35
+ if configuration[:package] == :country
36
+ geoip_instance.country(query.to_s)
37
+ else
38
+ geoip_instance.city(query.to_s)
39
+ end
40
+ result.nil? ? [] : [encode_hash(result.to_hash)]
41
+ elsif configuration[:package] == :city
42
+ addr = IPAddr.new(query.text).to_i
43
+ q = "SELECT l.country, l.region, l.city, l.latitude, l.longitude
44
+ FROM maxmind_geolite_city_location l WHERE l.loc_id = (SELECT b.loc_id FROM maxmind_geolite_city_blocks b
45
+ WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num)"
46
+ format_result(q, [:country_name, :region_name, :city_name, :latitude, :longitude])
47
+ elsif configuration[:package] == :country
48
+ addr = IPAddr.new(query.text).to_i
49
+ q = "SELECT country, country_code FROM maxmind_geolite_country
50
+ WHERE start_ip_num <= #{addr} AND #{addr} <= end_ip_num"
51
+ format_result(q, [:country_name, :country_code2])
52
+ end
53
+ end
54
+
55
+ def encode_hash(hash, encoding = "UTF-8")
56
+ hash.inject({}) do |h,i|
57
+ h[i[0]] = i[1].is_a?(String) ? i[1].encode(encoding) : i[1]
58
+ h
59
+ end
60
+ end
61
+
62
+ def format_result(query, attr_names)
63
+ if r = ActiveRecord::Base.connection.execute(query).first
64
+ r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array
65
+ [Hash[*attr_names.zip(r).flatten]]
66
+ else
67
+ []
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,41 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/melissa_street"
3
+
4
+ module Geocoder::Lookup
5
+ class MelissaStreet < Base
6
+
7
+ def name
8
+ "MelissaStreet"
9
+ end
10
+
11
+ def results(query)
12
+ return [] unless doc = fetch_data(query)
13
+
14
+ if doc["TransmissionResults"] == "GE05"
15
+ raise_error(Geocoder::InvalidApiKey) ||
16
+ Geocoder.log(:warn, "Melissa service error: invalid API key.")
17
+ end
18
+
19
+ return doc["Records"]
20
+ end
21
+
22
+ private # ---------------------------------------------------------------
23
+
24
+ def base_query_url(query)
25
+ "#{protocol}://address.melissadata.net/v3/WEB/GlobalAddress/doGlobalAddress?"
26
+ end
27
+
28
+ def query_url_params(query)
29
+ params = {
30
+ id: configuration.api_key,
31
+ format: "JSON",
32
+ a1: query.sanitized_text,
33
+ loc: query.options[:city],
34
+ admarea: query.options[:state],
35
+ postal: query.options[:postal],
36
+ ctry: query.options[:country]
37
+ }
38
+ params.merge(super)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/nationaal_georegister_nl"
3
+
4
+ module Geocoder::Lookup
5
+ class NationaalGeoregisterNl < Base
6
+
7
+ def name
8
+ 'Nationaal Georegister Nederland'
9
+ end
10
+
11
+ private # ---------------------------------------------------------------
12
+
13
+ def cache_key(query)
14
+ base_query_url(query) + hash_to_query(query_url_params(query))
15
+ end
16
+
17
+ def base_query_url(query)
18
+ "#{protocol}://geodata.nationaalgeoregister.nl/locatieserver/v3/free?"
19
+ end
20
+
21
+ def valid_response?(response)
22
+ json = parse_json(response.body)
23
+ super(response) if json
24
+ end
25
+
26
+ def results(query)
27
+ return [] unless doc = fetch_data(query)
28
+ return doc['response']['docs']
29
+ end
30
+
31
+ def query_url_params(query)
32
+ {
33
+ fl: '*',
34
+ q: query.text
35
+ }.merge(super)
36
+ end
37
+ end
38
+ end
@@ -9,27 +9,47 @@ 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)
25
37
  doc.is_a?(Array) ? doc : [doc]
26
38
  end
27
39
 
40
+ def parse_raw_data(raw_data)
41
+ if raw_data.include?("Bandwidth limit exceeded")
42
+ raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.")
43
+ else
44
+ super(raw_data)
45
+ end
46
+ end
47
+
28
48
  def query_url_params(query)
29
49
  params = {
30
50
  :format => "json",
31
51
  :addressdetails => "1",
32
- :"accept-language" => configuration.language
52
+ :"accept-language" => (query.language || configuration.language)
33
53
  }.merge(super)
34
54
  if query.reverse_geocode?
35
55
  lat,lon = query.coordinates
@@ -0,0 +1,65 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/opencagedata'
3
+
4
+ module Geocoder::Lookup
5
+ class Opencagedata < Base
6
+
7
+ def name
8
+ "OpenCageData"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ private # ----------------------------------------------------------------
16
+
17
+ def base_query_url(query)
18
+ "#{protocol}://api.opencagedata.com/geocode/v1/json?"
19
+ end
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ # return doc["results"]
24
+
25
+ messages = doc['status']['message']
26
+ case doc['status']['code']
27
+ when 400 # Error with input
28
+ raise_error(Geocoder::InvalidRequest, messages) ||
29
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
30
+ when 403 # Key related error
31
+ raise_error(Geocoder::InvalidApiKey, messages) ||
32
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
33
+ when 402 # Quata Exceeded
34
+ raise_error(Geocoder::OverQueryLimitError, messages) ||
35
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
36
+ when 500 # Unknown error
37
+ raise_error(Geocoder::Error, messages) ||
38
+ Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
39
+ end
40
+
41
+ return doc["results"]
42
+ end
43
+
44
+ def query_url_params(query)
45
+ params = {
46
+ :q => query.sanitized_text,
47
+ :key => configuration.api_key,
48
+ :language => (query.language || configuration.language)
49
+ }.merge(super)
50
+
51
+ [:abbrv, :countrycode, :min_confidence, :no_dedupe, :no_annotations, :no_record, :limit].each do |option|
52
+ unless (option_value = query.options[option]).nil?
53
+ params[option] = option_value
54
+ end
55
+ end
56
+
57
+ unless (bounds = query.options[:bounds]).nil?
58
+ params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join(',')
59
+ end
60
+
61
+ params
62
+ end
63
+
64
+ end
65
+ end