geocoder 1.2.6 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (276) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +189 -1
  3. data/LICENSE +1 -1
  4. data/README.md +387 -755
  5. data/examples/autoexpire_cache_redis.rb +5 -3
  6. data/examples/reverse_geocode_job.rb +40 -0
  7. data/lib/generators/geocoder/config/templates/initializer.rb +17 -16
  8. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +2 -0
  9. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +2 -0
  10. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +1 -1
  11. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +1 -1
  12. data/lib/generators/geocoder/migration_version.rb +15 -0
  13. data/lib/geocoder/cache.rb +6 -2
  14. data/lib/geocoder/calculations.rb +30 -38
  15. data/lib/geocoder/cli.rb +2 -2
  16. data/lib/geocoder/configuration.rb +18 -5
  17. data/lib/geocoder/esri_token.rb +38 -0
  18. data/lib/geocoder/exceptions.rb +19 -0
  19. data/lib/geocoder/ip_address.rb +16 -11
  20. data/lib/geocoder/kernel_logger.rb +25 -0
  21. data/lib/geocoder/logger.rb +47 -0
  22. data/lib/geocoder/lookup.rb +31 -12
  23. data/lib/geocoder/lookups/amap.rb +63 -0
  24. data/lib/geocoder/lookups/baidu.rb +17 -9
  25. data/lib/geocoder/lookups/baidu_ip.rb +7 -31
  26. data/lib/geocoder/lookups/ban_data_gouv_fr.rb +143 -0
  27. data/lib/geocoder/lookups/base.rb +73 -25
  28. data/lib/geocoder/lookups/bing.rb +38 -15
  29. data/lib/geocoder/lookups/db_ip_com.rb +52 -0
  30. data/lib/geocoder/lookups/dstk.rb +4 -2
  31. data/lib/geocoder/lookups/esri.rb +55 -8
  32. data/lib/geocoder/lookups/freegeoip.rb +18 -5
  33. data/lib/geocoder/lookups/geocoder_ca.rb +5 -6
  34. data/lib/geocoder/lookups/geocodio.rb +8 -8
  35. data/lib/geocoder/lookups/geoip2.rb +10 -5
  36. data/lib/geocoder/lookups/geoportail_lu.rb +65 -0
  37. data/lib/geocoder/lookups/google.rb +37 -9
  38. data/lib/geocoder/lookups/google_places_details.rb +9 -9
  39. data/lib/geocoder/lookups/google_places_search.rb +33 -0
  40. data/lib/geocoder/lookups/google_premier.rb +11 -1
  41. data/lib/geocoder/lookups/here.rb +29 -23
  42. data/lib/geocoder/lookups/ip2location.rb +67 -0
  43. data/lib/geocoder/lookups/ipapi_com.rb +82 -0
  44. data/lib/geocoder/lookups/ipdata_co.rb +62 -0
  45. data/lib/geocoder/lookups/ipgeolocation.rb +51 -0
  46. data/lib/geocoder/lookups/ipinfo_io.rb +44 -0
  47. data/lib/geocoder/lookups/ipregistry.rb +68 -0
  48. data/lib/geocoder/lookups/ipstack.rb +63 -0
  49. data/lib/geocoder/lookups/latlon.rb +59 -0
  50. data/lib/geocoder/lookups/location_iq.rb +50 -0
  51. data/lib/geocoder/lookups/mapbox.rb +59 -0
  52. data/lib/geocoder/lookups/mapquest.rb +7 -9
  53. data/lib/geocoder/lookups/maxmind.rb +7 -7
  54. data/lib/geocoder/lookups/maxmind_geoip2.rb +70 -0
  55. data/lib/geocoder/lookups/maxmind_local.rb +9 -2
  56. data/lib/geocoder/lookups/nominatim.rb +18 -6
  57. data/lib/geocoder/lookups/opencagedata.rb +16 -9
  58. data/lib/geocoder/lookups/osmnames.rb +57 -0
  59. data/lib/geocoder/lookups/pelias.rb +63 -0
  60. data/lib/geocoder/lookups/pickpoint.rb +41 -0
  61. data/lib/geocoder/lookups/pointpin.rb +14 -13
  62. data/lib/geocoder/lookups/postcode_anywhere_uk.rb +7 -8
  63. data/lib/geocoder/lookups/postcodes_io.rb +31 -0
  64. data/lib/geocoder/lookups/smarty_streets.rb +23 -5
  65. data/lib/geocoder/lookups/telize.rb +42 -7
  66. data/lib/geocoder/lookups/tencent.rb +59 -0
  67. data/lib/geocoder/lookups/yandex.rb +17 -9
  68. data/lib/geocoder/models/active_record.rb +4 -3
  69. data/lib/geocoder/models/mongo_base.rb +0 -2
  70. data/lib/geocoder/query.rb +15 -1
  71. data/lib/geocoder/railtie.rb +1 -1
  72. data/lib/geocoder/request.rb +103 -14
  73. data/lib/geocoder/results/amap.rb +87 -0
  74. data/lib/geocoder/results/baidu.rb +10 -14
  75. data/lib/geocoder/results/ban_data_gouv_fr.rb +257 -0
  76. data/lib/geocoder/results/base.rb +13 -1
  77. data/lib/geocoder/results/bing.rb +5 -1
  78. data/lib/geocoder/results/db_ip_com.rb +58 -0
  79. data/lib/geocoder/results/esri.rb +30 -6
  80. data/lib/geocoder/results/freegeoip.rb +2 -7
  81. data/lib/geocoder/results/geocoder_ca.rb +3 -3
  82. data/lib/geocoder/results/geocodio.rb +15 -3
  83. data/lib/geocoder/results/geoip2.rb +37 -25
  84. data/lib/geocoder/results/geoportail_lu.rb +71 -0
  85. data/lib/geocoder/results/google.rb +26 -0
  86. data/lib/geocoder/results/google_places_details.rb +4 -0
  87. data/lib/geocoder/results/google_places_search.rb +52 -0
  88. data/lib/geocoder/results/here.rb +21 -1
  89. data/lib/geocoder/results/ip2location.rb +22 -0
  90. data/lib/geocoder/results/ipapi_com.rb +45 -0
  91. data/lib/geocoder/results/ipdata_co.rb +40 -0
  92. data/lib/geocoder/results/ipgeolocation.rb +59 -0
  93. data/lib/geocoder/results/ipinfo_io.rb +48 -0
  94. data/lib/geocoder/results/ipregistry.rb +308 -0
  95. data/lib/geocoder/results/ipstack.rb +60 -0
  96. data/lib/geocoder/results/latlon.rb +71 -0
  97. data/lib/geocoder/results/location_iq.rb +6 -0
  98. data/lib/geocoder/results/mapbox.rb +57 -0
  99. data/lib/geocoder/results/mapquest.rb +5 -8
  100. data/lib/geocoder/results/maxmind.rb +0 -5
  101. data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
  102. data/lib/geocoder/results/maxmind_local.rb +0 -5
  103. data/lib/geocoder/results/nominatim.rb +18 -3
  104. data/lib/geocoder/results/opencagedata.rb +20 -2
  105. data/lib/geocoder/results/osmnames.rb +56 -0
  106. data/lib/geocoder/results/pelias.rb +58 -0
  107. data/lib/geocoder/results/pickpoint.rb +6 -0
  108. data/lib/geocoder/results/pointpin.rb +0 -4
  109. data/lib/geocoder/results/postcodes_io.rb +40 -0
  110. data/lib/geocoder/results/smarty_streets.rb +55 -19
  111. data/lib/geocoder/results/telize.rb +0 -5
  112. data/lib/geocoder/results/tencent.rb +72 -0
  113. data/lib/geocoder/results/test.rb +1 -1
  114. data/lib/geocoder/results/yandex.rb +57 -7
  115. data/lib/geocoder/sql.rb +9 -6
  116. data/lib/geocoder/stores/active_record.rb +49 -10
  117. data/lib/geocoder/stores/base.rb +2 -14
  118. data/lib/geocoder/stores/mongo_base.rb +0 -31
  119. data/lib/geocoder/version.rb +1 -1
  120. data/lib/geocoder.rb +2 -1
  121. data/lib/hash_recursive_merge.rb +1 -2
  122. data/lib/maxmind_database.rb +4 -4
  123. data/lib/tasks/geocoder.rake +29 -4
  124. metadata +56 -159
  125. data/.gitignore +0 -6
  126. data/.travis.yml +0 -31
  127. data/Rakefile +0 -25
  128. data/gemfiles/Gemfile.mongoid-2.4.x +0 -16
  129. data/lib/geocoder/lookups/geocoder_us.rb +0 -39
  130. data/lib/geocoder/lookups/okf.rb +0 -43
  131. data/lib/geocoder/lookups/ovi.rb +0 -62
  132. data/lib/geocoder/lookups/yahoo.rb +0 -88
  133. data/lib/geocoder/results/geocoder_us.rb +0 -39
  134. data/lib/geocoder/results/okf.rb +0 -106
  135. data/lib/geocoder/results/ovi.rb +0 -62
  136. data/lib/geocoder/results/yahoo.rb +0 -55
  137. data/lib/oauth_util.rb +0 -112
  138. data/test/fixtures/baidu_invalid_key +0 -1
  139. data/test/fixtures/baidu_ip_202_198_16_3 +0 -19
  140. data/test/fixtures/baidu_ip_invalid_key +0 -1
  141. data/test/fixtures/baidu_ip_no_results +0 -1
  142. data/test/fixtures/baidu_no_results +0 -1
  143. data/test/fixtures/baidu_reverse +0 -1
  144. data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
  145. data/test/fixtures/bing_invalid_key +0 -1
  146. data/test/fixtures/bing_madison_square_garden +0 -40
  147. data/test/fixtures/bing_no_results +0 -16
  148. data/test/fixtures/bing_reverse +0 -42
  149. data/test/fixtures/cloudmade_invalid_key +0 -1
  150. data/test/fixtures/cloudmade_madison_square_garden +0 -1
  151. data/test/fixtures/cloudmade_no_results +0 -1
  152. data/test/fixtures/esri_madison_square_garden +0 -59
  153. data/test/fixtures/esri_no_results +0 -8
  154. data/test/fixtures/esri_reverse +0 -21
  155. data/test/fixtures/freegeoip_74_200_247_59 +0 -12
  156. data/test/fixtures/freegeoip_no_results +0 -1
  157. data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
  158. data/test/fixtures/geocoder_ca_no_results +0 -1
  159. data/test/fixtures/geocoder_ca_reverse +0 -34
  160. data/test/fixtures/geocoder_us_madison_square_garden +0 -1
  161. data/test/fixtures/geocoder_us_no_results +0 -1
  162. data/test/fixtures/geocodio_1101_pennsylvania_ave +0 -1
  163. data/test/fixtures/geocodio_bad_api_key +0 -3
  164. data/test/fixtures/geocodio_invalid +0 -4
  165. data/test/fixtures/geocodio_no_results +0 -1
  166. data/test/fixtures/geocodio_over_query_limit +0 -4
  167. data/test/fixtures/google_garbage +0 -456
  168. data/test/fixtures/google_madison_square_garden +0 -57
  169. data/test/fixtures/google_no_city_data +0 -44
  170. data/test/fixtures/google_no_locality +0 -51
  171. data/test/fixtures/google_no_results +0 -4
  172. data/test/fixtures/google_over_limit +0 -4
  173. data/test/fixtures/google_places_details_invalid_request +0 -4
  174. data/test/fixtures/google_places_details_madison_square_garden +0 -120
  175. data/test/fixtures/google_places_details_no_results +0 -4
  176. data/test/fixtures/google_places_details_no_reviews +0 -60
  177. data/test/fixtures/google_places_details_no_types +0 -66
  178. data/test/fixtures/here_madison_square_garden +0 -72
  179. data/test/fixtures/here_no_results +0 -8
  180. data/test/fixtures/mapquest_error +0 -16
  181. data/test/fixtures/mapquest_invalid_api_key +0 -16
  182. data/test/fixtures/mapquest_invalid_request +0 -16
  183. data/test/fixtures/mapquest_madison_square_garden +0 -52
  184. data/test/fixtures/mapquest_no_results +0 -16
  185. data/test/fixtures/maxmind_24_24_24_21 +0 -1
  186. data/test/fixtures/maxmind_24_24_24_22 +0 -1
  187. data/test/fixtures/maxmind_24_24_24_23 +0 -1
  188. data/test/fixtures/maxmind_24_24_24_24 +0 -1
  189. data/test/fixtures/maxmind_74_200_247_59 +0 -1
  190. data/test/fixtures/maxmind_invalid_key +0 -1
  191. data/test/fixtures/maxmind_no_results +0 -1
  192. data/test/fixtures/nominatim_madison_square_garden +0 -150
  193. data/test/fixtures/nominatim_no_results +0 -1
  194. data/test/fixtures/nominatim_over_limit +0 -1
  195. data/test/fixtures/okf_kirstinmaki +0 -67
  196. data/test/fixtures/okf_no_results +0 -4
  197. data/test/fixtures/opencagedata_invalid_api_key +0 -25
  198. data/test/fixtures/opencagedata_invalid_request +0 -26
  199. data/test/fixtures/opencagedata_madison_square_garden +0 -73
  200. data/test/fixtures/opencagedata_no_results +0 -29
  201. data/test/fixtures/opencagedata_over_limit +0 -31
  202. data/test/fixtures/ovi_madison_square_garden +0 -72
  203. data/test/fixtures/ovi_no_results +0 -8
  204. data/test/fixtures/pointpin_10_10_10_10 +0 -1
  205. data/test/fixtures/pointpin_555_555_555_555 +0 -1
  206. data/test/fixtures/pointpin_80_111_555_555 +0 -1
  207. data/test/fixtures/pointpin_no_results +0 -1
  208. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ +0 -1
  209. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error +0 -1
  210. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire +0 -1
  211. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded +0 -1
  212. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results +0 -1
  213. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey +0 -1
  214. data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key +0 -1
  215. data/test/fixtures/smarty_streets_11211 +0 -1
  216. data/test/fixtures/smarty_streets_madison_square_garden +0 -47
  217. data/test/fixtures/smarty_streets_no_results +0 -1
  218. data/test/fixtures/telize_10_10_10_10 +0 -1
  219. data/test/fixtures/telize_555_555_555_555 +0 -4
  220. data/test/fixtures/telize_74_200_247_59 +0 -1
  221. data/test/fixtures/telize_no_results +0 -1
  222. data/test/fixtures/yahoo_error +0 -1
  223. data/test/fixtures/yahoo_invalid_key +0 -2
  224. data/test/fixtures/yahoo_madison_square_garden +0 -52
  225. data/test/fixtures/yahoo_no_results +0 -10
  226. data/test/fixtures/yahoo_over_limit +0 -2
  227. data/test/fixtures/yandex_canada_rue_dupuis_14 +0 -446
  228. data/test/fixtures/yandex_invalid_key +0 -1
  229. data/test/fixtures/yandex_kremlin +0 -48
  230. data/test/fixtures/yandex_new_york +0 -1
  231. data/test/fixtures/yandex_no_city_and_town +0 -112
  232. data/test/fixtures/yandex_no_results +0 -16
  233. data/test/integration/http_client_test.rb +0 -31
  234. data/test/mongoid_test_helper.rb +0 -43
  235. data/test/test_helper.rb +0 -416
  236. data/test/unit/active_record_test.rb +0 -16
  237. data/test/unit/cache_test.rb +0 -37
  238. data/test/unit/calculations_test.rb +0 -220
  239. data/test/unit/configuration_test.rb +0 -55
  240. data/test/unit/error_handling_test.rb +0 -56
  241. data/test/unit/geocoder_test.rb +0 -78
  242. data/test/unit/https_test.rb +0 -17
  243. data/test/unit/ip_address_test.rb +0 -27
  244. data/test/unit/lookup_test.rb +0 -153
  245. data/test/unit/lookups/bing_test.rb +0 -68
  246. data/test/unit/lookups/dstk_test.rb +0 -26
  247. data/test/unit/lookups/esri_test.rb +0 -48
  248. data/test/unit/lookups/freegeoip_test.rb +0 -27
  249. data/test/unit/lookups/geocoder_ca_test.rb +0 -17
  250. data/test/unit/lookups/geocodio_test.rb +0 -55
  251. data/test/unit/lookups/geoip2_test.rb +0 -27
  252. data/test/unit/lookups/google_places_details_test.rb +0 -122
  253. data/test/unit/lookups/google_premier_test.rb +0 -22
  254. data/test/unit/lookups/google_test.rb +0 -84
  255. data/test/unit/lookups/mapquest_test.rb +0 -60
  256. data/test/unit/lookups/maxmind_local_test.rb +0 -28
  257. data/test/unit/lookups/maxmind_test.rb +0 -63
  258. data/test/unit/lookups/nominatim_test.rb +0 -31
  259. data/test/unit/lookups/okf_test.rb +0 -38
  260. data/test/unit/lookups/opencagedata_test.rb +0 -64
  261. data/test/unit/lookups/pointpin_test.rb +0 -30
  262. data/test/unit/lookups/postcode_anywhere_uk_test.rb +0 -70
  263. data/test/unit/lookups/smarty_streets_test.rb +0 -71
  264. data/test/unit/lookups/telize_test.rb +0 -36
  265. data/test/unit/lookups/yahoo_test.rb +0 -35
  266. data/test/unit/method_aliases_test.rb +0 -26
  267. data/test/unit/model_test.rb +0 -38
  268. data/test/unit/mongoid_test.rb +0 -47
  269. data/test/unit/near_test.rb +0 -87
  270. data/test/unit/oauth_util_test.rb +0 -31
  271. data/test/unit/proxy_test.rb +0 -37
  272. data/test/unit/query_test.rb +0 -52
  273. data/test/unit/rake_task_test.rb +0 -21
  274. data/test/unit/request_test.rb +0 -35
  275. data/test/unit/result_test.rb +0 -72
  276. data/test/unit/test_mode_test.rb +0 -70
@@ -12,19 +12,23 @@ 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
@@ -32,7 +36,7 @@ module Geocoder::Lookup
32
36
  meta = doc['metaDataProperty']['GeocoderResponseMetaData']
33
37
  return meta['found'].to_i > 0 ? doc['featureMember'] : []
34
38
  else
35
- warn "Yandex Geocoding API error: unexpected response format."
39
+ Geocoder.log(:warn, "Yandex Geocoding API error: unexpected response format.")
36
40
  return []
37
41
  end
38
42
  end
@@ -43,12 +47,16 @@ module Geocoder::Lookup
43
47
  else
44
48
  q = query.sanitized_text
45
49
  end
46
- {
50
+ params = {
47
51
  :geocode => q,
48
52
  :format => "json",
49
- :plng => "#{query.language || configuration.language}", # supports ru, uk, be
50
- :key => configuration.api_key
51
- }.merge(super)
53
+ :lang => "#{query.language || configuration.language}", # supports ru, uk, be, default -> ru
54
+ :apikey => configuration.api_key
55
+ }
56
+ unless (bounds = query.options[:bounds]).nil?
57
+ params[:bbox] = bounds.map{ |point| "%f,%f" % point }.join('~')
58
+ end
59
+ params.merge(super)
52
60
  end
53
61
  end
54
62
  end
@@ -18,7 +18,8 @@ module Geocoder
18
18
  :units => options[:units],
19
19
  :method => options[:method],
20
20
  :lookup => options[:lookup],
21
- :language => options[:language]
21
+ :language => options[:language],
22
+ :params => options[:params]
22
23
  )
23
24
  end
24
25
 
@@ -35,7 +36,8 @@ module Geocoder
35
36
  :units => options[:units],
36
37
  :method => options[:method],
37
38
  :lookup => options[:lookup],
38
- :language => options[:language]
39
+ :language => options[:language],
40
+ :params => options[:params]
39
41
  )
40
42
  end
41
43
 
@@ -47,4 +49,3 @@ module Geocoder
47
49
  end
48
50
  end
49
51
  end
50
-
@@ -1,5 +1,3 @@
1
- require 'geocoder'
2
-
3
1
  module Geocoder
4
2
 
5
3
  ##
@@ -32,7 +32,7 @@ module Geocoder
32
32
  # appropriate to the Query text.
33
33
  #
34
34
  def lookup
35
- if ip_address?
35
+ if !options[:street_address] and (options[:ip_address] or ip_address?)
36
36
  name = options[:ip_lookup] || Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
37
37
  else
38
38
  name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first
@@ -66,6 +66,13 @@ module Geocoder
66
66
  IpAddress.new(text).valid? rescue false
67
67
  end
68
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?
74
+ end
75
+
69
76
  ##
70
77
  # Is the Query text a loopback IP address?
71
78
  #
@@ -73,6 +80,13 @@ module Geocoder
73
80
  ip_address? && IpAddress.new(text).loopback?
74
81
  end
75
82
 
83
+ ##
84
+ # Is the Query text a private IP address?
85
+ #
86
+ def private_ip_address?
87
+ ip_address? && IpAddress.new(text).private?
88
+ end
89
+
76
90
  ##
77
91
  # Does the given string look like latitude/longitude coordinates?
78
92
  #
@@ -4,7 +4,7 @@ module Geocoder
4
4
  if defined? Rails::Railtie
5
5
  require 'rails'
6
6
  class Railtie < Rails::Railtie
7
- initializer 'geocoder.insert_into_active_record' do
7
+ initializer 'geocoder.insert_into_active_record', before: :load_config_initializers do
8
8
  ActiveSupport.on_load :active_record do
9
9
  Geocoder::Railtie.insert
10
10
  end
@@ -1,25 +1,114 @@
1
+ require 'ipaddr'
2
+
1
3
  module Geocoder
2
4
  module Request
3
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.
4
10
  def location
5
- @location ||= begin
6
- detected_ip = env['HTTP_X_REAL_IP'] || (
7
- env['HTTP_X_FORWARDED_FOR'] &&
8
- env['HTTP_X_FORWARDED_FOR'].split(",").first.strip
9
- )
10
- detected_ip = IpAddress.new(detected_ip.to_s)
11
- if detected_ip.valid? and !detected_ip.loopback?
12
- real_ip = detected_ip.to_s
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
13
92
  else
14
- real_ip = self.ip
93
+ ip
15
94
  end
16
- Geocoder.search(real_ip).first
17
95
  end
18
- @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
19
109
  end
20
110
  end
21
111
  end
22
112
 
23
- if defined?(Rack) and defined?(Rack::Request)
24
- Rack::Request.send :include, Geocoder::Request
25
- 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,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
@@ -7,38 +7,34 @@ module Geocoder::Result
7
7
  ['lat', 'lng'].map{ |i| @data['location'][i] }
8
8
  end
9
9
 
10
- def address
11
- @data['formatted_address']
12
- end
13
-
14
- def state
15
- province
16
- end
17
-
18
10
  def province
19
- @data['addressComponent']['province']
11
+ @data['addressComponent'] and @data['addressComponent']['province'] or ""
20
12
  end
21
13
 
14
+ alias_method :state, :province
15
+
22
16
  def city
23
- @data['addressComponent']['city']
17
+ @data['addressComponent'] and @data['addressComponent']['city'] or ""
24
18
  end
25
19
 
26
20
  def district
27
- @data['addressComponent']['district']
21
+ @data['addressComponent'] and @data['addressComponent']['district'] or ""
28
22
  end
29
23
 
30
24
  def street
31
- @data['addressComponent']['street']
25
+ @data['addressComponent'] and @data['addressComponent']['street'] or ""
32
26
  end
33
27
 
34
28
  def street_number
35
- @data['addressComponent']['street_number']
29
+ @data['addressComponent'] and @data['addressComponent']['street_number']
36
30
  end
37
31
 
38
32
  def formatted_address
39
- @data['formatted_address']
33
+ @data['formatted_address'] or ""
40
34
  end
41
35
 
36
+ alias_method :address, :formatted_address
37
+
42
38
  def address_components
43
39
  @data['addressComponent']
44
40
  end
@@ -0,0 +1,257 @@
1
+ # encoding: utf-8
2
+ require 'geocoder/results/base'
3
+
4
+ module Geocoder::Result
5
+ class BanDataGouvFr < Base
6
+
7
+ #### BASE METHODS ####
8
+
9
+ def self.response_attributes
10
+ %w[limit attribution version licence type features center]
11
+ end
12
+
13
+ response_attributes.each do |a|
14
+ unless method_defined?(a)
15
+ define_method a do
16
+ @data[a]
17
+ end
18
+ end
19
+ end
20
+
21
+ #### BEST RESULT ####
22
+
23
+ def result
24
+ features[0] if features.any?
25
+ end
26
+
27
+ #### GEOMETRY ####
28
+
29
+ def geometry
30
+ result['geometry'] if result
31
+ end
32
+
33
+ def precision
34
+ geometry['type'] if geometry
35
+ end
36
+
37
+ def coordinates
38
+ coords = geometry["coordinates"]
39
+ return [coords[1].to_f, coords[0].to_f]
40
+ end
41
+
42
+ #### PROPERTIES ####
43
+
44
+ # List of raw attrbutes returned by BAN data gouv fr API:
45
+ #
46
+ # :id => [string] UUID of the result, said to be not stable
47
+ # atm, based on IGN reference (Institut national de
48
+ # l'information géographique et forestière)
49
+ #
50
+ # :type => [string] result type (housenumber, street, city,
51
+ # town, village, locality)
52
+ #
53
+ # :score => [float] value between 0 and 1 giving result's
54
+ # relevancy
55
+ #
56
+ # :housenumber => [string] street number and extra information
57
+ # (bis, ter, A, B)
58
+ #
59
+ # :street => [string] street name
60
+ #
61
+ # :name => [string] housenumber and street name
62
+ #
63
+ # :postcode => [string] city post code (used for mails by La Poste,
64
+ # beware many cities got severeal postcodes)
65
+ #
66
+ # :citycode => [string] city code (INSEE reference,
67
+ # consider it as a french institutional UUID)
68
+ #
69
+ # :city => [string] city name
70
+ #
71
+ # :context => [string] department code, department name and
72
+ # region code
73
+ #
74
+ # :label => [string] full address without state, country name
75
+ # and country code
76
+ #
77
+ # CITIES ONLY PROPERTIES
78
+ #
79
+ # :adm_weight => [string] administrative weight (importance) of
80
+ # the city
81
+ #
82
+ # :population => [float] number of inhabitants with a 1000 factor
83
+ #
84
+ # For up to date doc (in french only) : https://adresse.data.gouv.fr/api/
85
+ #
86
+ def properties
87
+ result['properties'] if result
88
+ end
89
+
90
+ # List of usable Geocoder results' methods
91
+ #
92
+ # score => [float] result relevance 0 to 1
93
+ #
94
+ # location_id => [string] location's IGN UUID
95
+ #
96
+ # result_type => [string] housenumber / street / city
97
+ # / town / village / locality
98
+ #
99
+ # international_address => [string] full address with country code
100
+ #
101
+ # national_address => [string] full address with country code
102
+ #
103
+ # street_address => [string] housenumber + extra inf
104
+ # + street name
105
+ #
106
+ # street_number => [string] housenumber + extra inf
107
+ # (bis, ter, etc)
108
+ #
109
+ # street_name => [string] street's name
110
+ #
111
+ # city_name => [string] city's name
112
+ #
113
+ # city_code => [string] city's INSEE UUID
114
+ #
115
+ # postal_code => [string] city's postal code (used for mails)
116
+ #
117
+ # context => [string] city's department code, department
118
+ # name and region name
119
+ #
120
+ # demartment_name => [string] city's department name
121
+ #
122
+ # department_code => [string] city's department INSEE UUID
123
+ #
124
+ # region_name => [string] city's region name
125
+ #
126
+ # population => [string] city's inhabitants count
127
+ #
128
+ # administrative_weight => [integer] city's importance on a scale
129
+ # from 6 (capital city) to 1 (regular village)
130
+ #
131
+ def score
132
+ properties['score']
133
+ end
134
+
135
+ def location_id
136
+ properties['id']
137
+ end
138
+
139
+ # Types
140
+ #
141
+ # housenumber
142
+ # street
143
+ # city
144
+ # town
145
+ # village
146
+ # locality
147
+ #
148
+ def result_type
149
+ properties['type']
150
+ end
151
+
152
+ def international_address
153
+ "#{national_address}, #{country}"
154
+ end
155
+
156
+ def national_address
157
+ properties['label']
158
+ end
159
+
160
+ def street_address
161
+ properties['name']
162
+ end
163
+
164
+ def street_number
165
+ properties['housenumber']
166
+ end
167
+
168
+ def street_name
169
+ properties['street']
170
+ end
171
+
172
+ def city_name
173
+ properties['city']
174
+ end
175
+
176
+ def city_code
177
+ properties['citycode']
178
+ end
179
+
180
+ def postal_code
181
+ properties['postcode']
182
+ end
183
+
184
+ def context
185
+ properties['context'].split(/,/).map(&:strip)
186
+ end
187
+
188
+ def department_code
189
+ context[0] if context.length > 0
190
+ end
191
+
192
+ # Monkey logic to handle fact Paris is both a city and a department
193
+ # in Île-de-France region
194
+ def department_name
195
+ if context.length > 1
196
+ if context[1] == "Île-de-France"
197
+ "Paris"
198
+ else
199
+ context[1]
200
+ end
201
+ end
202
+ end
203
+
204
+ def region_name
205
+ if context.length == 2 && context[1] == "Île-de-France"
206
+ context[1]
207
+ elsif context.length > 2
208
+ context[2]
209
+ end
210
+ end
211
+
212
+ def country
213
+ "France"
214
+ end
215
+
216
+ # Country code types
217
+ # FR : France
218
+ # GF : Guyane Française
219
+ # RE : Réunion
220
+ # NC : Nouvelle-Calédonie
221
+ # GP : Guadeloupe
222
+ # MQ : Martinique
223
+ # MU : Maurice
224
+ # PF : Polynésie française
225
+ #
226
+ # Will need refacto to handle different country codes, but BAN API
227
+ # is currently mainly designed for geocode FR country code addresses
228
+ def country_code
229
+ "FR"
230
+ end
231
+
232
+ #### ALIAS METHODS ####
233
+
234
+ alias_method :address, :international_address
235
+ alias_method :street, :street_name
236
+ alias_method :city, :city_name
237
+ alias_method :state, :region_name
238
+ alias_method :state_code, :state
239
+
240
+ #### CITIES' METHODS ####
241
+
242
+ def population
243
+ (properties['population'].to_f * 1000).to_i if city?(result_type)
244
+ end
245
+
246
+ def administrative_weight
247
+ properties['adm_weight'].to_i if city?(result_type)
248
+ end
249
+
250
+ private
251
+
252
+ def city?(result_type)
253
+ %w(village town city).include?(result_type)
254
+ end
255
+
256
+ end
257
+ end
@@ -20,8 +20,20 @@ module Geocoder
20
20
  ##
21
21
  # A string in the given format.
22
22
  #
23
+ # This default implementation dumbly follows the United States address
24
+ # format and will return incorrect results for most countries. Some APIs
25
+ # return properly formatted addresses and those should be funneled
26
+ # through this method.
27
+ #
23
28
  def address(format = :full)
24
- fail
29
+ if state_code.to_s != ""
30
+ s = ", #{state_code}"
31
+ elsif state.to_s != ""
32
+ s = ", #{state}"
33
+ else
34
+ s = ""
35
+ end
36
+ "#{city}#{s} #{postal_code}, #{country}".sub(/^[ ,]*/, '')
25
37
  end
26
38
 
27
39
  ##