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,50 @@
1
+ require "geocoder/lookups/google"
2
+ require "geocoder/results/google_places_details"
3
+
4
+ module Geocoder
5
+ module Lookup
6
+ class GooglePlacesDetails < Google
7
+ def name
8
+ "Google Places Details"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def supported_protocols
16
+ [:https]
17
+ end
18
+
19
+ def query_url(query)
20
+ "#{protocol}://maps.googleapis.com/maps/api/place/details/json?#{url_query_string(query)}"
21
+ end
22
+
23
+ private
24
+
25
+ def results(query)
26
+ return [] unless doc = fetch_data(query)
27
+
28
+ case doc["status"]
29
+ when "OK"
30
+ return [doc["result"]]
31
+ when "OVER_QUERY_LIMIT"
32
+ raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Google Places Details API error: over query limit.")
33
+ when "REQUEST_DENIED"
34
+ raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Google Places Details API error: request denied.")
35
+ when "INVALID_REQUEST"
36
+ raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Google Places Details API error: invalid request.")
37
+ end
38
+
39
+ []
40
+ end
41
+
42
+ def query_url_google_params(query)
43
+ {
44
+ placeid: query.text,
45
+ language: query.language || configuration.language
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -31,7 +31,7 @@ module Geocoder::Lookup
31
31
 
32
32
  def sign(string)
33
33
  raw_private_key = url_safe_base64_decode(configuration.api_key[0])
34
- digest = OpenSSL::Digest::Digest.new('sha1')
34
+ digest = OpenSSL::Digest.new('sha1')
35
35
  raw_signature = OpenSSL::HMAC.digest(digest, raw_private_key, string)
36
36
  url_safe_base64_encode(raw_signature)
37
37
  end
@@ -0,0 +1,62 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/here'
3
+
4
+ module Geocoder::Lookup
5
+ class Here < Base
6
+
7
+ def name
8
+ "Here"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["app_id", "app_code"]
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{protocol}://#{if query.reverse_geocode? then 'reverse.' end}geocoder.api.here.com/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ return [] unless doc['Response'] && doc['Response']['View']
24
+ if r=doc['Response']['View']
25
+ return [] if r.nil? || !r.is_a?(Array) || r.empty?
26
+ return r.first['Result']
27
+ end
28
+ []
29
+ end
30
+
31
+ def query_url_params(query)
32
+ options = {
33
+ :gen=>4,
34
+ :app_id=>api_key,
35
+ :app_code=>api_code
36
+ }
37
+
38
+ if query.reverse_geocode?
39
+ super.merge(options).merge(
40
+ :prox=>query.sanitized_text,
41
+ :mode=>:retrieveAddresses
42
+ )
43
+ else
44
+ super.merge(options).merge(
45
+ :searchtext=>query.sanitized_text
46
+ )
47
+ end
48
+ end
49
+
50
+ def api_key
51
+ if a=configuration.api_key
52
+ return a.first if a.is_a?(Array)
53
+ end
54
+ end
55
+
56
+ def api_code
57
+ if a=configuration.api_key
58
+ return a.last if a.is_a?(Array)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,86 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ipapi_com'
3
+
4
+ module Geocoder::Lookup
5
+ class IpapiCom < Base
6
+
7
+ def name
8
+ "ip-api.com"
9
+ end
10
+
11
+ def query_url(query)
12
+ domain = configuration.api_key ? "pro.ip-api.com" : "ip-api.com"
13
+ url_ = "#{protocol}://#{domain}/json/#{query.sanitized_text}"
14
+
15
+ if (params = url_query_string(query)) && !params.empty?
16
+ url_ + "?" + params
17
+ else
18
+ url_
19
+ end
20
+ end
21
+
22
+ def supported_protocols
23
+ if configuration.api_key
24
+ [:http, :https]
25
+ else
26
+ [:http]
27
+ end
28
+ end
29
+
30
+
31
+ private
32
+
33
+ def parse_raw_data(raw_data)
34
+ if raw_data.chomp == "invalid key"
35
+ invalid_key_result
36
+ else
37
+ super(raw_data)
38
+ end
39
+ end
40
+
41
+ def results(query)
42
+ return [reserved_result(query.text)] if query.loopback_ip_address?
43
+
44
+ return [] unless doc = fetch_data(query)
45
+
46
+ if doc["message"] == "invalid key"
47
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.")
48
+ return []
49
+ else
50
+ return [doc]
51
+ end
52
+ end
53
+
54
+ def reserved_result(query)
55
+ {
56
+ "message" => "reserved range",
57
+ "query" => query,
58
+ "status" => "fail",
59
+ "ip" => query,
60
+ "city" => "",
61
+ "region_code" => "",
62
+ "region_name" => "",
63
+ "metrocode" => "",
64
+ "zipcode" => "",
65
+ "latitude" => "0",
66
+ "longitude" => "0",
67
+ "country_name" => "Reserved",
68
+ "country_code" => "RD"
69
+ }
70
+ end
71
+
72
+ def invalid_key_result
73
+ {
74
+ "message" => "invalid key"
75
+ }
76
+ end
77
+
78
+ def query_url_params(query)
79
+ params = {}
80
+ params.merge!(fields: configuration[:fields]) if configuration.has_key?(:fields)
81
+ params.merge!(key: configuration.api_key) if configuration.api_key
82
+ params.merge(super)
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,55 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ipinfo_io'
3
+
4
+ module Geocoder::Lookup
5
+ class IpinfoIo < Base
6
+
7
+ def name
8
+ "Ipinfo.io"
9
+ end
10
+
11
+ def query_url(query)
12
+ if configuration.api_key
13
+ "#{protocol}://ipinfo.io/#{query.sanitized_text}/geo?" + url_query_string(query)
14
+ else
15
+ "#{protocol}://ipinfo.io/#{query.sanitized_text}/geo"
16
+ end
17
+ end
18
+
19
+ # HTTPS available only for paid plans
20
+ def supported_protocols
21
+ if configuration.api_key
22
+ [:http, :https]
23
+ else
24
+ [:http]
25
+ end
26
+ end
27
+
28
+ private # ---------------------------------------------------------------
29
+
30
+ def results(query)
31
+ # don't look up a loopback address, just return the stored result
32
+ return [reserved_result(query.text)] if query.loopback_ip_address?
33
+ if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc)
34
+ []
35
+ else
36
+ [doc]
37
+ end
38
+ end
39
+
40
+ def empty_result?(doc)
41
+ !doc.is_a?(Hash) or doc.keys == ["ip"]
42
+ end
43
+
44
+ def reserved_result(ip)
45
+ {"message" => "Input string is not a valid IP address", "code" => 401}
46
+ end
47
+
48
+ def query_url_params(query)
49
+ {
50
+ token: configuration.api_key
51
+ }.merge(super)
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,59 @@
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
+ def query_url(query)
16
+ "#{protocol}://latlon.io/api/v1/#{'reverse_' if query.reverse_geocode?}geocode?#{url_query_string(query)}"
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
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
29
+ doc['error']['type'] == 'authentication_error'
30
+ raise_error(Geocoder::InvalidApiKey) ||
31
+ Geocoder.log(:warn, "LatLon.io service error: invalid API key.")
32
+ else
33
+ []
34
+ end
35
+ end
36
+
37
+ def supported_protocols
38
+ [:https]
39
+ end
40
+
41
+ private # ---------------------------------------------------------------
42
+
43
+ def query_url_params(query)
44
+ if query.reverse_geocode?
45
+ {
46
+ :token => configuration.api_key,
47
+ :lat => query.coordinates[0],
48
+ :lon => query.coordinates[1]
49
+ }.merge(super)
50
+ else
51
+ {
52
+ :token => configuration.api_key,
53
+ :address => query.sanitized_text
54
+ }.merge(super)
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,53 @@
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
+ def query_url(query)
12
+ "#{protocol}://api.mapbox.com/geocoding/v5/#{dataset}/#{url_query_string(query)}.json?access_token=#{configuration.api_key}"
13
+ end
14
+
15
+ private # ---------------------------------------------------------------
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 url_query_string(query)
29
+ require 'cgi' unless defined?(CGI) && defined?(CGI.escape)
30
+ if query.reverse_geocode?
31
+ lat,lon = query.coordinates
32
+ "#{CGI.escape lon},#{CGI.escape lat}"
33
+ else
34
+ CGI.escape query.text.to_s
35
+ end
36
+ end
37
+
38
+ def dataset
39
+ configuration[:dataset] || "mapbox.places"
40
+ end
41
+
42
+ def supported_protocols
43
+ [:https]
44
+ end
45
+
46
+ def sort_relevant_feature(features)
47
+ # Sort by descending relevance; Favor original order for equal relevance (eg occurs for reverse geocoding)
48
+ features.sort_by do |feature|
49
+ [feature["relevance"],-features.index(feature)]
50
+ end.reverse
51
+ end
52
+ end
53
+ end
@@ -14,9 +14,8 @@ module Geocoder::Lookup
14
14
  end
15
15
 
16
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)}?"
17
+ domain = configuration[:open] ? "open" : "www"
18
+ url = "#{protocol}://#{domain}.mapquestapi.com/geocoding/v1/#{search_type(query)}?"
20
19
  url + url_query_string(query)
21
20
  end
22
21
 
@@ -45,14 +44,15 @@ module Geocoder::Lookup
45
44
  case doc['info']['statuscode']
46
45
  when 400 # Error with input
47
46
  raise_error(Geocoder::InvalidRequest, messages) ||
48
- warn("Mapquest Geocoding API error: #{messages}")
47
+ Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
49
48
  when 403 # Key related error
50
49
  raise_error(Geocoder::InvalidApiKey, messages) ||
51
- warn("Mapquest Geocoding API error: #{messages}")
50
+ Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
52
51
  when 500 # Unknown error
53
52
  raise_error(Geocoder::Error, messages) ||
54
- warn("Mapquest Geocoding API error: #{messages}")
53
+ Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
55
54
  end
55
+ []
56
56
  end
57
57
 
58
58
  end
@@ -0,0 +1,15 @@
1
+ require 'geocoder/lookups/pelias'
2
+ require 'geocoder/results/mapzen'
3
+
4
+ # https://mapzen.com/documentation/search/search/ for more information
5
+ module Geocoder::Lookup
6
+ class Mapzen < Pelias
7
+ def name
8
+ 'Mapzen'
9
+ end
10
+
11
+ def endpoint
12
+ configuration[:endpoint] || 'search.mapzen.com'
13
+ end
14
+ end
15
+ end
@@ -64,14 +64,16 @@ module Geocoder::Lookup
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,69 @@
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!}/#{URI.escape(query.sanitized_text.strip)}"
19
+ end
20
+
21
+ private # ---------------------------------------------------------------
22
+
23
+ ##
24
+ # Return the name of the configured service, or raise an exception.
25
+ #
26
+ def configured_service!
27
+ if s = configuration[:service] and services.include?(s) and configuration[:basic_auth][:user] and configuration[:basic_auth][:password]
28
+ return s
29
+ else
30
+ raise(
31
+ Geocoder::ConfigurationError,
32
+ "When using MaxMind GeoIP2 you MUST specify a service name and basic_auth: " +
33
+ "Geocoder.configure(:maxmind_geoip2 => {:service => ...}, " +
34
+ ":basic_auth => {:user ..., :password => ...}), " +
35
+ "where service is one of: #{services.inspect}"
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 address
57
+ return [] if query.loopback_ip_address?
58
+ doc = fetch_data(query)
59
+ if doc
60
+ if !data_contains_error?(doc)
61
+ return [doc]
62
+ else
63
+ Geocoder.log(:warn, "MaxMind GeoIP2 Geocoding API error: #{doc['code']} (#{doc['error']}).")
64
+ end
65
+ end
66
+ return []
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,65 @@
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
+ result = geoip_class.new(configuration[:file]).city(query.to_s)
34
+ result.nil? ? [] : [encode_hash(result.to_hash)]
35
+ elsif configuration[:package] == :city
36
+ addr = IPAddr.new(query.text).to_i
37
+ q = "SELECT l.country, l.region, l.city, l.latitude, l.longitude
38
+ FROM maxmind_geolite_city_location l WHERE l.loc_id = (SELECT b.loc_id FROM maxmind_geolite_city_blocks b
39
+ WHERE b.start_ip_num <= #{addr} AND #{addr} <= b.end_ip_num)"
40
+ format_result(q, [:country_name, :region_name, :city_name, :latitude, :longitude])
41
+ elsif configuration[:package] == :country
42
+ addr = IPAddr.new(query.text).to_i
43
+ q = "SELECT country, country_code FROM maxmind_geolite_country
44
+ WHERE start_ip_num <= #{addr} AND #{addr} <= end_ip_num"
45
+ format_result(q, [:country_name, :country_code2])
46
+ end
47
+ end
48
+
49
+ def encode_hash(hash, encoding = "UTF-8")
50
+ hash.inject({}) do |h,i|
51
+ h[i[0]] = i[1].is_a?(String) ? i[1].encode(encoding) : i[1]
52
+ h
53
+ end
54
+ end
55
+
56
+ def format_result(query, attr_names)
57
+ if r = ActiveRecord::Base.connection.execute(query).first
58
+ r = r.values if r.is_a?(Hash) # some db adapters return Hash, some Array
59
+ [Hash[*attr_names.zip(r).flatten]]
60
+ else
61
+ []
62
+ end
63
+ end
64
+ end
65
+ end
@@ -25,11 +25,19 @@ module Geocoder::Lookup
25
25
  doc.is_a?(Array) ? doc : [doc]
26
26
  end
27
27
 
28
+ def parse_raw_data(raw_data)
29
+ if raw_data.include?("Bandwidth limit exceeded")
30
+ raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.")
31
+ else
32
+ super(raw_data)
33
+ end
34
+ end
35
+
28
36
  def query_url_params(query)
29
37
  params = {
30
38
  :format => "json",
31
39
  :addressdetails => "1",
32
- :"accept-language" => configuration.language
40
+ :"accept-language" => (query.language || configuration.language)
33
41
  }.merge(super)
34
42
  if query.reverse_geocode?
35
43
  lat,lon = query.coordinates
@@ -0,0 +1,44 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/okf"
3
+
4
+ module Geocoder::Lookup
5
+ class Okf < Base
6
+
7
+ def name
8
+ "Okf"
9
+ end
10
+
11
+ def query_url(query)
12
+ "#{protocol}://data.okf.fi/gis/1/geocode/json?" + url_query_string(query)
13
+ end
14
+
15
+ private # ---------------------------------------------------------------
16
+
17
+ def valid_response?(response)
18
+ json = parse_json(response.body)
19
+ status = json["status"] if json
20
+ super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
21
+ end
22
+
23
+ def results(query)
24
+ return [] unless doc = fetch_data(query)
25
+ case doc['status']; when "OK" # OK status implies >0 results
26
+ return doc['results']
27
+ end
28
+ return []
29
+ end
30
+
31
+ def query_url_okf_params(query)
32
+ params = {
33
+ (query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
34
+ :sensor => "false",
35
+ :language => (query.language || configuration.language)
36
+ }
37
+ params
38
+ end
39
+
40
+ def query_url_params(query)
41
+ query_url_okf_params(query).merge(super)
42
+ end
43
+ end
44
+ end