geo_calc 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +20 -0
- data/README.textile +181 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/geo_calc.gemspec +74 -0
- data/lib/geo_calc/calculations.rb +333 -0
- data/lib/geo_calc/core_ext.rb +228 -0
- data/lib/geo_calc/geo.rb +170 -0
- data/lib/geo_calc/geo_point.rb +103 -0
- data/lib/geo_calc/js/geo_calc.js +551 -0
- data/lib/geo_calc.rb +1 -0
- data/spec/geo_calc/calculations_spec.rb +174 -0
- data/spec/geo_calc/core_ext_spec.rb +272 -0
- data/spec/geo_calc/geo_point_spec.rb +228 -0
- data/spec/geo_calc/geo_spec.rb +99 -0
- data/spec/spec_helper.rb +12 -0
- metadata +118 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# - www.movable-type.co.uk/scripts/latlong.html
|
4
|
+
|
5
|
+
# Accuracy: since the earth is not quite a sphere, there are small errors in using spherical geometry; the earth is actually roughly ellipsoidal (or more precisely, oblate spheroidal) with a radius varying between about 6,378km (equatorial) and 6,357km (polar), and local radius of curvature varying from 6,336km (equatorial meridian) to 6,399km (polar). 6,371 km is the generally accepted value for the Earth’s mean radius. This means that errors from assuming spherical geometry might be up to 0.55% crossing the equator, though generally below 0.3%, depending on latitude and direction of travel. An accuracy of better than 3m in 1km is mostly good enough for me, but if you want greater accuracy, you could use the Vincenty formula for calculating geodesic distances on ellipsoids, which gives results accurate to within 1mm. (Out of sheer perversity – I’ve never needed such accuracy – I looked up this formula and discovered the JavaScript implementation was simpler than I expected).
|
6
|
+
# Trig functions take arguments in radians, so latitude, longitude, and bearings in degrees (either decimal or degrees/minutes/seconds) need to be converted to radians, rad = π.deg/180. When converting radians back to degrees (deg = 180.rad/π), West is negative if using signed decimal degrees. For bearings, values in the range -π to +π [-180° to +180°] need to be converted to 0 to +2π [0°–360°]; this can be done by (brng+2.π)%2.π [or brng+360)%360] where % is the modulo operator.
|
7
|
+
#
|
8
|
+
# The atan2() function widely used here takes two arguments, atan2(y, x), and computes the arc tangent of the ratio y/x. It is more flexible than atan(y/x), since it handles x=0, and it also returns values in all 4 quadrants -π to +π (the atan function returns values in the range -π/2 to +π/2).
|
9
|
+
# All bearings are with respect to true north, 0°=N, 90°=E, etc; if you are working from a compass, magnetic north varies from true north in a complex way around the earth, and the difference has to be compensated for by variances indicated on local maps.
|
10
|
+
#
|
11
|
+
# If you implement any formula involving atan2 in Microsoft Excel, you will need to reverse the arguments, as Excel has them the opposite way around from JavaScript – conventional order is atan2(y, x), but Excel uses atan2(x, y). To use atan2 in a (VBA) macro, you can use WorksheetFunction.Atan2().
|
12
|
+
# If you are using Google Maps, several of these functions are now provided in the Google Maps API V3 ‘spherical’ library (computeDistanceBetween(), computeHeading(), computeOffset(), interpolate(), etc;
|
13
|
+
# note they use a default Earth radius of 6,378,137 meters).
|
14
|
+
#
|
15
|
+
# I learned a lot from the US Census Bureau GIS FAQ which is no longer available, so I’ve made a copy.
|
16
|
+
# Thanks to Ed Williams’ Aviation Formulary for many of the formulæ.
|
17
|
+
# For miles, divide km by 1.609344
|
18
|
+
# For nautical miles, divide km by 1.852
|
19
|
+
|
20
|
+
describe GeoCalc do
|
21
|
+
|
22
|
+
# Point 1: 50 03 59N, 005 42 53W
|
23
|
+
# Point 2: 58 38 38N, 003 04 12W
|
24
|
+
context 'p1= (50 03 59N, 005 42 53W) .. p2= (58 38 38N, 003 04 12W)' do
|
25
|
+
before do
|
26
|
+
@p1 = GeoPoint.new "50 03 59N", "005 42 53W"
|
27
|
+
@p2 = GeoPoint.new "58 38 38N, 003 04 12W"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Distance: 968.9 km
|
31
|
+
describe '#distance_to' do
|
32
|
+
it 'should return the distance from p1 to p2 as 968.9 km' do
|
33
|
+
@p1.distance_to(@p2).should be_within(0.5).of(968.9)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Initial bearing: 009°07′11″
|
38
|
+
describe '#bearing_to' do
|
39
|
+
it 'should return the initial bearing from p1 to p2 as 009 07 11' do
|
40
|
+
@p1.bearing_to(@p2).to_dms.should match /009.+07.+11/
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Final bearing: 011°16′31″
|
45
|
+
describe '#final_bearing_to' do
|
46
|
+
it 'should return the initial bearing from p1 to p2 as 011 16 31' do
|
47
|
+
@p1.final_bearing_to(@p2).to_dms.should match /011.+16.+31/
|
48
|
+
end
|
49
|
+
end
|
50
|
+
#
|
51
|
+
# Midpoint: 54°21′44″N, 004°31′50″W
|
52
|
+
describe '#midpoint_to' do
|
53
|
+
it 'should return the initial bearing from p1 to p2 as 011 16 31' do
|
54
|
+
@p1.midpoint_to(@p2).to_dms.should match /54.+21.+44.+N, 004.+31.+50.+W/
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end # context
|
58
|
+
|
59
|
+
# Start Point: 53°19′14″N, 001°43′47″W
|
60
|
+
# Bearing: 096°01′18″
|
61
|
+
# Distance: 124.8
|
62
|
+
|
63
|
+
context 'Start Point: (53 19 14 N, 001 43 47 W)' do
|
64
|
+
before do
|
65
|
+
@p1 = GeoPoint.new "53 19 14 N, 001 43 47 W"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Destination point: 53°11′18″N, 000°08′00″E
|
69
|
+
# Final bearing: 097°30′52″
|
70
|
+
describe '#destination_point' do
|
71
|
+
it 'should return the destination_point as (53 11 18 N, 000 08 00 E)' do
|
72
|
+
# Bearing: 096°01′18″
|
73
|
+
# Distance: 124.8
|
74
|
+
@p2 = @p1.destination_point("096 01 18", 124.8)
|
75
|
+
@p2.to_dms.should match /53.+11.+18.+N, 000.+08.+00.+E/
|
76
|
+
|
77
|
+
# @p1.final_bearing_to(@p2).to_dms.should == "097°30′52″"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Intersection:
|
83
|
+
# Point 1: 51.885 N, 0.235 E; Brng 108.63°
|
84
|
+
# Point 2: 49.008 N, 2.549 E; Brng 32.72°
|
85
|
+
# Distance: 124.8 km
|
86
|
+
context 'Points: (51.885 N, 0.235 E) Brng 108.63 .. (49.008 N, 2.549 E) Brng 32.72' do
|
87
|
+
before do
|
88
|
+
@p1 = GeoPoint.new "51.885 N, 0.235 E"
|
89
|
+
@brng1 = "108.63"
|
90
|
+
@p2 = GeoPoint.new "49.008 N, 2.549 E"
|
91
|
+
@brng2 = "32.72"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Intersection point: 50°54′06″N, 004°29′39″E
|
95
|
+
describe '#intersection' do
|
96
|
+
it 'should return the intersection between p1 and p2 as (50 54 06 N, 004 29 39 E)' do
|
97
|
+
GeoCalc.intersection(@p1, @brng1, @p2, @brng2).to_dms.should match /50.+54.+06.+N, 004.+29.+39.+E/
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Rhumb lines:
|
103
|
+
# Point 1: (50 21 50 N, 004 09 25 W)
|
104
|
+
# Point 2: (42 21 04 N, 071 02 27 W)
|
105
|
+
context 'Points: (50 21 50N, 004 09 25W ) (42 21 04N, 071 02 27 W)' do
|
106
|
+
before do
|
107
|
+
@p1 = GeoPoint.new "50 21 50N, 004 09 25 W"
|
108
|
+
@p2 = GeoPoint.new "42 21 04N, 071 02 27 W"
|
109
|
+
end
|
110
|
+
|
111
|
+
# Distance: 5196 km
|
112
|
+
describe '#rhumb_distance_to' do
|
113
|
+
it 'should return the distance from p1 to p2 as 5196 km' do
|
114
|
+
@p1.rhumb_distance_to(@p2).should be_within(1).of(5196)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Bearing: 260°07′38″
|
119
|
+
describe '#rhumb_bearing_to' do
|
120
|
+
it 'should return the initial bearing from p1 to p2 as 260 07 38' do
|
121
|
+
@p1.rhumb_bearing_to(@p2).to_dms.should match /260.+07.+38/
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Start Point: (51 07 32N, 001 20 17E)
|
127
|
+
# Bearing: 116°38′10
|
128
|
+
# Distance: 40.23 km
|
129
|
+
context 'Start Point: (51 07 32N, 001 20 17E), Bearing: 116 38 10, Distance: 40.23 km' do
|
130
|
+
before do
|
131
|
+
@p1 = GeoPoint.new "51 07 32N, 001 20 17E"
|
132
|
+
@brng = "116 38 10"
|
133
|
+
@dist = 40.23
|
134
|
+
end
|
135
|
+
|
136
|
+
# Destination point: 50°57′48″N, 001°51′09″E
|
137
|
+
describe '#rhumb_destination_point' do
|
138
|
+
it 'should return the destination_point as (50 57 48 N, 001 51 09 E)' do
|
139
|
+
@p2 = @p1.rhumb_destination_point(@brng, @dist)
|
140
|
+
@p2.to_dms.should match /50.+57.+48.+N, 001.+51.+09.+E/
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Conversion
|
146
|
+
# Point: (52°12′17.0″N, 000°08′26.0″E)
|
147
|
+
# 52.20472 numeric deg (lat)
|
148
|
+
# 0.14056 numeric deg (lon)
|
149
|
+
context 'Point: (52 12 17.0 N, 000 08 26.0 E)' do
|
150
|
+
before do
|
151
|
+
@p = GeoPoint.new "52 12 17.0 N", "000 08 26.0 E"
|
152
|
+
@lat = 52.20472
|
153
|
+
@lon = 0.14056
|
154
|
+
end
|
155
|
+
|
156
|
+
# 1° ≈ 111 km (110.57 eq’l — 111.70 polar)
|
157
|
+
# 1′ ≈ 1.85 km (= 1 nm) 0.01° ≈ 1.11 km
|
158
|
+
# 1″ ≈ 30.9 m 0.0001° ≈ 11.1 m
|
159
|
+
|
160
|
+
# Convert numeric degrees to deg/min/sec longitude (suffixed with E/W)
|
161
|
+
describe '#to_lon' do
|
162
|
+
it 'should Convert numeric degrees to deg/min/sec longitude (suffixed with E/W)' do
|
163
|
+
@lon.to_lon_dms.should match /000.+08.+26.+E/
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Convert numeric degrees to deg/min/sec latitude (suffixed with N/S)
|
168
|
+
describe '#to_lat' do
|
169
|
+
it 'should convert numeric degrees to deg/min/sec latitude (suffixed with N/S)' do
|
170
|
+
@lat.to_lat_dms.should match /52.+12.+17.+N/
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# - www.movable-type.co.uk/scripts/latlong.html
|
4
|
+
describe GeoPoint do
|
5
|
+
describe 'ruby core Class extensions' do
|
6
|
+
describe NumericGeoExt do
|
7
|
+
describe '#to_rad' do
|
8
|
+
it 'should convert 0 degrees to 0 radians' do
|
9
|
+
0.to_rad.should == 0
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should convert 180 degrees to PI radians' do
|
13
|
+
180.to_rad.should == Math::PI
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should convert 360 degrees to 6.28 radians' do
|
17
|
+
360.to_rad.should == Math::PI*2
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#to_radians' do
|
22
|
+
it 'should alias to_rad' do
|
23
|
+
360.to_rad.should == 360.to_radians
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#to_deg' do
|
28
|
+
it 'should convert 0 radians to 0 degrees' do
|
29
|
+
0.to_deg.should == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should convert PI radians to 180 degrees' do
|
33
|
+
180.to_rad.to_deg.should be_within(0.01).of(180)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should convert 6.28 radians to 360 degrees' do
|
37
|
+
360.to_rad.to_deg.should be_within(0.01).of(360)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#to_degrees' do
|
42
|
+
it 'should alias to_deg' do
|
43
|
+
360.to_deg.should == 360.to_degrees
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#to_fixed' do
|
48
|
+
it 'should make precision 4' do
|
49
|
+
1.123456.to_fixed(2).should == '1.12'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#to_precision' do
|
54
|
+
it 'should alis to_fixed' do
|
55
|
+
1.123456.to_precision(4).should == '1.1235'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#normalize' do
|
60
|
+
it 'should turn 180 deg and normalize' do
|
61
|
+
361.normalize_deg(180).should == 181
|
62
|
+
end
|
63
|
+
it 'should normalize deg' do
|
64
|
+
361.normalize_deg.should == 1
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should alias with #normalize_degrees' do
|
68
|
+
362.normalize_degrees.should == 2
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end # NumericGeoExt
|
72
|
+
|
73
|
+
describe NumericLatLngExt do
|
74
|
+
describe 'Fixnum extension' do
|
75
|
+
describe '#to_lat' do
|
76
|
+
it 'should return latitude degree value for 360' do
|
77
|
+
360.to_lat.should == 0
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
|
81
|
+
361.to_lat.should == 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#to_lng' do
|
86
|
+
it 'should return latitude degree value for 360' do
|
87
|
+
90.to_lng.should == 90
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
|
91
|
+
91.to_lng.should == 91
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'Float extension' do
|
97
|
+
describe '#to_lat' do
|
98
|
+
it 'should return latitude degree value for 360' do
|
99
|
+
(360.0).to_lat.should == 0
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
|
103
|
+
(361.1).to_lat.should be_within(0.01).of(1.1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#to_lng' do
|
108
|
+
it 'should return latitude degree value for 360' do
|
109
|
+
(360.0).to_lng.should == 0
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should normalize degrees before converting to latitude, so 361 becomes 1' do
|
113
|
+
(361.1).to_lng.should be_within(0.01).of(1.1)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'Array extension' do
|
119
|
+
describe '#to_lat' do
|
120
|
+
it 'should return latitude as first element' do
|
121
|
+
@arr = [4, 27]
|
122
|
+
@arr.to_lat.should == 4
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should return latitude as #to_lng of first element' do
|
126
|
+
@arr = ["4.1", 27]
|
127
|
+
@arr.to_lat.should == 4.1
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#to_lng' do
|
132
|
+
it 'should return latitude degree value for 360' do
|
133
|
+
@arr = [4, 27]
|
134
|
+
@arr.to_lng.should == 27
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should return latitude as #to_lng of first element' do
|
138
|
+
@arr = [4, "27.2"]
|
139
|
+
@arr.to_lng.should == 27.2
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '#to_lat_lng' do
|
144
|
+
it 'should return Array with lat, lng' do
|
145
|
+
@arr = ["3", {:lng => "2"}]
|
146
|
+
@arr.to_lat_lng.should == [3, 2]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#geo_point' do
|
151
|
+
it 'should return a GeoPoint' do
|
152
|
+
@p = [3, 2].geo_point
|
153
|
+
@p.should be_a(GeoPoint)
|
154
|
+
@p.to_lat_lng.should == [3, 2]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end # Array
|
158
|
+
|
159
|
+
describe 'Hash extension' do
|
160
|
+
describe '#to_lat' do
|
161
|
+
it 'should return latitude as #to_lat on the value for key :lat' do
|
162
|
+
@hash = {:lat => 4}
|
163
|
+
@hash.to_lat.should == 4
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should return latitude as #to_lat on the value for key :latitude' do
|
167
|
+
@hash = {:latitude => "7"}
|
168
|
+
@hash.to_lat.should == 7
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#to_lng' do
|
173
|
+
it 'should return latitude as #to_lng on the value for key :lng' do
|
174
|
+
@hash = {:lng => 2}
|
175
|
+
@hash.to_lng.should == 2
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should return latitude as #to_lng on the value for key :longitude' do
|
179
|
+
@hash = {:longitude => "3.1"}
|
180
|
+
@hash.to_lng.should == 3.1
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#to_lat_lng' do
|
185
|
+
it 'should return Array with lat, lng' do
|
186
|
+
@hash = {:lng => 2, :lat => "3"}
|
187
|
+
@hash.to_lat_lng.should == [3, 2]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '#geo_point' do
|
192
|
+
it 'should return a GeoPoint' do
|
193
|
+
@p = {:lng => 2, :lat => "3"}.geo_point
|
194
|
+
@p.should be_a(GeoPoint)
|
195
|
+
@p.to_lat_lng.should == [3, 2]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end # Hash
|
199
|
+
|
200
|
+
describe 'String extension' do
|
201
|
+
describe '#to_lat' do
|
202
|
+
it 'should not return latitude on empty String' do
|
203
|
+
@str = ""
|
204
|
+
lambda { @str.to_lat}.should raise_error
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should return latitude' do
|
208
|
+
@str = "4"
|
209
|
+
@str.to_lat.should == 4
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should convert to latitude' do
|
213
|
+
@str = "50 03 59N"
|
214
|
+
@str.to_lat.should be_within(0.4).of(50)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe '#to_lng' do
|
219
|
+
it 'should not return longitude on empty String' do
|
220
|
+
@str = ""
|
221
|
+
lambda { @str.to_lng}.should raise_error
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should return latitude' do
|
225
|
+
@str = "4"
|
226
|
+
@str.to_lat.should == 4
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'should convert to latitude' do
|
230
|
+
@str = "50 03 59E"
|
231
|
+
@str.to_lat.should be_within(0.4).of(50)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe '#to_lat_lng' do
|
236
|
+
it 'should return Array with lat, lng' do
|
237
|
+
@str = "4, 3"
|
238
|
+
@str.to_lat_lng.should == [4, 3]
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'should raise error if only latitude' do
|
242
|
+
@str = "4"
|
243
|
+
lambda { @str.to_lat_lng}.should raise_error
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should raise error if "," but only latitude' do
|
247
|
+
@str = "4,"
|
248
|
+
lambda { @str.to_lat_lng}.should raise_error
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'should raise error if "," in bad position' do
|
252
|
+
@str = ", 4 3"
|
253
|
+
lambda { @str.to_lat_lng}.should raise_error
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should raise error if not using "," as seperator' do
|
257
|
+
@str = "4; 3"
|
258
|
+
lambda { @str.to_lat_lng}.should raise_error
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe '#geo_point' do
|
263
|
+
it 'should return a GeoPoint' do
|
264
|
+
@p = "3, 2".geo_point
|
265
|
+
@p.should be_a(GeoPoint)
|
266
|
+
@p.to_lat_lng.should == [3, 2]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end # String
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# - www.movable-type.co.uk/scripts/latlong.html
|
4
|
+
describe GeoPoint do
|
5
|
+
describe '#initializer' do
|
6
|
+
describe '1 argument' do
|
7
|
+
describe 'single String' do
|
8
|
+
describe '50.1, 5.0 ' do
|
9
|
+
it 'should create a GeoPoint' do
|
10
|
+
p1 = GeoPoint.new "50.1, 5.0"
|
11
|
+
p1.should be_a(GeoPoint)
|
12
|
+
p1.lat.should == 50.1
|
13
|
+
p1.lon.should == 5.0
|
14
|
+
p1.unit.should == :degrees
|
15
|
+
p1.radius.should == 6371
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '(50.1, 5.0)' do
|
20
|
+
it 'should create a GeoPoint' do
|
21
|
+
p1 = GeoPoint.new "(50.1, 5.2)"
|
22
|
+
p1.should be_a(GeoPoint)
|
23
|
+
p1.lat.should == 50.1
|
24
|
+
p1.lon.should == 5.2
|
25
|
+
p1.unit.should == :degrees
|
26
|
+
p1.radius.should == 6371
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '58 38 38N, 003 04 12W' do
|
31
|
+
it 'should create a GeoPoint' do
|
32
|
+
p1 = GeoPoint.new "58 38 38N, 003 04 12W"
|
33
|
+
p1.should be_a(GeoPoint)
|
34
|
+
p1.lat.should be_within(0.5).of(58.38)
|
35
|
+
p1.lon.should be_within(0.5).of(356.5)
|
36
|
+
p1.unit.should == :degrees
|
37
|
+
p1.radius.should == 6371
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '(58 38 38N, 003 04 12W)' do
|
42
|
+
it 'should create a GeoPoint' do
|
43
|
+
p1 = GeoPoint.new "(58 38 38N, 003 04 12W)"
|
44
|
+
p1.should be_a(GeoPoint)
|
45
|
+
p1.lat.should be_within(0.5).of(58.38)
|
46
|
+
p1.lon.should be_within(0.5).of(356.5) # W is negative, then normalize
|
47
|
+
p1.unit.should == :degrees
|
48
|
+
p1.radius.should == 6371
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'single Array' do
|
54
|
+
describe '2 Fixed numbers: 50,5 ' do
|
55
|
+
it 'should create a GeoPoint' do
|
56
|
+
p1 = GeoPoint.new [50, 5]
|
57
|
+
p1.should be_a(GeoPoint)
|
58
|
+
p1.lat.should == 50
|
59
|
+
p1.lon.should == 5
|
60
|
+
p1.unit.should == :degrees
|
61
|
+
p1.radius.should == 6371
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '2 Float numbers: 50.1, 5.0 ' do
|
66
|
+
it 'should create a GeoPoint' do
|
67
|
+
p1 = GeoPoint.new [50.1, 5.0]
|
68
|
+
p1.should be_a(GeoPoint)
|
69
|
+
p1.lat.should == 50.1
|
70
|
+
p1.lon.should == 5.0
|
71
|
+
p1.unit.should == :degrees
|
72
|
+
p1.radius.should == 6371
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'single Hash' do
|
77
|
+
describe 'with: {:lat => 50.1, :lng => 5.1}' do
|
78
|
+
it 'should create a GeoPoint' do
|
79
|
+
p1 = GeoPoint.new :lat => 50.1, :lng => 5.1
|
80
|
+
p1.should be_a(GeoPoint)
|
81
|
+
p1.lat.should == 50.1
|
82
|
+
p1.lon.should == 5.1
|
83
|
+
p1.unit.should == :degrees
|
84
|
+
p1.radius.should == 6371
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'with: {:lat => 50.1, :long => 5.1}' do
|
89
|
+
it 'should create a GeoPoint' do
|
90
|
+
p1 = GeoPoint.new :lat => 50.1, :long => 5.1
|
91
|
+
p1.should be_a(GeoPoint)
|
92
|
+
p1.lat.should == 50.1
|
93
|
+
p1.lon.should == 5.1
|
94
|
+
p1.unit.should == :degrees
|
95
|
+
p1.radius.should == 6371
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'with: {:latitude => 50.1, :longitude => 5.1}' do
|
100
|
+
it 'should create a GeoPoint' do
|
101
|
+
p1 = GeoPoint.new :latitude => 50.1, :longitude => 5.1
|
102
|
+
p1.should be_a(GeoPoint)
|
103
|
+
p1.lat.should == 50.1
|
104
|
+
p1.lon.should == 5.1
|
105
|
+
p1.unit.should == :degrees
|
106
|
+
p1.radius.should == 6371
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'with 2 arguments' do
|
114
|
+
describe '2 Fixed numbers (Fixnum)' do
|
115
|
+
it 'should create a GeoPoint' do
|
116
|
+
p1 = GeoPoint.new 50, 5
|
117
|
+
p1.should be_a(GeoPoint)
|
118
|
+
p1.lat.should == 50
|
119
|
+
p1.lon.should == 5
|
120
|
+
p1.unit.should == :degrees
|
121
|
+
p1.radius.should == 6371
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '2 Float numbers' do
|
126
|
+
it 'should create a GeoPoint' do
|
127
|
+
p1 = GeoPoint.new 50.1, 5.0
|
128
|
+
p1.should be_a(GeoPoint)
|
129
|
+
p1.lat.should == 50.1
|
130
|
+
p1.lon.should == 5.0
|
131
|
+
p1.unit.should == :degrees
|
132
|
+
p1.radius.should == 6371
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '2 Strings: "58 38 38N", "003 04 12W"' do
|
137
|
+
it 'should create a GeoPoint' do
|
138
|
+
p1 = GeoPoint.new "58 38 38N", "003 04 12W"
|
139
|
+
p1.should be_a(GeoPoint)
|
140
|
+
p1.lat.should be_within(0.5).of(58.38)
|
141
|
+
p1.lon.should be_within(0.5).of(356.5)
|
142
|
+
p1.unit.should == :degrees
|
143
|
+
p1.radius.should == 6371
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end # initializer
|
148
|
+
|
149
|
+
describe '#to_s' do
|
150
|
+
before :each do
|
151
|
+
@p1 = GeoPoint.new 50.1, 5
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should return GeoPoint as a dms formatted String' do
|
155
|
+
@p1.to_s.should match /50.+5/
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should return GeoPoint as a dms formatted String' do
|
159
|
+
@p1.to_s(:dm, 2).should match /50.+5/
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#lat' do
|
164
|
+
before :each do
|
165
|
+
@p1 = GeoPoint.new 50, 5
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should return latitude' do
|
169
|
+
@p1.lat.should == 50
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#latitude (alias)' do
|
173
|
+
it 'should return latitude' do
|
174
|
+
@p1.latitude.should == 50
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#[]' do
|
180
|
+
before :each do
|
181
|
+
@p1 = GeoPoint.new 50, 5
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'index of 0 should return latitude' do
|
185
|
+
@p1[0].should == 50
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'index of 1 should return longitude' do
|
189
|
+
@p1[1].should == 5
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'index of 2 should raise error' do
|
193
|
+
lambda {@p1[2]}.should raise_error
|
194
|
+
end
|
195
|
+
|
196
|
+
it ':lat should return latitude' do
|
197
|
+
@p1[:lat].should == 50
|
198
|
+
end
|
199
|
+
|
200
|
+
it ':long should return longitude' do
|
201
|
+
@p1[:long].should == 5
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe '#to_arr' do
|
206
|
+
before :each do
|
207
|
+
@p1 = GeoPoint.new 50, 5
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should return GeoPoint as an array depending on state of reverse_arr' do
|
211
|
+
@p1.to_arr.should == [50, 5]
|
212
|
+
end
|
213
|
+
|
214
|
+
describe '#reverse_arr!' do
|
215
|
+
it 'should reverse the array returned by #to_arr to [lng, lat]' do
|
216
|
+
@p1.reverse_arr!
|
217
|
+
@p1.to_arr.should == [5, 50]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe '#reverse_arr!' do
|
222
|
+
it 'should turn effect of #to_arr back to normal [lat, lng]' do
|
223
|
+
@p1.normal_arr!
|
224
|
+
@p1.to_arr.should == [50, 5]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|