geocoder2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/.gitignore +5 -0
  2. data/.travis.yml +27 -0
  3. data/CHANGELOG.md +329 -0
  4. data/LICENSE +20 -0
  5. data/README.md +796 -0
  6. data/Rakefile +25 -0
  7. data/bin/geocode2 +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 +15 -0
  12. data/json?address=26+leonard+street%2C+Belmont&key=AIzaSyDoltU6YL8XeIQrSLFGk6ZfpKaWkPukwYQ&language=en +68 -0
  13. data/lib/generators/geocoder2/config/config_generator.rb +14 -0
  14. data/lib/generators/geocoder2/config/templates/initializer.rb +21 -0
  15. data/lib/geocoder2/cache.rb +89 -0
  16. data/lib/geocoder2/calculations.rb +389 -0
  17. data/lib/geocoder2/cli.rb +121 -0
  18. data/lib/geocoder2/configuration.rb +130 -0
  19. data/lib/geocoder2/configuration_hash.rb +11 -0
  20. data/lib/geocoder2/exceptions.rb +21 -0
  21. data/lib/geocoder2/lookup.rb +86 -0
  22. data/lib/geocoder2/lookups/baidu.rb +54 -0
  23. data/lib/geocoder2/lookups/base.rb +266 -0
  24. data/lib/geocoder2/lookups/bing.rb +47 -0
  25. data/lib/geocoder2/lookups/dstk.rb +20 -0
  26. data/lib/geocoder2/lookups/esri.rb +48 -0
  27. data/lib/geocoder2/lookups/freegeoip.rb +43 -0
  28. data/lib/geocoder2/lookups/geocoder_ca.rb +54 -0
  29. data/lib/geocoder2/lookups/geocoder_us.rb +39 -0
  30. data/lib/geocoder2/lookups/google.rb +69 -0
  31. data/lib/geocoder2/lookups/google_premier.rb +47 -0
  32. data/lib/geocoder2/lookups/mapquest.rb +59 -0
  33. data/lib/geocoder2/lookups/maxmind.rb +88 -0
  34. data/lib/geocoder2/lookups/nominatim.rb +44 -0
  35. data/lib/geocoder2/lookups/ovi.rb +62 -0
  36. data/lib/geocoder2/lookups/test.rb +44 -0
  37. data/lib/geocoder2/lookups/yahoo.rb +86 -0
  38. data/lib/geocoder2/lookups/yandex.rb +54 -0
  39. data/lib/geocoder2/models/active_record.rb +46 -0
  40. data/lib/geocoder2/models/base.rb +42 -0
  41. data/lib/geocoder2/models/mongo_base.rb +60 -0
  42. data/lib/geocoder2/models/mongo_mapper.rb +26 -0
  43. data/lib/geocoder2/models/mongoid.rb +32 -0
  44. data/lib/geocoder2/query.rb +107 -0
  45. data/lib/geocoder2/railtie.rb +26 -0
  46. data/lib/geocoder2/request.rb +23 -0
  47. data/lib/geocoder2/results/baidu.rb +79 -0
  48. data/lib/geocoder2/results/base.rb +67 -0
  49. data/lib/geocoder2/results/bing.rb +48 -0
  50. data/lib/geocoder2/results/dstk.rb +6 -0
  51. data/lib/geocoder2/results/esri.rb +51 -0
  52. data/lib/geocoder2/results/freegeoip.rb +45 -0
  53. data/lib/geocoder2/results/geocoder_ca.rb +60 -0
  54. data/lib/geocoder2/results/geocoder_us.rb +39 -0
  55. data/lib/geocoder2/results/google.rb +124 -0
  56. data/lib/geocoder2/results/google_premier.rb +6 -0
  57. data/lib/geocoder2/results/mapquest.rb +51 -0
  58. data/lib/geocoder2/results/maxmind.rb +135 -0
  59. data/lib/geocoder2/results/nominatim.rb +94 -0
  60. data/lib/geocoder2/results/ovi.rb +62 -0
  61. data/lib/geocoder2/results/test.rb +16 -0
  62. data/lib/geocoder2/results/yahoo.rb +55 -0
  63. data/lib/geocoder2/results/yandex.rb +80 -0
  64. data/lib/geocoder2/sql.rb +106 -0
  65. data/lib/geocoder2/stores/active_record.rb +272 -0
  66. data/lib/geocoder2/stores/base.rb +120 -0
  67. data/lib/geocoder2/stores/mongo_base.rb +89 -0
  68. data/lib/geocoder2/stores/mongo_mapper.rb +13 -0
  69. data/lib/geocoder2/stores/mongoid.rb +13 -0
  70. data/lib/geocoder2/version.rb +3 -0
  71. data/lib/geocoder2.rb +55 -0
  72. data/lib/hash_recursive_merge.rb +74 -0
  73. data/lib/oauth_util.rb +112 -0
  74. data/lib/tasks/geocoder2.rake +27 -0
  75. data/test/active_record_test.rb +15 -0
  76. data/test/cache_test.rb +35 -0
  77. data/test/calculations_test.rb +211 -0
  78. data/test/configuration_test.rb +78 -0
  79. data/test/custom_block_test.rb +32 -0
  80. data/test/error_handling_test.rb +43 -0
  81. data/test/fixtures/baidu_invalid_key +1 -0
  82. data/test/fixtures/baidu_no_results +1 -0
  83. data/test/fixtures/baidu_reverse +1 -0
  84. data/test/fixtures/baidu_shanghai_pearl_tower +12 -0
  85. data/test/fixtures/bing_invalid_key +1 -0
  86. data/test/fixtures/bing_madison_square_garden +40 -0
  87. data/test/fixtures/bing_no_results +16 -0
  88. data/test/fixtures/bing_reverse +42 -0
  89. data/test/fixtures/esri_madison_square_garden +59 -0
  90. data/test/fixtures/esri_no_results +8 -0
  91. data/test/fixtures/esri_reverse +21 -0
  92. data/test/fixtures/freegeoip_74_200_247_59 +12 -0
  93. data/test/fixtures/freegeoip_no_results +1 -0
  94. data/test/fixtures/geocoder_ca_madison_square_garden +1 -0
  95. data/test/fixtures/geocoder_ca_no_results +1 -0
  96. data/test/fixtures/geocoder_ca_reverse +34 -0
  97. data/test/fixtures/geocoder_us_madison_square_garden +1 -0
  98. data/test/fixtures/geocoder_us_no_results +1 -0
  99. data/test/fixtures/google_garbage +456 -0
  100. data/test/fixtures/google_madison_square_garden +57 -0
  101. data/test/fixtures/google_no_city_data +44 -0
  102. data/test/fixtures/google_no_locality +51 -0
  103. data/test/fixtures/google_no_results +4 -0
  104. data/test/fixtures/google_over_limit +4 -0
  105. data/test/fixtures/mapquest_error +16 -0
  106. data/test/fixtures/mapquest_invalid_api_key +16 -0
  107. data/test/fixtures/mapquest_invalid_request +16 -0
  108. data/test/fixtures/mapquest_madison_square_garden +52 -0
  109. data/test/fixtures/mapquest_no_results +16 -0
  110. data/test/fixtures/maxmind_24_24_24_21 +1 -0
  111. data/test/fixtures/maxmind_24_24_24_22 +1 -0
  112. data/test/fixtures/maxmind_24_24_24_23 +1 -0
  113. data/test/fixtures/maxmind_24_24_24_24 +1 -0
  114. data/test/fixtures/maxmind_74_200_247_59 +1 -0
  115. data/test/fixtures/maxmind_invalid_key +1 -0
  116. data/test/fixtures/maxmind_no_results +1 -0
  117. data/test/fixtures/nominatim_madison_square_garden +150 -0
  118. data/test/fixtures/nominatim_no_results +1 -0
  119. data/test/fixtures/ovi_madison_square_garden +72 -0
  120. data/test/fixtures/ovi_no_results +8 -0
  121. data/test/fixtures/yahoo_error +1 -0
  122. data/test/fixtures/yahoo_invalid_key +2 -0
  123. data/test/fixtures/yahoo_madison_square_garden +52 -0
  124. data/test/fixtures/yahoo_no_results +10 -0
  125. data/test/fixtures/yahoo_over_limit +2 -0
  126. data/test/fixtures/yandex_invalid_key +1 -0
  127. data/test/fixtures/yandex_kremlin +48 -0
  128. data/test/fixtures/yandex_no_city_and_town +112 -0
  129. data/test/fixtures/yandex_no_results +16 -0
  130. data/test/geocoder_test.rb +59 -0
  131. data/test/https_test.rb +16 -0
  132. data/test/integration/smoke_test.rb +26 -0
  133. data/test/lookup_test.rb +117 -0
  134. data/test/method_aliases_test.rb +25 -0
  135. data/test/mongoid_test.rb +46 -0
  136. data/test/mongoid_test_helper.rb +43 -0
  137. data/test/near_test.rb +61 -0
  138. data/test/oauth_util_test.rb +30 -0
  139. data/test/proxy_test.rb +36 -0
  140. data/test/query_test.rb +52 -0
  141. data/test/request_test.rb +29 -0
  142. data/test/result_test.rb +42 -0
  143. data/test/services_test.rb +393 -0
  144. data/test/test_helper.rb +289 -0
  145. data/test/test_mode_test.rb +59 -0
  146. metadata +213 -0
@@ -0,0 +1,74 @@
1
+ #
2
+ # = Hash Recursive Merge
3
+ #
4
+ # Merges a Ruby Hash recursively, Also known as deep merge.
5
+ # Recursive version of Hash#merge and Hash#merge!.
6
+ #
7
+ # Category:: Ruby
8
+ # Package:: Hash
9
+ # Author:: Simone Carletti <weppos@weppos.net>
10
+ # Copyright:: 2007-2008 The Authors
11
+ # License:: MIT License
12
+ # Link:: http://www.simonecarletti.com/
13
+ # Source:: http://gist.github.com/gists/6391/
14
+ #
15
+ module HashRecursiveMerge
16
+
17
+ #
18
+ # Recursive version of Hash#merge!
19
+ #
20
+ # Adds the contents of +other_hash+ to +hsh+,
21
+ # merging entries in +hsh+ with duplicate keys with those from +other_hash+.
22
+ #
23
+ # Compared with Hash#merge!, this method supports nested hashes.
24
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
25
+ # it merges and returns the values from both arrays.
26
+ #
27
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
28
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
29
+ # h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
30
+ #
31
+ # Simply using Hash#merge! would return
32
+ #
33
+ # h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
34
+ #
35
+ def rmerge!(other_hash)
36
+ merge!(other_hash) do |key, oldval, newval|
37
+ oldval.class == self.class ? oldval.rmerge!(newval) : newval
38
+ end
39
+ end
40
+
41
+ #
42
+ # Recursive version of Hash#merge
43
+ #
44
+ # Compared with Hash#merge!, this method supports nested hashes.
45
+ # When both +hsh+ and +other_hash+ contains an entry with the same key,
46
+ # it merges and returns the values from both arrays.
47
+ #
48
+ # Compared with Hash#merge, this method provides a different approch
49
+ # for merging nasted hashes.
50
+ # If the value of a given key is an Hash and both +other_hash+ abd +hsh
51
+ # includes the same key, the value is merged instead replaced with
52
+ # +other_hash+ value.
53
+ #
54
+ # h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
55
+ # h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
56
+ # h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
57
+ #
58
+ # Simply using Hash#merge would return
59
+ #
60
+ # h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
61
+ #
62
+ def rmerge(other_hash)
63
+ r = {}
64
+ merge(other_hash) do |key, oldval, newval|
65
+ r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+
72
+ class Hash
73
+ include HashRecursiveMerge
74
+ end
data/lib/oauth_util.rb ADDED
@@ -0,0 +1,112 @@
1
+ # A utility for signing an url using OAuth in a way that's convenient for debugging
2
+ # Note: the standard Ruby OAuth lib is here http://github.com/mojodna/oauth
3
+ # Source: http://gist.github.com/383159
4
+ # License: http://gist.github.com/375593
5
+ # Usage: see example.rb below
6
+ #
7
+ # NOTE: This file has been modified from the original Gist:
8
+ #
9
+ # 1. Fix to prevent param-array conversion, as mentioned in Gist comment.
10
+ # 2. Query string escaping has been changed. See:
11
+ # https://github.com/alexreisner/geocoder2/pull/360
12
+ #
13
+
14
+ require 'uri'
15
+ require 'cgi'
16
+ require 'openssl'
17
+ require 'base64'
18
+
19
+ class OauthUtil
20
+
21
+ attr_accessor :consumer_key, :consumer_secret, :token, :token_secret, :req_method,
22
+ :sig_method, :oauth_version, :callback_url, :params, :req_url, :base_str
23
+
24
+ def initialize
25
+ @consumer_key = ''
26
+ @consumer_secret = ''
27
+ @token = ''
28
+ @token_secret = ''
29
+ @req_method = 'GET'
30
+ @sig_method = 'HMAC-SHA1'
31
+ @oauth_version = '1.0'
32
+ @callback_url = ''
33
+ end
34
+
35
+ # openssl::random_bytes returns non-word chars, which need to be removed. using alt method to get length
36
+ # ref http://snippets.dzone.com/posts/show/491
37
+ def nonce
38
+ Array.new( 5 ) { rand(256) }.pack('C*').unpack('H*').first
39
+ end
40
+
41
+ def percent_encode( string )
42
+
43
+ # ref http://snippets.dzone.com/posts/show/1260
44
+ return URI.escape( string, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]") ).gsub('*', '%2A')
45
+ end
46
+
47
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.2
48
+ def signature
49
+ key = percent_encode( @consumer_secret ) + '&' + percent_encode( @token_secret )
50
+
51
+ # ref: http://blog.nathanielbibler.com/post/63031273/openssl-hmac-vs-ruby-hmac-benchmarks
52
+ digest = OpenSSL::Digest::Digest.new( 'sha1' )
53
+ hmac = OpenSSL::HMAC.digest( digest, key, @base_str )
54
+
55
+ # ref http://groups.google.com/group/oauth-ruby/browse_thread/thread/9110ed8c8f3cae81
56
+ Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
57
+ end
58
+
59
+ # sort (very important as it affects the signature), concat, and percent encode
60
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.1.1
61
+ # @ref http://oauth.net/core/1.0/#9.2.1
62
+ # @ref http://oauth.net/core/1.0/#rfc.section.A.5.1
63
+ def query_string
64
+ pairs = []
65
+ @params.sort.each { | key, val |
66
+ pairs.push( "#{ CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') } }=#{ CGI.escape(val.to_s) }" )
67
+ }
68
+ pairs.join '&'
69
+ end
70
+
71
+ # organize params & create signature
72
+ def sign( parsed_url )
73
+
74
+ @params = {
75
+ 'oauth_consumer_key' => @consumer_key,
76
+ 'oauth_nonce' => nonce,
77
+ 'oauth_signature_method' => @sig_method,
78
+ 'oauth_timestamp' => Time.now.to_i.to_s,
79
+ 'oauth_version' => @oauth_version
80
+ }
81
+
82
+ # if url has query, merge key/values into params obj overwriting defaults
83
+ if parsed_url.query
84
+ CGI.parse( parsed_url.query ).each do |k,v|
85
+ if v.is_a?(Array) && v.count == 1
86
+ @params[k] = v.first
87
+ else
88
+ @params[k] = v
89
+ end
90
+ end
91
+ end
92
+
93
+ # @ref http://oauth.net/core/1.0/#rfc.section.9.1.2
94
+ @req_url = parsed_url.scheme + '://' + parsed_url.host + parsed_url.path
95
+
96
+ # create base str. make it an object attr for ez debugging
97
+ # ref http://oauth.net/core/1.0/#anchor14
98
+ @base_str = [
99
+ @req_method,
100
+ percent_encode( req_url ),
101
+
102
+ # normalization is just x-www-form-urlencoded
103
+ percent_encode( query_string )
104
+
105
+ ].join( '&' )
106
+
107
+ # add signature
108
+ @params[ 'oauth_signature' ] = signature
109
+
110
+ return self
111
+ end
112
+ end
@@ -0,0 +1,27 @@
1
+ namespace :geocode do
2
+ desc "Geocode all objects without coordinates."
3
+ task :all => :environment do
4
+ class_name = ENV['CLASS'] || ENV['class']
5
+ sleep_timer = ENV['SLEEP'] || ENV['sleep']
6
+ raise "Please specify a CLASS (model)" unless class_name
7
+ klass = class_from_string(class_name)
8
+
9
+ klass.not_geocoded.each do |obj|
10
+ obj.geocode; obj.save
11
+ sleep(sleep_timer.to_f) unless sleep_timer.nil?
12
+ end
13
+ end
14
+ end
15
+
16
+ ##
17
+ # Get a class object from the string given in the shell environment.
18
+ # Similar to ActiveSupport's +constantize+ method.
19
+ #
20
+ def class_from_string(class_name)
21
+ parts = class_name.split("::")
22
+ constant = Object
23
+ parts.each do |part|
24
+ constant = constant.const_get(part)
25
+ end
26
+ constant
27
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class ActiveRecordTest < Test::Unit::TestCase
5
+
6
+ def test_exclude_condition_when_model_has_a_custom_primary_key
7
+ venue = VenuePlus.new(*venue_params(:msg))
8
+
9
+ # just call private method directly so we don't have to stub .near scope
10
+ conditions = venue.class.send(:add_exclude_condition, ["fake_condition"], venue)
11
+
12
+ assert_match( /#{VenuePlus.primary_key}/, conditions.join)
13
+ end
14
+
15
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class CacheTest < Test::Unit::TestCase
5
+
6
+ def test_second_occurrence_of_request_is_cache_hit
7
+ Geocoder2.configure(:cache => {})
8
+ Geocoder2::Lookup.all_services_except_test.each do |l|
9
+ Geocoder2.configure(:lookup => l)
10
+ set_api_key!(l)
11
+ results = Geocoder2.search("Madison Square Garden")
12
+ assert !results.first.cache_hit,
13
+ "Lookup #{l} returned erroneously cached result."
14
+ results = Geocoder2.search("Madison Square Garden")
15
+ assert results.first.cache_hit,
16
+ "Lookup #{l} did not return cached result."
17
+ end
18
+ end
19
+
20
+ def test_google_over_query_limit_does_not_hit_cache
21
+ Geocoder2.configure(:cache => {})
22
+ Geocoder2.configure(:lookup => :google)
23
+ set_api_key!(:google)
24
+ Geocoder2.configure(:always_raise => :all)
25
+ assert_raises Geocoder2::OverQueryLimitError do
26
+ Geocoder2.search("over limit")
27
+ end
28
+ lookup = Geocoder2::Lookup.get(:google)
29
+ assert_equal false, lookup.instance_variable_get(:@cache_hit)
30
+ assert_raises Geocoder2::OverQueryLimitError do
31
+ Geocoder2.search("over limit")
32
+ end
33
+ assert_equal false, lookup.instance_variable_get(:@cache_hit)
34
+ end
35
+ end
@@ -0,0 +1,211 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class CalculationsTest < Test::Unit::TestCase
5
+ def setup
6
+ Geocoder2.configure(
7
+ :units => :mi,
8
+ :distances => :linear
9
+ )
10
+ end
11
+
12
+ # --- degree distance ---
13
+
14
+ def test_longitude_degree_distance_at_equator
15
+ assert_equal 69, Geocoder2::Calculations.longitude_degree_distance(0).round
16
+ end
17
+
18
+ def test_longitude_degree_distance_at_new_york
19
+ assert_equal 53, Geocoder2::Calculations.longitude_degree_distance(40).round
20
+ end
21
+
22
+ def test_longitude_degree_distance_at_north_pole
23
+ assert_equal 0, Geocoder2::Calculations.longitude_degree_distance(89.98).round
24
+ end
25
+
26
+
27
+ # --- distance between ---
28
+
29
+ def test_distance_between_in_miles
30
+ assert_equal 69, Geocoder2::Calculations.distance_between([0,0], [0,1]).round
31
+ la_to_ny = Geocoder2::Calculations.distance_between([34.05,-118.25], [40.72,-74]).round
32
+ assert (la_to_ny - 2444).abs < 10
33
+ end
34
+
35
+ def test_distance_between_in_kilometers
36
+ assert_equal 111, Geocoder2::Calculations.distance_between([0,0], [0,1], :units => :km).round
37
+ la_to_ny = Geocoder2::Calculations.distance_between([34.05,-118.25], [40.72,-74], :units => :km).round
38
+ assert (la_to_ny - 3942).abs < 10
39
+ end
40
+
41
+ def test_distance_between_in_nautical_miles
42
+ assert_equal 60, Geocoder2::Calculations.distance_between([0,0], [0,1], :units => :nm).round
43
+ la_to_ny = Geocoder2::Calculations.distance_between([34.05,-118.25], [40.72,-74], :units => :nm).round
44
+ assert (la_to_ny - 2124).abs < 10
45
+ end
46
+
47
+
48
+ # --- geographic center ---
49
+
50
+ def test_geographic_center_with_arrays
51
+ assert_equal [0.0, 0.5],
52
+ Geocoder2::Calculations.geographic_center([[0,0], [0,1]])
53
+ assert_equal [0.0, 1.0],
54
+ Geocoder2::Calculations.geographic_center([[0,0], [0,1], [0,2]])
55
+ end
56
+
57
+ def test_geographic_center_with_mixed_arguments
58
+ p1 = [0, 0]
59
+ p2 = Landmark.new("Some Cold Place", 0, 1)
60
+ assert_equal [0.0, 0.5], Geocoder2::Calculations.geographic_center([p1, p2])
61
+ end
62
+
63
+
64
+ # --- bounding box ---
65
+
66
+ def test_bounding_box_calculation_in_miles
67
+ center = [51, 7] # Cologne, DE
68
+ radius = 10 # miles
69
+ dlon = radius / Geocoder2::Calculations.latitude_degree_distance
70
+ dlat = radius / Geocoder2::Calculations.longitude_degree_distance(center[0])
71
+ corners = [50.86, 6.77, 51.14, 7.23]
72
+ assert_equal corners.map{ |i| (i * 100).round },
73
+ Geocoder2::Calculations.bounding_box(center, radius).map{ |i| (i * 100).round }
74
+ end
75
+
76
+ def test_bounding_box_calculation_in_kilometers
77
+ center = [51, 7] # Cologne, DE
78
+ radius = 111 # kilometers (= 1 degree latitude)
79
+ dlon = radius / Geocoder2::Calculations.latitude_degree_distance(:km)
80
+ dlat = radius / Geocoder2::Calculations.longitude_degree_distance(center[0], :km)
81
+ corners = [50, 5.41, 52, 8.59]
82
+ assert_equal corners.map{ |i| (i * 100).round },
83
+ Geocoder2::Calculations.bounding_box(center, radius, :units => :km).map{ |i| (i * 100).round }
84
+ end
85
+
86
+ def test_bounding_box_calculation_with_object
87
+ center = [51, 7] # Cologne, DE
88
+ radius = 10 # miles
89
+ dlon = radius / Geocoder2::Calculations.latitude_degree_distance
90
+ dlat = radius / Geocoder2::Calculations.longitude_degree_distance(center[0])
91
+ corners = [50.86, 6.77, 51.14, 7.23]
92
+ obj = Landmark.new("Cologne", center[0], center[1])
93
+ assert_equal corners.map{ |i| (i * 100).round },
94
+ Geocoder2::Calculations.bounding_box(obj, radius).map{ |i| (i * 100).round }
95
+ end
96
+
97
+ def test_bounding_box_calculation_with_address_string
98
+ assert_nothing_raised do
99
+ Geocoder2::Calculations.bounding_box("4893 Clay St, San Francisco, CA", 50)
100
+ end
101
+ end
102
+
103
+ # --- random point ---
104
+
105
+ def test_random_point_within_radius
106
+ 20.times do
107
+ center = [51, 7] # Cologne, DE
108
+ radius = 10 # miles
109
+ random_point = Geocoder2::Calculations.random_point_near(center, radius)
110
+ distance = Geocoder2::Calculations.distance_between(center, random_point)
111
+ assert distance <= radius
112
+ end
113
+ end
114
+
115
+ # --- bearing ---
116
+
117
+ def test_compass_points
118
+ assert_equal "N", Geocoder2::Calculations.compass_point(0)
119
+ assert_equal "N", Geocoder2::Calculations.compass_point(1.0)
120
+ assert_equal "N", Geocoder2::Calculations.compass_point(360)
121
+ assert_equal "N", Geocoder2::Calculations.compass_point(361)
122
+ assert_equal "N", Geocoder2::Calculations.compass_point(-22)
123
+ assert_equal "NW", Geocoder2::Calculations.compass_point(-23)
124
+ assert_equal "S", Geocoder2::Calculations.compass_point(180)
125
+ assert_equal "S", Geocoder2::Calculations.compass_point(181)
126
+ end
127
+
128
+ def test_bearing_between
129
+ bearings = {
130
+ :n => 0,
131
+ :e => 90,
132
+ :s => 180,
133
+ :w => 270
134
+ }
135
+ points = {
136
+ :n => [41, -75],
137
+ :e => [40, -74],
138
+ :s => [39, -75],
139
+ :w => [40, -76]
140
+ }
141
+ directions = [:n, :e, :s, :w]
142
+ methods = [:linear, :spherical]
143
+
144
+ methods.each do |m|
145
+ directions.each_with_index do |d,i|
146
+ opp = directions[(i + 2) % 4] # opposite direction
147
+ b = Geocoder2::Calculations.bearing_between(
148
+ points[d], points[opp], :method => m)
149
+ assert (b - bearings[opp]).abs < 1,
150
+ "Bearing (#{m}) should be close to #{bearings[opp]} but was #{b}."
151
+ end
152
+ end
153
+ end
154
+
155
+ def test_spherical_bearing_to
156
+ l = Landmark.new(*landmark_params(:msg))
157
+ assert_equal 324, l.bearing_to([50,-85], :method => :spherical).round
158
+ end
159
+
160
+ def test_spherical_bearing_from
161
+ l = Landmark.new(*landmark_params(:msg))
162
+ assert_equal 136, l.bearing_from([50,-85], :method => :spherical).round
163
+ end
164
+
165
+ def test_linear_bearing_from_and_to_are_exactly_opposite
166
+ l = Landmark.new(*landmark_params(:msg))
167
+ assert_equal l.bearing_from([50,-86.1]), l.bearing_to([50,-86.1]) - 180
168
+ end
169
+
170
+ def test_extract_coordinates
171
+ coords = [-23,47]
172
+ l = Landmark.new("Madagascar", coords[0], coords[1])
173
+ assert_equal coords, Geocoder2::Calculations.extract_coordinates(l)
174
+ assert_equal coords, Geocoder2::Calculations.extract_coordinates(coords)
175
+ end
176
+
177
+ def test_extract_nan_coordinates
178
+ result = Geocoder2::Calculations.extract_coordinates([ nil, nil ])
179
+ assert_nan_coordinates?(result)
180
+
181
+ result = Geocoder2::Calculations.extract_coordinates(nil)
182
+ assert_nan_coordinates?(result)
183
+
184
+ result = Geocoder2::Calculations.extract_coordinates('')
185
+ assert_nan_coordinates?(result)
186
+
187
+ result = Geocoder2::Calculations.extract_coordinates([ 'nix' ])
188
+ assert_nan_coordinates?(result)
189
+
190
+ o = Object.new
191
+ result = Geocoder2::Calculations.extract_coordinates(o)
192
+ assert_nan_coordinates?(result)
193
+ end
194
+
195
+ def test_coordinates_present
196
+ assert Geocoder2::Calculations.coordinates_present?(3.23)
197
+ assert !Geocoder2::Calculations.coordinates_present?(nil)
198
+ assert !Geocoder2::Calculations.coordinates_present?(Geocoder2::Calculations::NAN)
199
+ assert !Geocoder2::Calculations.coordinates_present?(3.23, nil)
200
+ end
201
+
202
+ private # ------------------------------------------------------------------
203
+
204
+ def assert_nan_coordinates?(value)
205
+ assert value.is_a?(Array) &&
206
+ value.size == 2 &&
207
+ value[0].nan? &&
208
+ value[1].nan?,
209
+ "Expected value to be [NaN, NaN] but was #{value}"
210
+ end
211
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class ConfigurationTest < Test::Unit::TestCase
5
+ def setup
6
+ Geocoder2::Configuration.set_defaults
7
+ end
8
+
9
+ def test_exception_raised_on_bad_lookup_config
10
+ Geocoder2.configure(:lookup => :stoopid)
11
+ assert_raises Geocoder2::ConfigurationError do
12
+ Geocoder2.search "something dumb"
13
+ end
14
+ end
15
+
16
+ def test_setting_with_class_method
17
+ Geocoder2::Configuration.units = :test
18
+ assert_equal :test, Geocoder2.config.units
19
+ end
20
+
21
+ def test_setting_with_configure_method
22
+ Geocoder2.configure(:units => :test)
23
+ assert_equal :test, Geocoder2.config.units
24
+ end
25
+
26
+ def test_setting_with_block_syntax
27
+ orig = $VERBOSE; $VERBOSE = nil
28
+ Geocoder2.configure do |config|
29
+ config.units = :test
30
+ end
31
+ assert_equal :test, Geocoder2.config.units
32
+ ensure
33
+ $VERBOSE = orig
34
+ end
35
+
36
+ def test_config_for_lookup
37
+ Geocoder2.configure(
38
+ :timeout => 5,
39
+ :api_key => "aaa",
40
+ :google => {
41
+ :timeout => 2
42
+ }
43
+ )
44
+ assert_equal 2, Geocoder2.config_for_lookup(:google).timeout
45
+ assert_equal "aaa", Geocoder2.config_for_lookup(:google).api_key
46
+ end
47
+
48
+ def test_model_configuration
49
+ Landmark.reverse_geocoded_by :latitude, :longitude, :method => :spherical, :units => :km
50
+ assert_equal :km, Landmark.geocoder2_options[:units]
51
+ assert_equal :spherical, Landmark.geocoder2_options[:method]
52
+
53
+ v = Landmark.new(*landmark_params(:msg))
54
+ v.latitude = 0
55
+ v.longitude = 0
56
+ assert_equal 111, v.distance_to([0,1]).round
57
+ v.latitude = 40.750354
58
+ v.longitude = -73.993371
59
+ assert_equal 136, v.bearing_from([50,-85]).round
60
+ end
61
+
62
+ def test_configuration_chain
63
+ v = Landmark.new(*landmark_params(:msg))
64
+ v.latitude = 0
65
+ v.longitude = 0
66
+
67
+ # method option > global configuration
68
+ Geocoder2.configure(:units => :km)
69
+ assert_equal 69, v.distance_to([0,1], :mi).round
70
+
71
+ # per-model configuration > global configuration
72
+ Landmark.reverse_geocoded_by :latitude, :longitude, :method => :spherical, :units => :mi
73
+ assert_equal 69, v.distance_to([0,1]).round
74
+
75
+ # method option > per-model configuration
76
+ assert_equal 111, v.distance_to([0,1], :km).round
77
+ end
78
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class CustomBlockTest < Test::Unit::TestCase
5
+
6
+ def test_geocode_with_block_runs_block
7
+ e = Event.new(*venue_params(:msg))
8
+ coords = [40.750354, -73.993371]
9
+ e.geocode
10
+ assert_equal coords.map{ |c| c.to_s }.join(','), e.coords_string
11
+ end
12
+
13
+ def test_geocode_with_block_doesnt_auto_assign_coordinates
14
+ e = Event.new(*venue_params(:msg))
15
+ e.geocode
16
+ assert_nil e.latitude
17
+ assert_nil e.longitude
18
+ end
19
+
20
+ def test_reverse_geocode_with_block_runs_block
21
+ e = Party.new(*landmark_params(:msg))
22
+ e.reverse_geocode
23
+ assert_equal "US", e.country
24
+ end
25
+
26
+ def test_reverse_geocode_with_block_doesnt_auto_assign_address
27
+ e = Party.new(*landmark_params(:msg))
28
+ e.reverse_geocode
29
+ assert_nil e.address
30
+ end
31
+ end
32
+
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class ErrorHandlingTest < Test::Unit::TestCase
5
+
6
+ def teardown
7
+ Geocoder2.configure(:always_raise => [])
8
+ end
9
+
10
+ def test_does_not_choke_on_timeout
11
+ # keep test output clean: suppress timeout warning
12
+ orig = $VERBOSE; $VERBOSE = nil
13
+ Geocoder2::Lookup.all_services_except_test.each do |l|
14
+ Geocoder2.configure(:lookup => l)
15
+ set_api_key!(l)
16
+ assert_nothing_raised { Geocoder2.search("timeout") }
17
+ end
18
+ ensure
19
+ $VERBOSE = orig
20
+ end
21
+
22
+ def test_always_raise_timeout_error
23
+ Geocoder2.configure(:always_raise => [TimeoutError])
24
+ Geocoder2::Lookup.all_services_except_test.each do |l|
25
+ lookup = Geocoder2::Lookup.get(l)
26
+ set_api_key!(l)
27
+ assert_raises TimeoutError do
28
+ lookup.send(:results, Geocoder2::Query.new("timeout"))
29
+ end
30
+ end
31
+ end
32
+
33
+ def test_always_raise_socket_error
34
+ Geocoder2.configure(:always_raise => [SocketError])
35
+ Geocoder2::Lookup.all_services_except_test.each do |l|
36
+ lookup = Geocoder2::Lookup.get(l)
37
+ set_api_key!(l)
38
+ assert_raises SocketError do
39
+ lookup.send(:results, Geocoder2::Query.new("socket_error"))
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1 @@
1
+ {"results":[],"status":5,"msg":"AK Illegal or Not Exist:"}
@@ -0,0 +1 @@
1
+ {"status":0,"result":[]}
@@ -0,0 +1 @@
1
+ {"status":0,"result":{"location":{"lng":121.48789948569,"lat":31.249161555654},"formatted_address":"上海市闸北区天潼路619号","business":"七浦路,海宁路,北京东路","addressComponent":{"city":"上海市","district":"闸北区","province":"上海市","street":"天潼路","street_number":"619号"},"cityCode":289}}
@@ -0,0 +1,12 @@
1
+ {
2
+ "status":0,
3
+ "result":{
4
+ "location":{
5
+ "lng":116.30814954222,
6
+ "lat":40.056885091681
7
+ },
8
+ "precise":1,
9
+ "confidence":80,
10
+ "level":"\u5546\u52a1\u5927\u53a6"
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ {"authenticationResultCode":"InvalidCredentials","brandLogoUri":"http:\\/\\/dev.virtualearth.net\\/Branding\\/logo_powered_by.png","copyright":"Copyright \xC2\xA9 2012 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.","errorDetails":["Access was denied. You may have entered your credentials incorrectly, or you might not have access to the requested resource or operation."],"resourceSets":[],"statusCode":401,"statusDescription":"Unauthorized","traceId":"5c539f6e70c44b2e858741b6c932318e|EWRM001670|02.00.83.1900|"}