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,59 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/uk_ordnance_survey_names'
3
+
4
+ module Geocoder::Lookup
5
+ class UkOrdnanceSurveyNames < Base
6
+
7
+ def name
8
+ 'Ordance Survey Names'
9
+ end
10
+
11
+ def supported_protocols
12
+ [:https]
13
+ end
14
+
15
+ def base_query_url(query)
16
+ "#{protocol}://api.os.uk/search/names/v1/find?"
17
+ end
18
+
19
+ def required_api_key_parts
20
+ ["key"]
21
+ end
22
+
23
+ def query_url(query)
24
+ base_query_url(query) + url_query_string(query)
25
+ end
26
+
27
+ private # -------------------------------------------------------------
28
+
29
+ def results(query)
30
+ return [] unless doc = fetch_data(query)
31
+ return [] if doc['header']['totalresults'].zero?
32
+ return doc['results'].map { |r| r['GAZETTEER_ENTRY'] }
33
+ end
34
+
35
+ def query_url_params(query)
36
+ {
37
+ query: query.sanitized_text,
38
+ key: configuration.api_key,
39
+ fq: filter
40
+ }.merge(super)
41
+ end
42
+
43
+ def local_types
44
+ %w[
45
+ City
46
+ Hamlet
47
+ Other_Settlement
48
+ Town
49
+ Village
50
+ Postcode
51
+ ]
52
+ end
53
+
54
+ def filter
55
+ local_types.map { |t| "local_type:#{t}" }.join(' ')
56
+ end
57
+
58
+ end
59
+ end
@@ -12,27 +12,30 @@ module Geocoder::Lookup
12
12
  "http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}"
13
13
  end
14
14
 
15
- def query_url(query)
16
- "#{protocol}://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
15
+ def supported_protocols
16
+ [:https]
17
17
  end
18
18
 
19
19
  private # ---------------------------------------------------------------
20
20
 
21
+ def base_query_url(query)
22
+ "#{protocol}://geocode-maps.yandex.ru/1.x/?"
23
+ end
24
+
21
25
  def results(query)
22
26
  return [] unless doc = fetch_data(query)
23
27
  if err = doc['error']
24
28
  if err["status"] == 401 and err["message"] == "invalid key"
25
- raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key.")
29
+ raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.")
26
30
  else
27
- warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
31
+ Geocoder.log(:warn, "Yandex Geocoding API error: #{err['status']} (#{err['message']}).")
28
32
  end
29
33
  return []
30
34
  end
31
35
  if doc = doc['response']['GeoObjectCollection']
32
- meta = doc['metaDataProperty']['GeocoderResponseMetaData']
33
- return meta['found'].to_i > 0 ? doc['featureMember'] : []
36
+ return doc['featureMember'].to_a
34
37
  else
35
- warn "Yandex Geocoding API error: unexpected response format."
38
+ Geocoder.log(:warn, "Yandex Geocoding API error: unexpected response format.")
36
39
  return []
37
40
  end
38
41
  end
@@ -43,12 +46,16 @@ module Geocoder::Lookup
43
46
  else
44
47
  q = query.sanitized_text
45
48
  end
46
- {
49
+ params = {
47
50
  :geocode => q,
48
51
  :format => "json",
49
- :plng => "#{configuration.language}", # supports ru, uk, be
50
- :key => configuration.api_key
51
- }.merge(super)
52
+ :lang => "#{query.language || configuration.language}", # supports ru, uk, be, default -> ru
53
+ :apikey => configuration.api_key
54
+ }
55
+ unless (bounds = query.options[:bounds]).nil?
56
+ params[:bbox] = bounds.map{ |point| "%f,%f" % point }.join('~')
57
+ end
58
+ params.merge(super)
52
59
  end
53
60
  end
54
61
  end
@@ -16,7 +16,10 @@ module Geocoder
16
16
  :longitude => options[:longitude] || :longitude,
17
17
  :geocode_block => block,
18
18
  :units => options[:units],
19
- :method => options[:method]
19
+ :method => options[:method],
20
+ :lookup => options[:lookup],
21
+ :language => options[:language],
22
+ :params => options[:params]
20
23
  )
21
24
  end
22
25
 
@@ -30,8 +33,11 @@ module Geocoder
30
33
  :latitude => latitude_attr,
31
34
  :longitude => longitude_attr,
32
35
  :reverse_block => block,
33
- :units => options[:units],
34
- :method => options[:method]
36
+ :units => options[:units],
37
+ :method => options[:method],
38
+ :lookup => options[:lookup],
39
+ :language => options[:language],
40
+ :params => options[:params]
35
41
  )
36
42
  end
37
43
 
@@ -43,4 +49,3 @@ module Geocoder
43
49
  end
44
50
  end
45
51
  end
46
-
@@ -1,5 +1,3 @@
1
- require 'geocoder'
2
-
3
1
  module Geocoder
4
2
 
5
3
  ##
@@ -29,7 +27,7 @@ module Geocoder
29
27
  private # ----------------------------------------------------------------
30
28
 
31
29
  def geocoder_init(options)
32
- unless @geocoder_options
30
+ unless defined?(@geocoder_options)
33
31
  @geocoder_options = {}
34
32
  require "geocoder/stores/#{geocoder_file_name}"
35
33
  include Geocoder::Store.const_get(geocoder_module_name)
@@ -39,4 +37,3 @@ module Geocoder
39
37
  end
40
38
  end
41
39
  end
42
-
@@ -1,5 +1,3 @@
1
- require 'geocoder'
2
-
3
1
  module Geocoder
4
2
 
5
3
  ##
@@ -19,7 +17,9 @@ module Geocoder
19
17
  :geocode_block => block,
20
18
  :units => options[:units],
21
19
  :method => options[:method],
22
- :skip_index => options[:skip_index] || false
20
+ :skip_index => options[:skip_index] || false,
21
+ :lookup => options[:lookup],
22
+ :language => options[:language]
23
23
  )
24
24
  end
25
25
 
@@ -34,7 +34,9 @@ module Geocoder
34
34
  :reverse_block => block,
35
35
  :units => options[:units],
36
36
  :method => options[:method],
37
- :skip_index => options[:skip_index] || false
37
+ :skip_index => options[:skip_index] || false,
38
+ :lookup => options[:lookup],
39
+ :language => options[:language]
38
40
  )
39
41
  end
40
42
 
@@ -32,10 +32,10 @@ module Geocoder
32
32
  # appropriate to the Query text.
33
33
  #
34
34
  def lookup
35
- if ip_address?
36
- name = Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
35
+ if !options[:street_address] and (options[:ip_address] or ip_address?)
36
+ name = options[:ip_lookup] || Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
37
37
  else
38
- name = Configuration.lookup || Geocoder::Lookup.street_services.first
38
+ name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first
39
39
  end
40
40
  Lookup.get(name)
41
41
  end
@@ -63,14 +63,28 @@ module Geocoder
63
63
  # dot-delimited numbers.
64
64
  #
65
65
  def ip_address?
66
- !!text.to_s.match(/\A(::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\z/)
66
+ IpAddress.new(text).valid? rescue false
67
+ end
68
+
69
+ ##
70
+ # Is the Query text a loopback or private IP address?
71
+ #
72
+ def internal_ip_address?
73
+ ip_address? && IpAddress.new(text).internal?
67
74
  end
68
75
 
69
76
  ##
70
77
  # Is the Query text a loopback IP address?
71
78
  #
72
79
  def loopback_ip_address?
73
- !!(self.ip_address? and (text == "0.0.0.0" or text.to_s.match(/\A127/)))
80
+ ip_address? && IpAddress.new(text).loopback?
81
+ end
82
+
83
+ ##
84
+ # Is the Query text a private IP address?
85
+ #
86
+ def private_ip_address?
87
+ ip_address? && IpAddress.new(text).private?
74
88
  end
75
89
 
76
90
  ##
@@ -98,6 +112,10 @@ module Geocoder
98
112
  coordinates?
99
113
  end
100
114
 
115
+ def language
116
+ options[:language]
117
+ end
118
+
101
119
  private # ----------------------------------------------------------------
102
120
 
103
121
  def params_given?
@@ -1,17 +1,17 @@
1
- require 'geocoder'
2
1
  require 'geocoder/models/active_record'
3
2
 
4
3
  module Geocoder
5
4
  if defined? Rails::Railtie
6
5
  require 'rails'
7
6
  class Railtie < Rails::Railtie
8
- initializer 'geocoder.insert_into_active_record' do
7
+ initializer 'geocoder.insert_into_active_record', before: :load_config_initializers do
9
8
  ActiveSupport.on_load :active_record do
10
9
  Geocoder::Railtie.insert
11
10
  end
12
11
  end
13
12
  rake_tasks do
14
13
  load "tasks/geocoder.rake"
14
+ load "tasks/maxmind.rake"
15
15
  end
16
16
  end
17
17
  end
@@ -1,23 +1,114 @@
1
- require 'geocoder'
1
+ require 'ipaddr'
2
2
 
3
3
  module Geocoder
4
4
  module Request
5
5
 
6
+ # The location() method is vulnerable to trivial IP spoofing.
7
+ # Don't use it in authorization/authentication code, or any
8
+ # other security-sensitive application. Use safe_location
9
+ # instead.
6
10
  def location
7
- unless defined?(@location)
8
- if env.has_key?('HTTP_X_REAL_IP')
9
- @location = Geocoder.search(env['HTTP_X_REAL_IP']).first
10
- elsif env.has_key?('HTTP_X_FORWARDED_FOR')
11
- @location = Geocoder.search(env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/)[0]).first
11
+ @location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first
12
+ end
13
+
14
+ # This safe_location() protects you from trivial IP spoofing.
15
+ # For requests that go through a proxy that you haven't
16
+ # whitelisted as trusted in your Rack config, you will get the
17
+ # location for the IP of the last untrusted proxy in the chain,
18
+ # not the original client IP. You WILL NOT get the location
19
+ # corresponding to the original client IP for any request sent
20
+ # through a non-whitelisted proxy.
21
+ def safe_location
22
+ @safe_location ||= Geocoder.search(ip, ip_address: true).first
23
+ end
24
+
25
+ # There's a whole zoo of nonstandard headers added by various
26
+ # proxy softwares to indicate original client IP.
27
+ # ANY of these can be trivially spoofed!
28
+ # (except REMOTE_ADDR, which should by set by your server,
29
+ # and is included at the end as a fallback.
30
+ # Order does matter: we're following the convention established in
31
+ # ActionDispatch::RemoteIp::GetIp::calculate_ip()
32
+ # https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb
33
+ # where the forwarded_for headers, possibly containing lists,
34
+ # are arbitrarily preferred over headers expected to contain a
35
+ # single address.
36
+ GEOCODER_CANDIDATE_HEADERS = ['HTTP_X_FORWARDED_FOR',
37
+ 'HTTP_X_FORWARDED',
38
+ 'HTTP_FORWARDED_FOR',
39
+ 'HTTP_FORWARDED',
40
+ 'HTTP_X_CLIENT_IP',
41
+ 'HTTP_CLIENT_IP',
42
+ 'HTTP_X_REAL_IP',
43
+ 'HTTP_X_CLUSTER_CLIENT_IP',
44
+ 'REMOTE_ADDR']
45
+
46
+ def geocoder_spoofable_ip
47
+
48
+ # We could use a more sophisticated IP-guessing algorithm here,
49
+ # in which we'd try to resolve the use of different headers by
50
+ # different proxies. The idea is that by comparing IPs repeated
51
+ # in different headers, you can sometimes decide which header
52
+ # was used by a proxy further along in the chain, and thus
53
+ # prefer the headers used earlier. However, the gains might not
54
+ # be worth the performance tradeoff, since this method is likely
55
+ # to be called on every request in a lot of applications.
56
+ GEOCODER_CANDIDATE_HEADERS.each do |header|
57
+ if @env.has_key? header
58
+ addrs = geocoder_split_ip_addresses(@env[header])
59
+ addrs = geocoder_remove_port_from_addresses(addrs)
60
+ addrs = geocoder_reject_non_ipv4_addresses(addrs)
61
+ addrs = geocoder_reject_trusted_ip_addresses(addrs)
62
+ return addrs.first if addrs.any?
63
+ end
64
+ end
65
+
66
+ @env['REMOTE_ADDR']
67
+ end
68
+
69
+ private
70
+
71
+ def geocoder_split_ip_addresses(ip_addresses)
72
+ ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
73
+ end
74
+
75
+ # use Rack's trusted_proxy?() method to filter out IPs that have
76
+ # been configured as trusted; includes private ranges by
77
+ # default. (we don't want every lookup to return the location
78
+ # of our own proxy/load balancer)
79
+ def geocoder_reject_trusted_ip_addresses(ip_addresses)
80
+ ip_addresses.reject { |ip| trusted_proxy?(ip) }
81
+ end
82
+
83
+ def geocoder_remove_port_from_addresses(ip_addresses)
84
+ ip_addresses.map do |ip|
85
+ # IPv4
86
+ if ip.count('.') > 0
87
+ ip.split(':').first
88
+ # IPv6 bracket notation
89
+ elsif match = ip.match(/\[(\S+)\]/)
90
+ match.captures.first
91
+ # IPv6 bare notation
12
92
  else
13
- @location = Geocoder.search(ip).first
93
+ ip
14
94
  end
15
95
  end
16
- @location
96
+ end
97
+
98
+ def geocoder_reject_non_ipv4_addresses(ip_addresses)
99
+ ips = []
100
+ for ip in ip_addresses
101
+ begin
102
+ valid_ip = IPAddr.new(ip)
103
+ rescue
104
+ valid_ip = false
105
+ end
106
+ ips << valid_ip.to_s if valid_ip
107
+ end
108
+ return ips.any? ? ips : ip_addresses
17
109
  end
18
110
  end
19
111
  end
20
112
 
21
- if defined?(Rack) and defined?(Rack::Request)
22
- Rack::Request.send :include, Geocoder::Request
23
- end
113
+ ActionDispatch::Request.__send__(:include, Geocoder::Request) if defined?(ActionDispatch::Request)
114
+ Rack::Request.__send__(:include, Geocoder::Request) if defined?(Rack::Request)
@@ -0,0 +1,146 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder
4
+ module Result
5
+ class AbstractApi < Base
6
+
7
+ ##
8
+ # Geolocation
9
+
10
+ def state
11
+ @data['region']
12
+ end
13
+
14
+ def state_code
15
+ @data['region_iso_code']
16
+ end
17
+
18
+ def city
19
+ @data["city"]
20
+ end
21
+
22
+ def city_geoname_id
23
+ @data["city_geoname_id"]
24
+ end
25
+
26
+ def region_geoname_id
27
+ @data["region_geoname_id"]
28
+ end
29
+
30
+ def postal_code
31
+ @data["postal_code"]
32
+ end
33
+
34
+ def country
35
+ @data["country"]
36
+ end
37
+
38
+ def country_code
39
+ @data["country_code"]
40
+ end
41
+
42
+ def country_geoname_id
43
+ @data["country_geoname_id"]
44
+ end
45
+
46
+ def country_is_eu
47
+ @data["country_is_eu"]
48
+ end
49
+
50
+ def continent
51
+ @data["continent"]
52
+ end
53
+
54
+ def continent_code
55
+ @data["continent_code"]
56
+ end
57
+
58
+ def continent_geoname_id
59
+ @data["continent_geoname_id"]
60
+ end
61
+
62
+ ##
63
+ # Security
64
+
65
+ def is_vpn?
66
+ @data.dig "security", "is_vpn"
67
+ end
68
+
69
+ ##
70
+ # Timezone
71
+
72
+ def timezone_name
73
+ @data.dig "timezone", "name"
74
+ end
75
+
76
+ def timezone_abbreviation
77
+ @data.dig "timezone", "abbreviation"
78
+ end
79
+
80
+ def timezone_gmt_offset
81
+ @data.dig "timezone", "gmt_offset"
82
+ end
83
+
84
+ def timezone_current_time
85
+ @data.dig "timezone", "current_time"
86
+ end
87
+
88
+ def timezone_is_dst
89
+ @data.dig "timezone", "is_dst"
90
+ end
91
+
92
+ ##
93
+ # Flag
94
+
95
+ def flag_emoji
96
+ @data.dig "flag", "emoji"
97
+ end
98
+
99
+ def flag_unicode
100
+ @data.dig "flag", "unicode"
101
+ end
102
+
103
+ def flag_png
104
+ @data.dig "flag", "png"
105
+ end
106
+
107
+ def flag_svg
108
+ @data.dig "flag", "svg"
109
+ end
110
+
111
+ ##
112
+ # Currency
113
+
114
+ def currency_currency_name
115
+ @data.dig "currency", "currency_name"
116
+ end
117
+
118
+ def currency_currency_code
119
+ @data.dig "currency", "currency_code"
120
+ end
121
+
122
+ ##
123
+ # Connection
124
+
125
+ def connection_autonomous_system_number
126
+ @data.dig "connection", "autonomous_system_number"
127
+ end
128
+
129
+ def connection_autonomous_system_organization
130
+ @data.dig "connection", "autonomous_system_organization"
131
+ end
132
+
133
+ def connection_connection_type
134
+ @data.dig "connection", "connection_type"
135
+ end
136
+
137
+ def connection_isp_name
138
+ @data.dig "connection", "isp_name"
139
+ end
140
+
141
+ def connection_organization_name
142
+ @data.dig "connection", "organization_name"
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,87 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class Amap < Base
5
+
6
+ def coordinates
7
+ location = @data['location'] || @data['roadinters'].try(:first).try(:[], 'location') \
8
+ || address_components.try(:[], 'streetNumber').try(:[], 'location')
9
+ location.to_s.split(",").reverse.map(&:to_f)
10
+ end
11
+
12
+ def address
13
+ formatted_address
14
+ end
15
+
16
+ def state
17
+ province
18
+ end
19
+
20
+ def province
21
+ address_components['province']
22
+ end
23
+
24
+ def city
25
+ address_components['city'] == [] ? province : address_components["city"]
26
+ end
27
+
28
+ def district
29
+ address_components['district']
30
+ end
31
+
32
+ def street
33
+ if address_components["neighborhood"]["name"] != []
34
+ return address_components["neighborhood"]["name"]
35
+ elsif address_components['township'] != []
36
+ return address_components["township"]
37
+ else
38
+ return @data['street'] || address_components['streetNumber'].try(:[], 'street')
39
+ end
40
+ end
41
+
42
+ def street_number
43
+ @data['number'] || address_components['streetNumber'].try(:[], 'number')
44
+ end
45
+
46
+ def formatted_address
47
+ @data['formatted_address']
48
+ end
49
+
50
+ def address_components
51
+ @data['addressComponent'] || @data
52
+ end
53
+
54
+ def state_code
55
+ ""
56
+ end
57
+
58
+ def postal_code
59
+ ""
60
+ end
61
+
62
+ def country
63
+ "China"
64
+ end
65
+
66
+ def country_code
67
+ "CN"
68
+ end
69
+
70
+ ##
71
+ # Get address components of a given type. Valid types are defined in
72
+ # Baidu's Geocoding API documentation and include (among others):
73
+ #
74
+ # :business
75
+ # :cityCode
76
+ #
77
+ def self.response_attributes
78
+ %w[roads pois roadinters]
79
+ end
80
+
81
+ response_attributes.each do |a|
82
+ define_method a do
83
+ @data[a]
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,57 @@
1
+ require 'geocoder/results/base'
2
+
3
+ module Geocoder::Result
4
+ class AmazonLocationService < Base
5
+ def initialize(result)
6
+ @place = result
7
+ end
8
+
9
+ def coordinates
10
+ [@place.geometry.point[1], @place.geometry.point[0]]
11
+ end
12
+
13
+ def address
14
+ @place.label
15
+ end
16
+
17
+ def neighborhood
18
+ @place.neighborhood
19
+ end
20
+
21
+ def route
22
+ @place.street
23
+ end
24
+
25
+ def city
26
+ @place.municipality || @place.sub_region
27
+ end
28
+
29
+ def state
30
+ @place.region
31
+ end
32
+
33
+ def state_code
34
+ @place.region
35
+ end
36
+
37
+ def province
38
+ @place.region
39
+ end
40
+
41
+ def province_code
42
+ @place.region
43
+ end
44
+
45
+ def postal_code
46
+ @place.postal_code
47
+ end
48
+
49
+ def country
50
+ @place.country
51
+ end
52
+
53
+ def country_code
54
+ @place.country
55
+ end
56
+ end
57
+ end