bw-geocoder 1.2.5

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 (233) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +31 -0
  3. data/CHANGELOG.md +377 -0
  4. data/LICENSE +20 -0
  5. data/README.md +1041 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode +5 -0
  8. data/examples/autoexpire_cache_dalli.rb +62 -0
  9. data/examples/autoexpire_cache_redis.rb +28 -0
  10. data/examples/cache_bypass.rb +48 -0
  11. data/gemfiles/Gemfile.mongoid-2.4.x +16 -0
  12. data/lib/generators/geocoder/config/config_generator.rb +14 -0
  13. data/lib/generators/geocoder/config/templates/initializer.rb +21 -0
  14. data/lib/generators/geocoder/maxmind/geolite_city_generator.rb +28 -0
  15. data/lib/generators/geocoder/maxmind/geolite_country_generator.rb +28 -0
  16. data/lib/generators/geocoder/maxmind/templates/migration/geolite_city.rb +30 -0
  17. data/lib/generators/geocoder/maxmind/templates/migration/geolite_country.rb +17 -0
  18. data/lib/geocoder/cache.rb +90 -0
  19. data/lib/geocoder/calculations.rb +428 -0
  20. data/lib/geocoder/cli.rb +121 -0
  21. data/lib/geocoder/configuration.rb +124 -0
  22. data/lib/geocoder/configuration_hash.rb +11 -0
  23. data/lib/geocoder/exceptions.rb +21 -0
  24. data/lib/geocoder/ip_address.rb +21 -0
  25. data/lib/geocoder/lookup.rb +100 -0
  26. data/lib/geocoder/lookups/baidu.rb +55 -0
  27. data/lib/geocoder/lookups/baidu_ip.rb +54 -0
  28. data/lib/geocoder/lookups/base.rb +302 -0
  29. data/lib/geocoder/lookups/bing.rb +59 -0
  30. data/lib/geocoder/lookups/dstk.rb +20 -0
  31. data/lib/geocoder/lookups/esri.rb +48 -0
  32. data/lib/geocoder/lookups/freegeoip.rb +47 -0
  33. data/lib/geocoder/lookups/geocoder_ca.rb +54 -0
  34. data/lib/geocoder/lookups/geocoder_us.rb +39 -0
  35. data/lib/geocoder/lookups/geocodio.rb +42 -0
  36. data/lib/geocoder/lookups/google.rb +67 -0
  37. data/lib/geocoder/lookups/google_places_details.rb +50 -0
  38. data/lib/geocoder/lookups/google_premier.rb +47 -0
  39. data/lib/geocoder/lookups/here.rb +62 -0
  40. data/lib/geocoder/lookups/ip_address_labs.rb +43 -0
  41. data/lib/geocoder/lookups/mapquest.rb +60 -0
  42. data/lib/geocoder/lookups/maxmind.rb +90 -0
  43. data/lib/geocoder/lookups/maxmind_local.rb +58 -0
  44. data/lib/geocoder/lookups/nominatim.rb +52 -0
  45. data/lib/geocoder/lookups/okf.rb +43 -0
  46. data/lib/geocoder/lookups/opencagedata.rb +58 -0
  47. data/lib/geocoder/lookups/ovi.rb +62 -0
  48. data/lib/geocoder/lookups/pointpin.rb +68 -0
  49. data/lib/geocoder/lookups/smarty_streets.rb +45 -0
  50. data/lib/geocoder/lookups/telize.rb +40 -0
  51. data/lib/geocoder/lookups/test.rb +44 -0
  52. data/lib/geocoder/lookups/yahoo.rb +88 -0
  53. data/lib/geocoder/lookups/yandex.rb +54 -0
  54. data/lib/geocoder/models/active_record.rb +50 -0
  55. data/lib/geocoder/models/base.rb +39 -0
  56. data/lib/geocoder/models/mongo_base.rb +64 -0
  57. data/lib/geocoder/models/mongo_mapper.rb +26 -0
  58. data/lib/geocoder/models/mongoid.rb +32 -0
  59. data/lib/geocoder/query.rb +111 -0
  60. data/lib/geocoder/railtie.rb +26 -0
  61. data/lib/geocoder/request.rb +25 -0
  62. data/lib/geocoder/results/baidu.rb +79 -0
  63. data/lib/geocoder/results/baidu_ip.rb +62 -0
  64. data/lib/geocoder/results/base.rb +67 -0
  65. data/lib/geocoder/results/bing.rb +48 -0
  66. data/lib/geocoder/results/dstk.rb +6 -0
  67. data/lib/geocoder/results/esri.rb +51 -0
  68. data/lib/geocoder/results/freegeoip.rb +45 -0
  69. data/lib/geocoder/results/geocoder_ca.rb +60 -0
  70. data/lib/geocoder/results/geocoder_us.rb +39 -0
  71. data/lib/geocoder/results/geocodio.rb +66 -0
  72. data/lib/geocoder/results/google.rb +124 -0
  73. data/lib/geocoder/results/google_places_details.rb +35 -0
  74. data/lib/geocoder/results/google_premier.rb +6 -0
  75. data/lib/geocoder/results/here.rb +62 -0
  76. data/lib/geocoder/results/ip_address_labs.rb +78 -0
  77. data/lib/geocoder/results/mapquest.rb +51 -0
  78. data/lib/geocoder/results/maxmind.rb +135 -0
  79. data/lib/geocoder/results/maxmind_local.rb +49 -0
  80. data/lib/geocoder/results/nominatim.rb +94 -0
  81. data/lib/geocoder/results/okf.rb +106 -0
  82. data/lib/geocoder/results/opencagedata.rb +82 -0
  83. data/lib/geocoder/results/ovi.rb +62 -0
  84. data/lib/geocoder/results/pointpin.rb +44 -0
  85. data/lib/geocoder/results/smarty_streets.rb +106 -0
  86. data/lib/geocoder/results/telize.rb +45 -0
  87. data/lib/geocoder/results/test.rb +33 -0
  88. data/lib/geocoder/results/yahoo.rb +55 -0
  89. data/lib/geocoder/results/yandex.rb +84 -0
  90. data/lib/geocoder/sql.rb +107 -0
  91. data/lib/geocoder/stores/active_record.rb +278 -0
  92. data/lib/geocoder/stores/base.rb +127 -0
  93. data/lib/geocoder/stores/mongo_base.rb +89 -0
  94. data/lib/geocoder/stores/mongo_mapper.rb +13 -0
  95. data/lib/geocoder/stores/mongoid.rb +13 -0
  96. data/lib/geocoder/version.rb +3 -0
  97. data/lib/geocoder.rb +47 -0
  98. data/lib/hash_recursive_merge.rb +74 -0
  99. data/lib/maxmind_database.rb +109 -0
  100. data/lib/oauth_util.rb +112 -0
  101. data/lib/tasks/geocoder.rake +29 -0
  102. data/lib/tasks/maxmind.rake +73 -0
  103. data/test/fixtures/baidu_invalid_key +1 -0
  104. data/test/fixtures/baidu_ip_202_198_16_3 +19 -0
  105. data/test/fixtures/baidu_ip_invalid_key +1 -0
  106. data/test/fixtures/baidu_ip_no_results +1 -0
  107. data/test/fixtures/baidu_no_results +1 -0
  108. data/test/fixtures/baidu_reverse +1 -0
  109. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  110. data/test/fixtures/bing_invalid_key +1 -0
  111. data/test/fixtures/bing_madison_square_garden +40 -0
  112. data/test/fixtures/bing_no_results +16 -0
  113. data/test/fixtures/bing_reverse +42 -0
  114. data/test/fixtures/cloudmade_invalid_key +1 -0
  115. data/test/fixtures/cloudmade_madison_square_garden +1 -0
  116. data/test/fixtures/cloudmade_no_results +1 -0
  117. data/test/fixtures/esri_madison_square_garden +59 -0
  118. data/test/fixtures/esri_no_results +8 -0
  119. data/test/fixtures/esri_reverse +21 -0
  120. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  121. data/test/fixtures/freegeoip_no_results +1 -0
  122. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  123. data/test/fixtures/geocoder_ca_no_results +1 -0
  124. data/test/fixtures/geocoder_ca_reverse +34 -0
  125. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  126. data/test/fixtures/geocoder_us_no_results +1 -0
  127. data/test/fixtures/geocodio_1101_pennsylvania_ave +1 -0
  128. data/test/fixtures/geocodio_bad_api_key +3 -0
  129. data/test/fixtures/geocodio_invalid +4 -0
  130. data/test/fixtures/geocodio_no_results +1 -0
  131. data/test/fixtures/geocodio_over_query_limit +4 -0
  132. data/test/fixtures/google_garbage +456 -0
  133. data/test/fixtures/google_madison_square_garden +57 -0
  134. data/test/fixtures/google_no_city_data +44 -0
  135. data/test/fixtures/google_no_locality +51 -0
  136. data/test/fixtures/google_no_results +4 -0
  137. data/test/fixtures/google_over_limit +4 -0
  138. data/test/fixtures/google_places_details_invalid_request +4 -0
  139. data/test/fixtures/google_places_details_madison_square_garden +120 -0
  140. data/test/fixtures/google_places_details_no_results +4 -0
  141. data/test/fixtures/google_places_details_no_reviews +60 -0
  142. data/test/fixtures/google_places_details_no_types +66 -0
  143. data/test/fixtures/here_madison_square_garden +72 -0
  144. data/test/fixtures/here_no_results +8 -0
  145. data/test/fixtures/mapquest_error +16 -0
  146. data/test/fixtures/mapquest_invalid_api_key +16 -0
  147. data/test/fixtures/mapquest_invalid_request +16 -0
  148. data/test/fixtures/mapquest_madison_square_garden +52 -0
  149. data/test/fixtures/mapquest_no_results +16 -0
  150. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  151. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  152. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  153. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  154. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  155. data/test/fixtures/maxmind_invalid_key +1 -0
  156. data/test/fixtures/maxmind_no_results +1 -0
  157. data/test/fixtures/nominatim_madison_square_garden +150 -0
  158. data/test/fixtures/nominatim_no_results +1 -0
  159. data/test/fixtures/nominatim_over_limit +1 -0
  160. data/test/fixtures/okf_kirstinmaki +67 -0
  161. data/test/fixtures/okf_no_results +4 -0
  162. data/test/fixtures/opencagedata_invalid_api_key +25 -0
  163. data/test/fixtures/opencagedata_invalid_request +26 -0
  164. data/test/fixtures/opencagedata_madison_square_garden +73 -0
  165. data/test/fixtures/opencagedata_no_results +29 -0
  166. data/test/fixtures/opencagedata_over_limit +31 -0
  167. data/test/fixtures/ovi_madison_square_garden +72 -0
  168. data/test/fixtures/ovi_no_results +8 -0
  169. data/test/fixtures/pointpin_10_10_10_10 +1 -0
  170. data/test/fixtures/pointpin_555_555_555_555 +1 -0
  171. data/test/fixtures/pointpin_80_111_555_555 +1 -0
  172. data/test/fixtures/pointpin_no_results +1 -0
  173. data/test/fixtures/smarty_streets_11211 +1 -0
  174. data/test/fixtures/smarty_streets_madison_square_garden +47 -0
  175. data/test/fixtures/smarty_streets_no_results +1 -0
  176. data/test/fixtures/telize_10_10_10_10 +1 -0
  177. data/test/fixtures/telize_555_555_555_555 +4 -0
  178. data/test/fixtures/telize_74_200_247_59 +1 -0
  179. data/test/fixtures/telize_no_results +1 -0
  180. data/test/fixtures/yahoo_error +1 -0
  181. data/test/fixtures/yahoo_invalid_key +2 -0
  182. data/test/fixtures/yahoo_madison_square_garden +52 -0
  183. data/test/fixtures/yahoo_no_results +10 -0
  184. data/test/fixtures/yahoo_over_limit +2 -0
  185. data/test/fixtures/yandex_canada_rue_dupuis_14 +446 -0
  186. data/test/fixtures/yandex_invalid_key +1 -0
  187. data/test/fixtures/yandex_kremlin +48 -0
  188. data/test/fixtures/yandex_new_york +1 -0
  189. data/test/fixtures/yandex_no_city_and_town +112 -0
  190. data/test/fixtures/yandex_no_results +16 -0
  191. data/test/integration/http_client_test.rb +31 -0
  192. data/test/mongoid_test_helper.rb +43 -0
  193. data/test/test_helper.rb +386 -0
  194. data/test/unit/active_record_test.rb +16 -0
  195. data/test/unit/cache_test.rb +37 -0
  196. data/test/unit/calculations_test.rb +220 -0
  197. data/test/unit/configuration_test.rb +55 -0
  198. data/test/unit/error_handling_test.rb +56 -0
  199. data/test/unit/geocoder_test.rb +78 -0
  200. data/test/unit/https_test.rb +17 -0
  201. data/test/unit/ip_address_test.rb +27 -0
  202. data/test/unit/lookup_test.rb +153 -0
  203. data/test/unit/lookups/bing_test.rb +68 -0
  204. data/test/unit/lookups/dstk_test.rb +26 -0
  205. data/test/unit/lookups/esri_test.rb +48 -0
  206. data/test/unit/lookups/freegeoip_test.rb +27 -0
  207. data/test/unit/lookups/geocoder_ca_test.rb +17 -0
  208. data/test/unit/lookups/geocodio_test.rb +55 -0
  209. data/test/unit/lookups/google_places_details_test.rb +122 -0
  210. data/test/unit/lookups/google_premier_test.rb +22 -0
  211. data/test/unit/lookups/google_test.rb +84 -0
  212. data/test/unit/lookups/mapquest_test.rb +60 -0
  213. data/test/unit/lookups/maxmind_local_test.rb +28 -0
  214. data/test/unit/lookups/maxmind_test.rb +63 -0
  215. data/test/unit/lookups/nominatim_test.rb +31 -0
  216. data/test/unit/lookups/okf_test.rb +38 -0
  217. data/test/unit/lookups/opencagedata_test.rb +64 -0
  218. data/test/unit/lookups/pointpin_test.rb +30 -0
  219. data/test/unit/lookups/smarty_streets_test.rb +71 -0
  220. data/test/unit/lookups/telize_test.rb +36 -0
  221. data/test/unit/lookups/yahoo_test.rb +35 -0
  222. data/test/unit/method_aliases_test.rb +26 -0
  223. data/test/unit/model_test.rb +38 -0
  224. data/test/unit/mongoid_test.rb +47 -0
  225. data/test/unit/near_test.rb +87 -0
  226. data/test/unit/oauth_util_test.rb +31 -0
  227. data/test/unit/proxy_test.rb +37 -0
  228. data/test/unit/query_test.rb +52 -0
  229. data/test/unit/rake_task_test.rb +21 -0
  230. data/test/unit/request_test.rb +35 -0
  231. data/test/unit/result_test.rb +72 -0
  232. data/test/unit/test_mode_test.rb +70 -0
  233. metadata +281 -0
@@ -0,0 +1,43 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/okf"
3
+
4
+ module Geocoder::Lookup
5
+ class Okf < Base
6
+
7
+ def name
8
+ "Okf"
9
+ end
10
+
11
+ def query_url(query)
12
+ "#{protocol}://data.okf.fi/gis/1/geocode/json?" + url_query_string(query)
13
+ end
14
+
15
+ private # ---------------------------------------------------------------
16
+
17
+ def valid_response?(response)
18
+ status = parse_json(response.body)["status"]
19
+ super(response) and ['OK', 'ZERO_RESULTS'].include?(status)
20
+ end
21
+
22
+ def results(query)
23
+ return [] unless doc = fetch_data(query)
24
+ case doc['status']; when "OK" # OK status implies >0 results
25
+ return doc['results']
26
+ end
27
+ return []
28
+ end
29
+
30
+ def query_url_okf_params(query)
31
+ params = {
32
+ (query.reverse_geocode? ? :latlng : :address) => query.sanitized_text,
33
+ :sensor => "false",
34
+ :language => (query.language || configuration.language)
35
+ }
36
+ params
37
+ end
38
+
39
+ def query_url_params(query)
40
+ query_url_okf_params(query).merge(super)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,58 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/opencagedata'
3
+
4
+ module Geocoder::Lookup
5
+ class Opencagedata < Base
6
+
7
+ def name
8
+ "OpenCageData"
9
+ end
10
+
11
+ def query_url(query)
12
+ "#{protocol}://api.opencagedata.com/geocode/v1/json?key=#{configuration.api_key}&#{url_query_string(query)}"
13
+ end
14
+
15
+ def required_api_key_parts
16
+ ["key"]
17
+ end
18
+
19
+ private
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ # return doc["results"]
24
+
25
+ messages = doc['status']['message']
26
+ case doc['status']['code']
27
+ when 400 # Error with input
28
+ raise_error(Geocoder::InvalidRequest, messages) ||
29
+ warn("Opencagedata Geocoding API error: #{messages}")
30
+ when 403 # Key related error
31
+ raise_error(Geocoder::InvalidApiKey, messages) ||
32
+ warn("Opencagedata Geocoding API error: #{messages}")
33
+ when 402 # Quata Exceeded
34
+ raise_error(Geocoder::OverQueryLimitError, messages) ||
35
+ warn("Opencagedata Geocoding API error: #{messages}")
36
+ when 500 # Unknown error
37
+ raise_error(Geocoder::Error, messages) ||
38
+ warn("Opencagedata Geocoding API error: #{messages}")
39
+ end
40
+
41
+ return doc["results"]
42
+ end
43
+
44
+ def query_url_params(query)
45
+ params = {
46
+ :q => query.sanitized_text,
47
+ :language => (query.language || configuration.language)
48
+ }.merge(super)
49
+
50
+ unless (bounds = query.options[:bounds]).nil?
51
+ params[:bounds] = bounds.map{ |point| "%f,%f" % point }.join(',')
52
+ end
53
+
54
+ params
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,62 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/ovi'
3
+
4
+ module Geocoder::Lookup
5
+ class Ovi < Base
6
+
7
+ def name
8
+ "Ovi"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ []
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{protocol}://lbs.ovi.com/search/6.2/#{if query.reverse_geocode? then 'reverse' end}geocode.json?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ return [] unless doc['Response'] && doc['Response']['View']
24
+ if r=doc['Response']['View']
25
+ return [] if r.nil? || !r.is_a?(Array) || r.empty?
26
+ return r.first['Result']
27
+ end
28
+ []
29
+ end
30
+
31
+ def query_url_params(query)
32
+ options = {
33
+ :gen=>1,
34
+ :app_id=>api_key,
35
+ :app_code=>api_code
36
+ }
37
+
38
+ if query.reverse_geocode?
39
+ super.merge(options).merge(
40
+ :prox=>query.sanitized_text,
41
+ :mode=>:retrieveAddresses
42
+ )
43
+ else
44
+ super.merge(options).merge(
45
+ :searchtext=>query.sanitized_text
46
+ )
47
+ end
48
+ end
49
+
50
+ def api_key
51
+ if a=configuration.api_key
52
+ return a.first if a.is_a?(Array)
53
+ end
54
+ end
55
+
56
+ def api_code
57
+ if a=configuration.api_key
58
+ return a.last if a.is_a?(Array)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,68 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/pointpin'
3
+
4
+ module Geocoder::Lookup
5
+ class Pointpin < Base
6
+
7
+ def name
8
+ "Pointpin"
9
+ end
10
+
11
+ def required_api_key_parts
12
+ ["key"]
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{ protocol }://geo.pointp.in/#{ api_key }/json/#{ query.sanitized_text }"
17
+ end
18
+
19
+ private
20
+
21
+ def results(query)
22
+ # don't look up a loopback address, just return the stored result
23
+ return [] if query.loopback_ip_address?
24
+ doc = fetch_data(query)
25
+ if doc and doc.is_a?(Hash)
26
+ if !data_contains_error?(doc)
27
+ return [doc]
28
+ elsif doc['error']
29
+ case doc['error']
30
+ when "Invalid IP address"
31
+ raise_error(Geocoder::InvalidRequest) || warn("Invalid Pointpin request.")
32
+ when "Invalid API key"
33
+ raise_error(Geocoder::InvalidApiKey) || warn("Invalid Pointpin API key.")
34
+ when "Address not found"
35
+ warn("Address not found.")
36
+ end
37
+ else
38
+ raise_error(Geocoder::Error) || warn("Pointpin server error")
39
+ end
40
+ end
41
+
42
+ return []
43
+ end
44
+
45
+ def data_contains_error?(parsed_data)
46
+ parsed_data.keys.include?('error')
47
+ end
48
+
49
+ def reserved_result(ip)
50
+ {
51
+ "ip" => ip,
52
+ "city" => "",
53
+ "region_code" => "",
54
+ "region_name" => "",
55
+ "metrocode" => "",
56
+ "zipcode" => "",
57
+ "latitude" => "0",
58
+ "longitude" => "0",
59
+ "country_name" => "Reserved",
60
+ "country_code" => "RD"
61
+ }
62
+ end
63
+
64
+ def api_key
65
+ configuration.api_key
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,45 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/smarty_streets'
3
+
4
+ module Geocoder::Lookup
5
+ class SmartyStreets < Base
6
+ def name
7
+ "SmartyStreets"
8
+ end
9
+
10
+ def required_api_key_parts
11
+ %w(auti-id auth-token)
12
+ end
13
+
14
+ def query_url(query)
15
+ path = zipcode_only?(query) ? "zipcode" : "street-address"
16
+ "#{protocol}://api.smartystreets.com/#{path}?#{url_query_string(query)}"
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def zipcode_only?(query)
22
+ !query.text.is_a?(Array) and query.to_s.strip =~ /\A\d{5}(-\d{4})?\Z/
23
+ end
24
+
25
+ def query_url_params(query)
26
+ params = {}
27
+ if zipcode_only?(query)
28
+ params[:zipcode] = query.sanitized_text
29
+ else
30
+ params[:street] = query.sanitized_text
31
+ end
32
+ if configuration.api_key.is_a?(Array)
33
+ params[:"auth-id"] = configuration.api_key[0]
34
+ params[:"auth-token"] = configuration.api_key[1]
35
+ else
36
+ params[:"auth-token"] = configuration.api_key
37
+ end
38
+ params.merge(super)
39
+ end
40
+
41
+ def results(query)
42
+ fetch_data(query) || []
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/telize'
3
+
4
+ module Geocoder::Lookup
5
+ class Telize < Base
6
+
7
+ def name
8
+ "Telize"
9
+ end
10
+
11
+ def query_url(query)
12
+ #currently doesn't support HTTPS
13
+ "http://www.telize.com/geoip/#{query.sanitized_text}"
14
+ end
15
+
16
+ private # ---------------------------------------------------------------
17
+
18
+ def use_ssl?
19
+ false
20
+ end
21
+
22
+ def results(query)
23
+ # don't look up a loopback address, just return the stored result
24
+ return [reserved_result(query.text)] if query.loopback_ip_address?
25
+ if (doc = fetch_data(query)).nil? or doc['code'] == 401 or empty_result?(doc)
26
+ []
27
+ else
28
+ [doc]
29
+ end
30
+ end
31
+
32
+ def empty_result?(doc)
33
+ !doc.is_a?(Hash) or doc.keys == ["ip"]
34
+ end
35
+
36
+ def reserved_result(ip)
37
+ {"message" => "Input string is not a valid IP address", "code" => 401}
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ require 'geocoder/lookups/base'
2
+ require 'geocoder/results/test'
3
+
4
+ module Geocoder
5
+ module Lookup
6
+ class Test < Base
7
+
8
+ def name
9
+ "Test"
10
+ end
11
+
12
+ def self.add_stub(query_text, results)
13
+ stubs[query_text] = results
14
+ end
15
+
16
+ def self.set_default_stub(results)
17
+ @default_stub = results
18
+ end
19
+
20
+ def self.read_stub(query_text)
21
+ stubs.fetch(query_text) {
22
+ return @default_stub unless @default_stub.nil?
23
+ raise ArgumentError, "unknown stub request #{query_text}"
24
+ }
25
+ end
26
+
27
+ def self.stubs
28
+ @stubs ||= {}
29
+ end
30
+
31
+ def self.reset
32
+ @stubs = {}
33
+ @default_stub = nil
34
+ end
35
+
36
+ private
37
+
38
+ def results(query)
39
+ Geocoder::Lookup::Test.read_stub(query.text)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,88 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/yahoo"
3
+ require 'oauth_util'
4
+
5
+ module Geocoder::Lookup
6
+ class Yahoo < Base
7
+
8
+ def name
9
+ "Yahoo BOSS"
10
+ end
11
+
12
+ def map_link_url(coordinates)
13
+ "http://maps.yahoo.com/#lat=#{coordinates[0]}&lon=#{coordinates[1]}"
14
+ end
15
+
16
+ def required_api_key_parts
17
+ ["consumer key", "consumer secret"]
18
+ end
19
+
20
+ def query_url(query)
21
+ parsed_url = URI.parse(raw_url(query))
22
+ o = OauthUtil.new
23
+ o.consumer_key = configuration.api_key[0]
24
+ o.consumer_secret = configuration.api_key[1]
25
+ base_url + o.sign(parsed_url).query_string
26
+ end
27
+
28
+ private # ---------------------------------------------------------------
29
+
30
+ def results(query)
31
+ return [] unless doc = fetch_data(query)
32
+ doc = doc['bossresponse']
33
+ if doc['responsecode'].to_i == 200
34
+ if doc['placefinder']['count'].to_i > 0
35
+ return doc['placefinder']['results']
36
+ else
37
+ return []
38
+ end
39
+ else
40
+ warn "Yahoo Geocoding API error: #{doc['responsecode']} (#{doc['reason']})."
41
+ return []
42
+ end
43
+ end
44
+
45
+ ##
46
+ # Yahoo returns errors as XML even when JSON format is specified.
47
+ # Handle that here, without parsing the XML
48
+ # (which would add unnecessary complexity).
49
+ # Yahoo auth errors can also be cryptic, so add raw error desc
50
+ # to warning message.
51
+ #
52
+ def parse_raw_data(raw_data)
53
+ if raw_data.match(/^<\?xml/)
54
+ if raw_data.include?("Rate Limit Exceeded")
55
+ raise_error(Geocoder::OverQueryLimitError) || warn("Over API query limit.")
56
+ elsif raw_data =~ /<yahoo:description>(Please provide valid credentials.*)<\/yahoo:description>/i
57
+ raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key. Error response: #{$1}")
58
+ end
59
+ else
60
+ super(raw_data)
61
+ end
62
+ end
63
+
64
+ def query_url_params(query)
65
+ lang = (query.language || configuration.language).to_s
66
+ lang += '_US' if lang == 'en'
67
+ {
68
+ :location => query.sanitized_text,
69
+ :flags => "JXTSR",
70
+ :gflags => "AC#{'R' if query.reverse_geocode?}",
71
+ :locale => lang,
72
+ :appid => configuration.api_key
73
+ }.merge(super)
74
+ end
75
+
76
+ def cache_key(query)
77
+ raw_url(query)
78
+ end
79
+
80
+ def base_url
81
+ "#{protocol}://yboss.yahooapis.com/geo/placefinder?"
82
+ end
83
+
84
+ def raw_url(query)
85
+ base_url + url_query_string(query)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,54 @@
1
+ require 'geocoder/lookups/base'
2
+ require "geocoder/results/yandex"
3
+
4
+ module Geocoder::Lookup
5
+ class Yandex < Base
6
+
7
+ def name
8
+ "Yandex"
9
+ end
10
+
11
+ def map_link_url(coordinates)
12
+ "http://maps.yandex.ru/?ll=#{coordinates.reverse.join(',')}"
13
+ end
14
+
15
+ def query_url(query)
16
+ "#{protocol}://geocode-maps.yandex.ru/1.x/?" + url_query_string(query)
17
+ end
18
+
19
+ private # ---------------------------------------------------------------
20
+
21
+ def results(query)
22
+ return [] unless doc = fetch_data(query)
23
+ if err = doc['error']
24
+ if err["status"] == 401 and err["message"] == "invalid key"
25
+ raise_error(Geocoder::InvalidApiKey) || warn("Invalid API key.")
26
+ else
27
+ warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
28
+ end
29
+ return []
30
+ end
31
+ if doc = doc['response']['GeoObjectCollection']
32
+ meta = doc['metaDataProperty']['GeocoderResponseMetaData']
33
+ return meta['found'].to_i > 0 ? doc['featureMember'] : []
34
+ else
35
+ warn "Yandex Geocoding API error: unexpected response format."
36
+ return []
37
+ end
38
+ end
39
+
40
+ def query_url_params(query)
41
+ if query.reverse_geocode?
42
+ q = query.coordinates.reverse.join(",")
43
+ else
44
+ q = query.sanitized_text
45
+ end
46
+ {
47
+ :geocode => q,
48
+ :format => "json",
49
+ :plng => "#{query.language || configuration.language}", # supports ru, uk, be
50
+ :key => configuration.api_key
51
+ }.merge(super)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ require 'geocoder/models/base'
2
+
3
+ module Geocoder
4
+ module Model
5
+ module ActiveRecord
6
+ include Base
7
+
8
+ ##
9
+ # Set attribute names and include the Geocoder module.
10
+ #
11
+ def geocoded_by(address_attr, options = {}, &block)
12
+ geocoder_init(
13
+ :geocode => true,
14
+ :user_address => address_attr,
15
+ :latitude => options[:latitude] || :latitude,
16
+ :longitude => options[:longitude] || :longitude,
17
+ :geocode_block => block,
18
+ :units => options[:units],
19
+ :method => options[:method],
20
+ :lookup => options[:lookup],
21
+ :language => options[:language]
22
+ )
23
+ end
24
+
25
+ ##
26
+ # Set attribute names and include the Geocoder module.
27
+ #
28
+ def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block)
29
+ geocoder_init(
30
+ :reverse_geocode => true,
31
+ :fetched_address => options[:address] || :address,
32
+ :latitude => latitude_attr,
33
+ :longitude => longitude_attr,
34
+ :reverse_block => block,
35
+ :units => options[:units],
36
+ :method => options[:method],
37
+ :lookup => options[:lookup],
38
+ :language => options[:language]
39
+ )
40
+ end
41
+
42
+
43
+ private # --------------------------------------------------------------
44
+
45
+ def geocoder_file_name; "active_record"; end
46
+ def geocoder_module_name; "ActiveRecord"; end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,39 @@
1
+ module Geocoder
2
+
3
+ ##
4
+ # Methods for invoking Geocoder in a model.
5
+ #
6
+ module Model
7
+ module Base
8
+
9
+ def geocoder_options
10
+ if defined?(@geocoder_options)
11
+ @geocoder_options
12
+ elsif superclass.respond_to?(:geocoder_options)
13
+ superclass.geocoder_options || { }
14
+ else
15
+ { }
16
+ end
17
+ end
18
+
19
+ def geocoded_by
20
+ fail
21
+ end
22
+
23
+ def reverse_geocoded_by
24
+ fail
25
+ end
26
+
27
+ private # ----------------------------------------------------------------
28
+
29
+ def geocoder_init(options)
30
+ unless defined?(@geocoder_options)
31
+ @geocoder_options = {}
32
+ require "geocoder/stores/#{geocoder_file_name}"
33
+ include Geocoder::Store.const_get(geocoder_module_name)
34
+ end
35
+ @geocoder_options.merge! options
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,64 @@
1
+ require 'geocoder'
2
+
3
+ module Geocoder
4
+
5
+ ##
6
+ # Methods for invoking Geocoder in a model.
7
+ #
8
+ module Model
9
+ module MongoBase
10
+
11
+ ##
12
+ # Set attribute names and include the Geocoder module.
13
+ #
14
+ def geocoded_by(address_attr, options = {}, &block)
15
+ geocoder_init(
16
+ :geocode => true,
17
+ :user_address => address_attr,
18
+ :coordinates => options[:coordinates] || :coordinates,
19
+ :geocode_block => block,
20
+ :units => options[:units],
21
+ :method => options[:method],
22
+ :skip_index => options[:skip_index] || false,
23
+ :lookup => options[:lookup],
24
+ :language => options[:language]
25
+ )
26
+ end
27
+
28
+ ##
29
+ # Set attribute names and include the Geocoder module.
30
+ #
31
+ def reverse_geocoded_by(coordinates_attr, options = {}, &block)
32
+ geocoder_init(
33
+ :reverse_geocode => true,
34
+ :fetched_address => options[:address] || :address,
35
+ :coordinates => coordinates_attr,
36
+ :reverse_block => block,
37
+ :units => options[:units],
38
+ :method => options[:method],
39
+ :skip_index => options[:skip_index] || false,
40
+ :lookup => options[:lookup],
41
+ :language => options[:language]
42
+ )
43
+ end
44
+
45
+ private # ----------------------------------------------------------------
46
+
47
+ def geocoder_init(options)
48
+ unless geocoder_initialized?
49
+ @geocoder_options = { }
50
+ require "geocoder/stores/#{geocoder_file_name}"
51
+ include Geocoder::Store.const_get(geocoder_module_name)
52
+ end
53
+ @geocoder_options.merge! options
54
+ end
55
+
56
+ def geocoder_initialized?
57
+ included_modules.include? Geocoder::Store.const_get(geocoder_module_name)
58
+ rescue NameError
59
+ false
60
+ end
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,26 @@
1
+ require 'geocoder/models/base'
2
+ require 'geocoder/models/mongo_base'
3
+
4
+ module Geocoder
5
+ module Model
6
+ module MongoMapper
7
+ include Base
8
+ include MongoBase
9
+
10
+ def self.included(base); base.extend(self); end
11
+
12
+ private # --------------------------------------------------------------
13
+
14
+ def geocoder_file_name; "mongo_mapper"; end
15
+ def geocoder_module_name; "MongoMapper"; end
16
+
17
+ def geocoder_init(options)
18
+ super(options)
19
+ if options[:skip_index] == false
20
+ ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
21
+ :min => -180, :max => 180 # create 2d index
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end