geocoder 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of geocoder might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +6 -0
- data/README.rdoc +12 -8
- data/lib/geocoder.rb +1 -0
- data/lib/geocoder/models/mongo_base.rb +55 -0
- data/lib/geocoder/models/mongo_mapper.rb +24 -0
- data/lib/geocoder/models/mongoid.rb +2 -25
- data/lib/geocoder/stores/active_record.rb +1 -1
- data/lib/geocoder/stores/mongo_base.rb +81 -0
- data/lib/geocoder/stores/mongo_mapper.rb +13 -0
- data/lib/geocoder/stores/mongoid.rb +3 -69
- data/lib/geocoder/version.rb +1 -1
- data/test/calculations_test.rb +147 -0
- data/test/configuration_test.rb +12 -0
- data/test/custom_block_test.rb +35 -0
- data/test/error_handling_test.rb +31 -0
- data/test/geocoder_test.rb +4 -442
- data/test/https_test.rb +20 -0
- data/test/input_handling_test.rb +41 -0
- data/test/lookup_test.rb +30 -0
- data/test/method_aliases_test.rb +29 -0
- data/test/mongoid_test.rb +35 -0
- data/test/mongoid_test_helper.rb +28 -0
- data/test/proxy_test.rb +27 -0
- data/test/result_test.rb +32 -0
- data/test/services_test.rb +95 -0
- metadata +20 -3
data/CHANGELOG.rdoc
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
Per-release changes to Geocoder.
|
4
4
|
|
5
|
+
== 1.0.2 (2011 June 25)
|
6
|
+
|
7
|
+
* Add support for MongoMapper (thanks github.com/spagalloco).
|
8
|
+
* Fix: user-specified coordinates field wasn't working with Mongoid (thanks github.com/thisduck).
|
9
|
+
* Fix: invalid location given to near scope was returning all results (Active Record) or error (Mongoid) (thanks github.com/ogennadi).
|
10
|
+
|
5
11
|
== 1.0.1 (2011 May 17)
|
6
12
|
|
7
13
|
* Add option to not rescue from certain exceptions (thanks github.com/ahmedrb).
|
data/README.rdoc
CHANGED
@@ -77,6 +77,10 @@ Reverse geocoding is similar:
|
|
77
77
|
reverse_geocoded_by :coordinates
|
78
78
|
after_validation :reverse_geocode # auto-fetch address
|
79
79
|
|
80
|
+
=== MongoMapper
|
81
|
+
|
82
|
+
MongoMapper is very similar to Mongoid, just be sure to include <tt>Geocoder::Model::Mongoid</tt>.
|
83
|
+
|
80
84
|
=== Bulk Geocoding
|
81
85
|
|
82
86
|
If you have just added geocoding to an existing application with a lot of objects you can use this Rake task to geocode them all:
|
@@ -152,7 +156,7 @@ You can convert these numbers to compass point names by using the utility method
|
|
152
156
|
|
153
157
|
<i>Note: when using SQLite +distance+ and +bearing+ values are provided for interface consistency only. They are not very accurate.</i>
|
154
158
|
|
155
|
-
To calculate accurate distance and bearing with SQLite or
|
159
|
+
To calculate accurate distance and bearing with SQLite or MongoDB:
|
156
160
|
|
157
161
|
obj.distance_to([43.9,-98.6]) # distance from obj to point
|
158
162
|
obj.bearing_to([43.9,-98.6]) # bearing from obj to point
|
@@ -163,10 +167,10 @@ The <tt>bearing_from/to</tt> methods take a single argument which can be: a <tt>
|
|
163
167
|
|
164
168
|
== More on Configuration
|
165
169
|
|
166
|
-
You are not stuck with using the +latitude+ and +longitude+ database column names (with ActiveRecord) or the +coordinates+ array (
|
170
|
+
You are not stuck with using the +latitude+ and +longitude+ database column names (with ActiveRecord) or the +coordinates+ array (Mongo) for storing coordinates. For example:
|
167
171
|
|
168
172
|
geocoded_by :address, :latitude => :lat, :longitude => :lon # ActiveRecord
|
169
|
-
geocoded_by :address, :coordinates => :coords #
|
173
|
+
geocoded_by :address, :coordinates => :coords # MongoDB
|
170
174
|
|
171
175
|
The +address+ method can return any string you'd use to search Google Maps. For example, any of the following are acceptable:
|
172
176
|
|
@@ -185,7 +189,7 @@ If your model has +street+, +city+, +state+, and +country+ attributes you might
|
|
185
189
|
For reverse geocoding you can also specify an alternate name attribute where the address will be stored, for example:
|
186
190
|
|
187
191
|
reverse_geocoded_by :lat, :lon, :address => :location # ActiveRecord
|
188
|
-
reverse_geocoded_by :coordinates, :address => :loc #
|
192
|
+
reverse_geocoded_by :coordinates, :address => :loc # MongoDB
|
189
193
|
|
190
194
|
|
191
195
|
== Advanced Geocoding
|
@@ -211,7 +215,7 @@ Every <tt>Geocoder::Result</tt> object, +result+, provides the following data:
|
|
211
215
|
* <tt>result.state</tt> - string
|
212
216
|
* <tt>result.state_code</tt> - string
|
213
217
|
* <tt>result.postal_code</tt> - string
|
214
|
-
* <tt>result.
|
218
|
+
* <tt>result.country</tt> - string
|
215
219
|
* <tt>result.country_code</tt> - string
|
216
220
|
|
217
221
|
If you're familiar with the results returned by the geocoding service you're using you can access even more data, but you'll need to be familiar with the particular <tt>Geocoder::Result</tt> object you're using and the structure of your geocoding service's responses. (See below for links to geocoding service documentation.)
|
@@ -410,15 +414,15 @@ When you install the Geocoder gem it adds a +geocode+ command to your shell. You
|
|
410
414
|
There are also a number of options for setting the geocoding API, key, and language, viewing the raw JSON reponse, and more. Please run <tt>geocode -h</tt> for details.
|
411
415
|
|
412
416
|
|
413
|
-
== Notes on
|
417
|
+
== Notes on MongoDB
|
414
418
|
|
415
419
|
=== The Near Method
|
416
420
|
|
417
|
-
|
421
|
+
Mongo document classes (Mongoid and MongoMapper) have a built-in +near+ scope, but since it only works two-dimensions Geocoder overrides it with its own spherical +near+ method in geocoded classes.
|
418
422
|
|
419
423
|
=== Latitude/Longitude Order
|
420
424
|
|
421
|
-
Coordinates are generally printed and spoken as latitude, then logitude ([lat,lon]). Geocoder respects this convention and always expects method arguments to be given in [lat,lon] order. However, MongoDB requires that coordinates be stored in [lon,lat] order as per the GeoJSON spec (http://geojson.org/geojson-spec.html#positions), so internally they are stored "backwards." However, this does not affect order of arguments to methods when using Mongoid.
|
425
|
+
Coordinates are generally printed and spoken as latitude, then logitude ([lat,lon]). Geocoder respects this convention and always expects method arguments to be given in [lat,lon] order. However, MongoDB requires that coordinates be stored in [lon,lat] order as per the GeoJSON spec (http://geojson.org/geojson-spec.html#positions), so internally they are stored "backwards." However, this does not affect order of arguments to methods when using Mongoid or MongoMapper.
|
422
426
|
|
423
427
|
|
424
428
|
== Distance Queries in SQLite
|
data/lib/geocoder.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
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
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Set attribute names and include the Geocoder module.
|
25
|
+
#
|
26
|
+
def reverse_geocoded_by(coordinates_attr, options = {}, &block)
|
27
|
+
geocoder_init(
|
28
|
+
:reverse_geocode => true,
|
29
|
+
:fetched_address => options[:address] || :address,
|
30
|
+
:coordinates => coordinates_attr,
|
31
|
+
:reverse_block => block
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
private # ----------------------------------------------------------------
|
36
|
+
|
37
|
+
def geocoder_init(options)
|
38
|
+
unless geocoder_initialized?
|
39
|
+
@geocoder_options = {}
|
40
|
+
require "geocoder/stores/#{geocoder_file_name}"
|
41
|
+
include eval("Geocoder::Store::" + geocoder_module_name)
|
42
|
+
end
|
43
|
+
@geocoder_options.merge! options
|
44
|
+
end
|
45
|
+
|
46
|
+
def geocoder_initialized?
|
47
|
+
begin
|
48
|
+
included_modules.include? eval("Geocoder::Store::" + geocoder_module_name)
|
49
|
+
rescue NameError
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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
|
+
ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
|
20
|
+
:min => -180, :max => 180 # create 2d index
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,37 +1,14 @@
|
|
1
1
|
require 'geocoder/models/base'
|
2
|
+
require 'geocoder/models/mongo_base'
|
2
3
|
|
3
4
|
module Geocoder
|
4
5
|
module Model
|
5
6
|
module Mongoid
|
6
7
|
include Base
|
8
|
+
include MongoBase
|
7
9
|
|
8
10
|
def self.included(base); base.extend(self); end
|
9
11
|
|
10
|
-
##
|
11
|
-
# Set attribute names and include the Geocoder module.
|
12
|
-
#
|
13
|
-
def geocoded_by(address_attr, options = {}, &block)
|
14
|
-
geocoder_init(
|
15
|
-
:geocode => true,
|
16
|
-
:user_address => address_attr,
|
17
|
-
:coordinates => options[:coordinates] || :coordinates,
|
18
|
-
:geocode_block => block
|
19
|
-
)
|
20
|
-
end
|
21
|
-
|
22
|
-
##
|
23
|
-
# Set attribute names and include the Geocoder module.
|
24
|
-
#
|
25
|
-
def reverse_geocoded_by(coordinates_attr, options = {}, &block)
|
26
|
-
geocoder_init(
|
27
|
-
:reverse_geocode => true,
|
28
|
-
:fetched_address => options[:address] || :address,
|
29
|
-
:coordinates => coordinates_attr,
|
30
|
-
:reverse_block => block
|
31
|
-
)
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
12
|
private # --------------------------------------------------------------
|
36
13
|
|
37
14
|
def geocoder_file_name; "mongoid"; end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Geocoder::Store
|
2
|
+
module MongoBase
|
3
|
+
|
4
|
+
def self.included_by_model(base)
|
5
|
+
base.class_eval do
|
6
|
+
|
7
|
+
scope :geocoded, lambda {
|
8
|
+
where(geocoder_options[:coordinates].ne => nil)
|
9
|
+
}
|
10
|
+
|
11
|
+
scope :not_geocoded, lambda {
|
12
|
+
where(geocoder_options[:coordinates] => nil)
|
13
|
+
}
|
14
|
+
|
15
|
+
scope :near, lambda{ |location, *args|
|
16
|
+
coords = Geocoder::Calculations.extract_coordinates(location)
|
17
|
+
|
18
|
+
# no results if no lat/lon given
|
19
|
+
return where(:id => false) unless coords.is_a?(Array)
|
20
|
+
|
21
|
+
radius = args.size > 0 ? args.shift : 20
|
22
|
+
options = args.size > 0 ? args.shift : {}
|
23
|
+
|
24
|
+
# Use BSON::OrderedHash if Ruby's hashes are unordered.
|
25
|
+
# Conditions must be in order required by indexes (see mongo gem).
|
26
|
+
empty = RUBY_VERSION.split('.')[1].to_i < 9 ? BSON::OrderedHash.new : {}
|
27
|
+
|
28
|
+
conds = empty.clone
|
29
|
+
field = geocoder_options[:coordinates]
|
30
|
+
conds[field] = empty.clone
|
31
|
+
conds[field]["$nearSphere"] = coords.reverse
|
32
|
+
conds[field]["$maxDistance"] = \
|
33
|
+
Geocoder::Calculations.distance_to_radians(radius, options[:units] || :mi)
|
34
|
+
|
35
|
+
if obj = options[:exclude]
|
36
|
+
conds[:_id.ne] = obj.id
|
37
|
+
end
|
38
|
+
where(conds)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Coordinates [lat,lon] of the object.
|
45
|
+
# This method always returns coordinates in lat,lon order,
|
46
|
+
# even though internally they are stored in the opposite order.
|
47
|
+
#
|
48
|
+
def to_coordinates
|
49
|
+
coords = send(self.class.geocoder_options[:coordinates])
|
50
|
+
coords.is_a?(Array) ? coords.reverse : []
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Look up coordinates and assign to +latitude+ and +longitude+ attributes
|
55
|
+
# (or other as specified in +geocoded_by+). Returns coordinates (array).
|
56
|
+
#
|
57
|
+
def geocode
|
58
|
+
do_lookup(false) do |o,rs|
|
59
|
+
r = rs.first
|
60
|
+
unless r.coordinates.nil?
|
61
|
+
o.send :write_attribute, self.class.geocoder_options[:coordinates], r.coordinates.reverse
|
62
|
+
end
|
63
|
+
r.coordinates
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Look up address and assign to +address+ attribute (or other as specified
|
69
|
+
# in +reverse_geocoded_by+). Returns address (string).
|
70
|
+
#
|
71
|
+
def reverse_geocode
|
72
|
+
do_lookup(true) do |o,rs|
|
73
|
+
r = rs.first
|
74
|
+
unless r.address.nil?
|
75
|
+
o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address
|
76
|
+
end
|
77
|
+
r.address
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,79 +1,13 @@
|
|
1
1
|
require 'geocoder/stores/base'
|
2
|
+
require 'geocoder/stores/mongo_base'
|
2
3
|
|
3
4
|
module Geocoder::Store
|
4
5
|
module Mongoid
|
5
6
|
include Base
|
7
|
+
include MongoBase
|
6
8
|
|
7
9
|
def self.included(base)
|
8
|
-
base
|
9
|
-
|
10
|
-
scope :geocoded, lambda {
|
11
|
-
where(geocoder_options[:coordinates].ne => nil)
|
12
|
-
}
|
13
|
-
|
14
|
-
scope :not_geocoded, lambda {
|
15
|
-
where(geocoder_options[:coordinates] => nil)
|
16
|
-
}
|
17
|
-
|
18
|
-
scope :near, lambda{ |location, *args|
|
19
|
-
coords = Geocoder::Calculations.extract_coordinates(location)
|
20
|
-
radius = args.size > 0 ? args.shift : 20
|
21
|
-
options = args.size > 0 ? args.shift : {}
|
22
|
-
|
23
|
-
# Use BSON::OrderedHash if Ruby's hashes are unordered.
|
24
|
-
# Conditions must be in order required by indexes (see mongo gem).
|
25
|
-
empty = RUBY_VERSION.split('.')[1].to_i < 9 ? BSON::OrderedHash.new : {}
|
26
|
-
|
27
|
-
conds = empty.clone
|
28
|
-
conds[:coordinates] = empty.clone
|
29
|
-
conds[:coordinates]["$nearSphere"] = coords.reverse
|
30
|
-
conds[:coordinates]["$maxDistance"] = \
|
31
|
-
Geocoder::Calculations.distance_to_radians(radius, options[:units] || :mi)
|
32
|
-
|
33
|
-
if obj = options[:exclude]
|
34
|
-
conds[:_id.ne] = obj.id
|
35
|
-
end
|
36
|
-
criteria.where(conds)
|
37
|
-
}
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
##
|
42
|
-
# Coordinates [lat,lon] of the object.
|
43
|
-
# This method always returns coordinates in lat,lon order,
|
44
|
-
# even though internally they are stored in the opposite order.
|
45
|
-
#
|
46
|
-
def to_coordinates
|
47
|
-
coords = send(self.class.geocoder_options[:coordinates])
|
48
|
-
coords.is_a?(Array) ? coords.reverse : []
|
49
|
-
end
|
50
|
-
|
51
|
-
##
|
52
|
-
# Look up coordinates and assign to +latitude+ and +longitude+ attributes
|
53
|
-
# (or other as specified in +geocoded_by+). Returns coordinates (array).
|
54
|
-
#
|
55
|
-
def geocode
|
56
|
-
do_lookup(false) do |o,rs|
|
57
|
-
r = rs.first
|
58
|
-
unless r.coordinates.nil?
|
59
|
-
o.send :write_attribute, self.class.geocoder_options[:coordinates], r.coordinates.reverse
|
60
|
-
end
|
61
|
-
r.coordinates
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
##
|
66
|
-
# Look up address and assign to +address+ attribute (or other as specified
|
67
|
-
# in +reverse_geocoded_by+). Returns address (string).
|
68
|
-
#
|
69
|
-
def reverse_geocode
|
70
|
-
do_lookup(true) do |o,rs|
|
71
|
-
r = rs.first
|
72
|
-
unless r.address.nil?
|
73
|
-
o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address
|
74
|
-
end
|
75
|
-
r.address
|
76
|
-
end
|
10
|
+
MongoBase.included_by_model(base)
|
77
11
|
end
|
78
12
|
end
|
79
13
|
end
|
data/lib/geocoder/version.rb
CHANGED
@@ -0,0 +1,147 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class CalculationsTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
|
7
|
+
# --- degree distance ---
|
8
|
+
|
9
|
+
def test_longitude_degree_distance_at_equator
|
10
|
+
assert_equal 69, Geocoder::Calculations.longitude_degree_distance(0).round
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_longitude_degree_distance_at_new_york
|
14
|
+
assert_equal 53, Geocoder::Calculations.longitude_degree_distance(40).round
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_longitude_degree_distance_at_north_pole
|
18
|
+
assert_equal 0, Geocoder::Calculations.longitude_degree_distance(89.98).round
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# --- distance between ---
|
23
|
+
|
24
|
+
def test_distance_between_in_miles
|
25
|
+
assert_equal 69, Geocoder::Calculations.distance_between([0,0], [0,1]).round
|
26
|
+
la_to_ny = Geocoder::Calculations.distance_between([34.05,-118.25], [40.72,-74]).round
|
27
|
+
assert (la_to_ny - 2444).abs < 10
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_distance_between_in_kilometers
|
31
|
+
assert_equal 111, Geocoder::Calculations.distance_between([0,0], [0,1], :units => :km).round
|
32
|
+
la_to_ny = Geocoder::Calculations.distance_between([34.05,-118.25], [40.72,-74], :units => :km).round
|
33
|
+
assert (la_to_ny - 3942).abs < 10
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# --- geographic center ---
|
38
|
+
|
39
|
+
def test_geographic_center_with_arrays
|
40
|
+
assert_equal [0.0, 0.5],
|
41
|
+
Geocoder::Calculations.geographic_center([[0,0], [0,1]])
|
42
|
+
assert_equal [0.0, 1.0],
|
43
|
+
Geocoder::Calculations.geographic_center([[0,0], [0,1], [0,2]])
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_geographic_center_with_mixed_arguments
|
47
|
+
p1 = [0, 0]
|
48
|
+
p2 = Landmark.new("Some Cold Place", 0, 1)
|
49
|
+
assert_equal [0.0, 0.5], Geocoder::Calculations.geographic_center([p1, p2])
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# --- bounding box ---
|
54
|
+
|
55
|
+
def test_bounding_box_calculation_in_miles
|
56
|
+
center = [51, 7] # Cologne, DE
|
57
|
+
radius = 10 # miles
|
58
|
+
dlon = radius / Geocoder::Calculations.latitude_degree_distance
|
59
|
+
dlat = radius / Geocoder::Calculations.longitude_degree_distance(center[0])
|
60
|
+
corners = [50.86, 6.77, 51.14, 7.23]
|
61
|
+
assert_equal corners.map{ |i| (i * 100).round },
|
62
|
+
Geocoder::Calculations.bounding_box(center, radius).map{ |i| (i * 100).round }
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_bounding_box_calculation_in_kilometers
|
66
|
+
center = [51, 7] # Cologne, DE
|
67
|
+
radius = 111 # kilometers (= 1 degree latitude)
|
68
|
+
dlon = radius / Geocoder::Calculations.latitude_degree_distance(:km)
|
69
|
+
dlat = radius / Geocoder::Calculations.longitude_degree_distance(center[0], :km)
|
70
|
+
corners = [50, 5.41, 52, 8.59]
|
71
|
+
assert_equal corners.map{ |i| (i * 100).round },
|
72
|
+
Geocoder::Calculations.bounding_box(center, radius, :units => :km).map{ |i| (i * 100).round }
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_bounding_box_calculation_with_object
|
76
|
+
center = [51, 7] # Cologne, DE
|
77
|
+
radius = 10 # miles
|
78
|
+
dlon = radius / Geocoder::Calculations.latitude_degree_distance
|
79
|
+
dlat = radius / Geocoder::Calculations.longitude_degree_distance(center[0])
|
80
|
+
corners = [50.86, 6.77, 51.14, 7.23]
|
81
|
+
obj = Landmark.new("Cologne", center[0], center[1])
|
82
|
+
assert_equal corners.map{ |i| (i * 100).round },
|
83
|
+
Geocoder::Calculations.bounding_box(obj, radius).map{ |i| (i * 100).round }
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_bounding_box_calculation_with_address_string
|
87
|
+
assert_nothing_raised do
|
88
|
+
Geocoder::Calculations.bounding_box("4893 Clay St, San Francisco, CA", 50)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# --- bearing ---
|
94
|
+
|
95
|
+
def test_compass_points
|
96
|
+
assert_equal "N", Geocoder::Calculations.compass_point(0)
|
97
|
+
assert_equal "N", Geocoder::Calculations.compass_point(1.0)
|
98
|
+
assert_equal "N", Geocoder::Calculations.compass_point(360)
|
99
|
+
assert_equal "N", Geocoder::Calculations.compass_point(361)
|
100
|
+
assert_equal "N", Geocoder::Calculations.compass_point(-22)
|
101
|
+
assert_equal "NW", Geocoder::Calculations.compass_point(-23)
|
102
|
+
assert_equal "S", Geocoder::Calculations.compass_point(180)
|
103
|
+
assert_equal "S", Geocoder::Calculations.compass_point(181)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_bearing_between
|
107
|
+
bearings = {
|
108
|
+
:n => 0,
|
109
|
+
:e => 90,
|
110
|
+
:s => 180,
|
111
|
+
:w => 270
|
112
|
+
}
|
113
|
+
points = {
|
114
|
+
:n => [41, -75],
|
115
|
+
:e => [40, -74],
|
116
|
+
:s => [39, -75],
|
117
|
+
:w => [40, -76]
|
118
|
+
}
|
119
|
+
directions = [:n, :e, :s, :w]
|
120
|
+
methods = [:linear, :spherical]
|
121
|
+
|
122
|
+
methods.each do |m|
|
123
|
+
directions.each_with_index do |d,i|
|
124
|
+
opp = directions[(i + 2) % 4] # opposite direction
|
125
|
+
b = Geocoder::Calculations.bearing_between(
|
126
|
+
points[d], points[opp], :method => m)
|
127
|
+
assert (b - bearings[opp]).abs < 1,
|
128
|
+
"Bearing (#{m}) should be close to #{bearings[opp]} but was #{b}."
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_spherical_bearing_to
|
134
|
+
l = Landmark.new(*landmark_params(:msg))
|
135
|
+
assert_equal 324, l.bearing_to([50,-85], :method => :spherical).round
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_spherical_bearing_from
|
139
|
+
l = Landmark.new(*landmark_params(:msg))
|
140
|
+
assert_equal 136, l.bearing_from([50,-85], :method => :spherical).round
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_linear_bearing_from_and_to_are_exactly_opposite
|
144
|
+
l = Landmark.new(*landmark_params(:msg))
|
145
|
+
assert_equal l.bearing_from([50,-86.1]), l.bearing_to([50,-86.1]) - 180
|
146
|
+
end
|
147
|
+
end
|