geocoder 1.2.6 → 1.6.1

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 (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
  ##