vincenty 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/History.txt +22 -0
- data/Manifest.txt +1 -1
- data/{README.txt → README.md} +23 -38
- data/Rakefile +10 -7
- data/lib/angle.rb +168 -121
- data/lib/coordinate.rb +8 -3
- data/lib/core_extensions.rb +8 -7
- data/lib/latitude.rb +10 -11
- data/lib/longitude.rb +7 -6
- data/lib/track_and_distance.rb +5 -2
- data/lib/vincenty.rb +12 -13
- data/test/ts_angle.rb +29 -22
- data/test/ts_coordinate.rb +1 -1
- data/test/ts_latitude.rb +6 -6
- data/test/ts_longitude.rb +6 -6
- data/test/ts_track_and_distance.rb +4 -4
- data/test/ts_vincenty.rb +4 -4
- metadata +94 -41
data/.gemtest
ADDED
File without changes
|
data/History.txt
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 1.0.1 / 2009-03-03
|
2
|
+
|
3
|
+
* 1 major enhancement
|
4
|
+
|
5
|
+
* Birthday!
|
6
|
+
|
7
|
+
commit 1af085d57d8f5486b31fef3e018fa75a1e634028
|
8
|
+
Author: rbur004 <rob@burrowes.org>
|
9
|
+
Date: Fri Jan 1 11:10:33 2010 +1300
|
10
|
+
|
11
|
+
Found the error generating gems with gem 1.3.5 and Hoe. The url in README.txt should not have quotes around it.
|
12
|
+
|
13
|
+
Also the new way to specify the URL in the Rakefile is to use self.url. Kind of ugly really.
|
14
|
+
|
15
|
+
Also noted that Hoe is adding itself as a dependency in the gemspec, which it is not.
|
16
|
+
|
17
|
+
commit acc43b4a37c6cf878b9415524b8b720d46fec48b
|
18
|
+
Author: rbur004 <rob@burrowes.org>
|
19
|
+
Date: Thu Dec 31 23:39:08 2009 +1300
|
20
|
+
|
21
|
+
Commented out the require 'lib/vincenty.rb' as it is no longer required
|
22
|
+
|
1
23
|
commit e8d8d9bce43f58b0bd487e48308f170ca48c380d
|
2
24
|
Author: rbur004 <rob@burrowes.org>
|
3
25
|
Date: Thu Dec 31 22:50:16 2009 +1300
|
data/Manifest.txt
CHANGED
data/{README.txt → README.md}
RENAMED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
1
|
+
# Vincenty
|
2
2
|
|
3
|
-
*
|
3
|
+
* http://rbur004.github.com/vincenty/
|
4
|
+
* Source https://github.com/rbur004/vincenty
|
5
|
+
* Gem https://rubygems.org/gems/vincenty
|
4
6
|
|
5
|
-
|
7
|
+
## DESCRIPTION:
|
6
8
|
|
7
9
|
* Vincenty wrote an algorithm for calculating the bearing and distance between two coordinates on the earth
|
8
10
|
and an algorithm for finding a second coordinate, given a starting coordinate, bearing and destination.
|
@@ -27,13 +29,13 @@
|
|
27
29
|
|
28
30
|
** T Vincenty, "Direct and Inverse Solutions of Geodesics on the Ellipsoid with application of nested equations", Survey Review, vol XXII no 176, 1975 http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
|
29
31
|
|
30
|
-
|
32
|
+
## FEATURES/PROBLEMS:
|
31
33
|
|
32
34
|
* None that I yet know of :)
|
33
35
|
|
34
|
-
|
36
|
+
## SYNOPSIS:
|
35
37
|
|
36
|
-
flindersPeak = Vincenty.new("-37°57′3.72030
|
38
|
+
flindersPeak = Vincenty.new("-37°57′3.72030''", "144°25′29.52440''" )
|
37
39
|
buninyong = Vincenty.new("-37° 39' 10.15610''", "143° 55' 35.38390''")
|
38
40
|
track_and_bearing = flindersPeak.distanceAndAngle( buninyong )
|
39
41
|
puts track_and_bearing
|
@@ -45,15 +47,15 @@
|
|
45
47
|
#Angles is the parent class of Latitude and Longitude
|
46
48
|
Angle.new("-37°01′.125").strf( "The angle is %d°%2m′%2.5s″%N" ) -> "The angle is 37°01′07.50000″S"
|
47
49
|
|
48
|
-
|
50
|
+
## REQUIREMENTS:
|
49
51
|
|
50
52
|
* require 'rubygems'
|
51
53
|
|
52
|
-
|
54
|
+
## INSTALL:
|
53
55
|
|
54
56
|
* sudo gem install vincenty
|
55
57
|
|
56
|
-
|
58
|
+
## LICENSE:
|
57
59
|
|
58
60
|
Code unique to this implementation of Vincentys algrithm is distributed under the Ruby License.
|
59
61
|
|
@@ -65,42 +67,25 @@ Copyright (c) 2009
|
|
65
67
|
|
66
68
|
2. You may modify your copy of the software in any way, provided that
|
67
69
|
you do at least ONE of the following:
|
70
|
+
* place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software.
|
71
|
+
* use the modified software only within your corporation or organization.
|
72
|
+
* rename any non-standard executables so the names do not conflict with standard executables, which must also be provided.
|
73
|
+
* make other distribution arrangements with the author.
|
68
74
|
|
69
|
-
|
70
|
-
|
71
|
-
modifications to Usenet or an equivalent medium, or by allowing
|
72
|
-
the author to include your modifications in the software.
|
73
|
-
|
74
|
-
b) use the modified software only within your corporation or
|
75
|
-
organization.
|
76
|
-
|
77
|
-
c) rename any non-standard executables so the names do not conflict
|
78
|
-
with standard executables, which must also be provided.
|
79
|
-
|
80
|
-
d) make other distribution arrangements with the author.
|
81
|
-
|
82
|
-
3. You may distribute the software in object code or executable
|
83
|
-
form, provided that you do at least ONE of the following:
|
84
|
-
|
85
|
-
a) distribute the executables and library files of the software,
|
75
|
+
3. You may distribute the software in object code or executable form, provided that you do at least ONE of the following:
|
76
|
+
* distribute the executables and library files of the software,
|
86
77
|
together with instructions (in the manual page or equivalent)
|
87
78
|
on where to get the original distribution.
|
88
|
-
|
89
|
-
b) accompany the distribution with the machine-readable source of
|
79
|
+
* accompany the distribution with the machine-readable source of
|
90
80
|
the software.
|
91
|
-
|
92
|
-
c) give non-standard executables non-standard names, with
|
81
|
+
* give non-standard executables non-standard names, with
|
93
82
|
instructions on where to get the original software distribution.
|
94
|
-
|
95
|
-
d) make other distribution arrangements with the author.
|
83
|
+
* make other distribution arrangements with the author.
|
96
84
|
|
97
85
|
4. You may modify and include the part of the software into any other
|
98
|
-
software (possibly commercial). But some files
|
99
|
-
|
100
|
-
|
101
|
-
They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
|
102
|
-
files under the ./missing directory. See each file for the copying
|
103
|
-
condition.
|
86
|
+
software (possibly commercial). But some files or libraries used by
|
87
|
+
code in this distribution may not written by the author, so that they
|
88
|
+
are not under these terms.
|
104
89
|
|
105
90
|
5. The scripts and library files supplied as input to or produced as
|
106
91
|
output from the software do not automatically fall under the
|
data/Rakefile
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
# -*- ruby -*-
|
3
3
|
|
4
|
-
|
4
|
+
require 'rubygems'
|
5
5
|
require 'hoe'
|
6
6
|
#require 'lib/vincenty.rb'
|
7
|
-
|
7
|
+
Hoe.plugin :yard
|
8
8
|
|
9
9
|
Hoe.spec 'vincenty' do
|
10
10
|
self.rubyforge_name = "vincenty"
|
11
|
-
developer( "Rob Burrowes","
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
self.developer( "Rob Burrowes","r.burrowes@auckland.ac.nz")
|
12
|
+
|
13
|
+
self.yard_title = 'Vincenty'
|
14
|
+
self.yard_options = ['--markup', 'markdown', '--protected']
|
15
|
+
#self.url = "http://www.wikarekare.org"
|
16
|
+
#self.summary = "Vincenty Algorithm for Distance, Bearing between Map Coordinates."
|
17
|
+
#self.description = s.paragraphs_of('README.txt', 1..4).join("\n\n")
|
18
|
+
#self.remote_rdoc_dir = '' # Release to root
|
16
19
|
end
|
17
20
|
|
data/lib/angle.rb
CHANGED
@@ -1,140 +1,171 @@
|
|
1
|
+
require 'core_extensions.rb'
|
2
|
+
|
1
3
|
#Class Angle is a utility class that allows
|
2
|
-
# * Angle arithmetic
|
3
|
-
# * Angle comparison
|
4
|
+
# * Angle arithmetic ( +,-,*,/,**,% and unary +,-)
|
5
|
+
# * Angle comparison ( <=>, hence, <, >, >=, <=, == )
|
4
6
|
# * Conversion to and from degrees and radians
|
5
7
|
# * Conversion to string as radians or DMS format
|
6
8
|
|
7
|
-
require 'core_extensions.rb'
|
8
|
-
|
9
9
|
class Angle
|
10
10
|
include Comparable
|
11
11
|
|
12
|
-
#Provides test for Module Comparable
|
13
|
-
def <=>(v)
|
14
|
-
if v.class == Angle
|
15
|
-
@value <=> v.value
|
16
|
-
else
|
17
|
-
@value <=> v
|
18
|
-
end
|
19
|
-
end
|
20
12
|
|
21
|
-
attr_accessor :
|
13
|
+
attr_accessor :angle #stored in radians
|
14
|
+
alias :value :angle #Older version of angle used volue rather than angle
|
15
|
+
alias :value= :angle= #Older version of angle used volue rather than angle
|
22
16
|
|
23
|
-
#
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
17
|
+
#angle may be anything that has a to_f and to_radians.
|
18
|
+
#The Default for angle is degrees, but internally @angle is stored in radians.
|
19
|
+
#if radians is passed true or :radians, then angle is in radians, not degrees.
|
20
|
+
def initialize(angle=0, radians=false)
|
21
|
+
#assumes that we are getting an angle in degrees.
|
22
|
+
if radians == true || radians == :radians
|
23
|
+
@angle = angle.to_f #works for String, Fixed, other Angles and for Float.
|
29
24
|
else
|
30
|
-
if
|
31
|
-
@
|
25
|
+
if angle.class == Array
|
26
|
+
@angle = self.class.decimal_deg(*angle).to_radians #Wild assumption that the array is deg,min,sec.
|
32
27
|
else
|
33
|
-
@
|
28
|
+
@angle = angle.to_radians #we have a String and Numeric class version of this. Another Angle will work too.
|
34
29
|
end
|
35
30
|
end
|
36
31
|
end
|
37
32
|
|
38
|
-
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
33
|
+
|
34
|
+
#Class level function that converts 4 arguments into decimal degrees.
|
35
|
+
#Dircection is one of 'N', 'S', 'E', 'W'.
|
36
|
+
# direction is optional, as you might want to specify a negative value for degrees
|
37
|
+
# @return [Float] signed decimal degrees.
|
38
|
+
def self.decimal_deg(degrees=0, minutes=0, seconds=0, direction='N')
|
39
|
+
s = { 'N'=>1, 'S'=>-1, 'E'=>1, 'W'=>-1, 'n'=>1, 's'=>-1, 'e'=>1, 'w'=>-1 }
|
40
|
+
sign = s[direction]
|
41
|
+
sign = sign == nil ? 1 : sign #Assume 'N' or 'E' if the direction is not set.
|
42
|
+
#Shouldn't have a negative value for degrees if the direction is specified.
|
43
|
+
#I am defaulting to the degrees sign if it is set, otherwise the direction given
|
44
|
+
sign = degrees.sign == -1 || (degrees == 0 && (minutes < 0 || (minutes == 0 && seconds < 0))) ? -1 : sign
|
45
|
+
sign * (degrees.abs + minutes/60.0 + seconds/3600.0)
|
46
|
+
end
|
47
|
+
|
48
|
+
#Class level function that takes an array of [degress,minutes, seconds, direction]
|
49
|
+
# and returns decimal degrees
|
50
|
+
# @return [Float] signed decimal degrees.
|
51
|
+
def self.decimal_deg_from_ary(a)
|
52
|
+
self.decimal_deg(*a)
|
48
53
|
end
|
49
54
|
|
50
|
-
#Class level utility function to return the
|
51
|
-
#Assumes decimal degress unless radians == true
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
+
#Class level utility function to return the angle as deg,min,sec
|
56
|
+
#Assumes decimal degress unless radians == true .
|
57
|
+
# @return [Array] of signed deg, min, sec.
|
58
|
+
#Nb. * That min will be negative if the angle is negative and deg == 0
|
59
|
+
# * That sec will be negative if the angle is negative and deg == 0 && min == 0
|
60
|
+
def self.dms(angle, radians = false)
|
61
|
+
angle = angle.to_d if radians == true || radians == :radians
|
62
|
+
v = angle.abs
|
55
63
|
deg = v.floor
|
56
|
-
min = ((v
|
57
|
-
sec = ((v
|
64
|
+
min = ((v-deg)*60.0).floor
|
65
|
+
sec = ((v-deg-min/60.0)*3600.0)
|
58
66
|
|
59
|
-
if
|
60
|
-
if
|
61
|
-
|
67
|
+
if angle < 0
|
68
|
+
if deg == 0
|
69
|
+
if min == 0
|
70
|
+
sec = -sec
|
71
|
+
else
|
72
|
+
min = -min
|
73
|
+
end
|
62
74
|
else
|
63
|
-
|
75
|
+
deg = -deg
|
64
76
|
end
|
65
77
|
end
|
78
|
+
|
66
79
|
return deg,min,sec
|
67
80
|
end
|
68
81
|
|
69
82
|
#Class level function equivalent to Angle.new(r, true)
|
70
|
-
#
|
83
|
+
# @return [Angle]
|
71
84
|
def self.radians(r=0) #passed in radians.
|
72
85
|
self.new(r.to_f, true) #Nb. self is Angle, be we don't Angle.new, as we want subclasses to return their class, not Angle.
|
73
86
|
end
|
74
87
|
|
75
88
|
#Class level function equivalent to Angle.new(d, false) or just Angle.new(d)
|
76
|
-
#
|
89
|
+
# @return [Angle]
|
77
90
|
def self.degrees(d=0) #passed in degrees.
|
78
91
|
self.new(d.to_radians, true)
|
79
92
|
end
|
80
93
|
|
94
|
+
#Provides test for Module Comparable, giving us <,>,<=,>=,== between angles
|
95
|
+
# @return [true,false]
|
96
|
+
def <=>(angle)
|
97
|
+
if angle.class == Angle
|
98
|
+
@angle <=> angle.angle
|
99
|
+
else
|
100
|
+
@angle <=> angle
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
81
104
|
#unary +
|
82
|
-
#
|
105
|
+
# @return [Angle] equivalent to @angle
|
83
106
|
def +@
|
84
|
-
self.class.radians(@
|
107
|
+
self.class.radians(@angle) #Nb. Not Angle.new, as we want subclasses to return their class, not Angle.
|
85
108
|
end
|
86
109
|
|
87
110
|
#Unary -
|
88
|
-
#
|
111
|
+
# @return [Angle] -@angle
|
89
112
|
def -@
|
90
|
-
self.class.radians(-@
|
113
|
+
self.class.radians(-@angle)
|
91
114
|
end
|
92
115
|
|
93
|
-
#
|
94
|
-
|
95
|
-
|
116
|
+
# Binary addition operator. Can add angles and numbers, or two angles.
|
117
|
+
# @return [Angle]
|
118
|
+
def +(angle)
|
119
|
+
self.class.radians(@angle + angle)
|
96
120
|
end
|
97
121
|
|
98
|
-
#
|
99
|
-
|
100
|
-
|
122
|
+
# Binary subtraction operator. Can add angles and numbers, or two angles.
|
123
|
+
# @return [Angle]
|
124
|
+
def -(angle)
|
125
|
+
self.class.radians(@angle - angle)
|
101
126
|
end
|
102
127
|
|
103
|
-
#
|
104
|
-
|
105
|
-
|
128
|
+
# Binary multiply operator. Can add angles and numbers, or two angles.
|
129
|
+
# @return [Angle]
|
130
|
+
def *(angle)
|
131
|
+
self.class.radians(@angle * angle)
|
106
132
|
end
|
107
133
|
|
108
|
-
#
|
109
|
-
|
110
|
-
|
134
|
+
# Binary power of operator. Can add angles and numbers, or two angles.
|
135
|
+
# @return [Angle]
|
136
|
+
def **(angle)
|
137
|
+
self.class.radians(@angle ** angle)
|
111
138
|
end
|
112
139
|
|
113
|
-
#
|
114
|
-
|
115
|
-
|
140
|
+
# Binary division operator. Can add angles and numbers, or two angles.
|
141
|
+
# @return [Angle]
|
142
|
+
def /(angle)
|
143
|
+
self.class.radians(@angle / angle)
|
116
144
|
end
|
117
145
|
|
118
|
-
#
|
119
|
-
|
120
|
-
|
146
|
+
# Binary mod operator. Can add angles and numbers, or two angles.
|
147
|
+
# @return [Angle]
|
148
|
+
def %(angle)
|
149
|
+
self.class.radians(@angle % angle)
|
121
150
|
end
|
122
151
|
|
123
|
-
#
|
152
|
+
# @return [Float] angle in degrees
|
124
153
|
def to_degrees
|
125
|
-
@
|
154
|
+
@angle.to_degrees
|
126
155
|
end
|
127
156
|
|
157
|
+
# @return [Float] angle in degrees
|
128
158
|
alias to_d to_degrees
|
129
159
|
|
130
160
|
#Returns: angle in radians
|
131
161
|
def to_radians
|
132
|
-
@
|
162
|
+
@angle
|
133
163
|
end
|
134
164
|
|
135
165
|
alias to_r to_radians
|
136
166
|
|
137
|
-
#Returns
|
167
|
+
# Returns @angle as decimal_degrees
|
168
|
+
# @return [Array] of signed Floats: degrees,minutes,seconds
|
138
169
|
#Nb. * That min will be negative if the angle is negative and deg == 0
|
139
170
|
# * That sec will be negative if the angle is negative and deg == 0 && min == 0
|
140
171
|
def to_dms
|
@@ -143,7 +174,7 @@ class Angle
|
|
143
174
|
min = ((d-deg)*60).floor
|
144
175
|
sec = ((d-deg-min/60.0)*3600.0)
|
145
176
|
|
146
|
-
if @
|
177
|
+
if @angle < 0
|
147
178
|
if deg == 0
|
148
179
|
if min == 0
|
149
180
|
sec = -sec
|
@@ -158,76 +189,78 @@ class Angle
|
|
158
189
|
return deg, min, sec
|
159
190
|
end
|
160
191
|
|
161
|
-
#
|
192
|
+
# @return [Float] the angle in radians as a float (equivalent to to_radians)
|
162
193
|
alias to_f to_radians
|
163
194
|
|
164
|
-
#
|
195
|
+
# @return [Fixnum] the angle truncated to an integer, in radians.
|
165
196
|
def to_i
|
166
197
|
to_radians.to_i
|
167
198
|
end
|
168
199
|
|
200
|
+
# @return [Fixnum] the angle truncated to an integer, in radians.
|
169
201
|
alias to_int to_i
|
170
202
|
|
171
|
-
|
172
|
-
|
203
|
+
# @return [Array] the angle parameter as a Float and the @angle parameter from this class.
|
204
|
+
def coerce(angle)
|
205
|
+
[Float(angle), @angle]
|
173
206
|
end
|
174
207
|
|
175
|
-
#
|
208
|
+
# @return [Fixnum] the sign of the angle. 1 for positive, -1 for negative.
|
176
209
|
def sign
|
177
|
-
@
|
210
|
+
@angle.sign
|
178
211
|
end
|
179
212
|
|
180
|
-
#
|
213
|
+
# @return [Float] the absolute angle of the angle in radians
|
181
214
|
def abs
|
182
|
-
@
|
215
|
+
@angle.abs
|
183
216
|
end
|
184
217
|
|
185
|
-
#
|
218
|
+
# @return [Float] angle as compass bearing in radians.
|
186
219
|
#Compass bearings are clockwise, Math angles are counter clockwise.
|
187
220
|
def to_bearing
|
188
|
-
self.class.new(Math::PI * 2 - @
|
221
|
+
self.class.new(Math::PI * 2 - @angle,true)
|
189
222
|
end
|
190
223
|
|
191
|
-
#
|
224
|
+
# @return [Float] the reverse angle in radians. i.e. angle + PI (or angle + 180 degrees)
|
192
225
|
def reverse
|
193
|
-
if (
|
194
|
-
|
226
|
+
if (angle = @angle + Math::PI) > Math::PI * 2
|
227
|
+
angle -= Math::PI * 2
|
195
228
|
end
|
196
|
-
return self.class.new(
|
229
|
+
return self.class.new(angle,true)
|
197
230
|
end
|
198
231
|
|
199
232
|
|
200
|
-
#
|
233
|
+
# @return [String] angle in radians as a string.
|
201
234
|
def to_s(fmt = nil)
|
202
235
|
return to_radians.to_s if(fmt == nil)
|
203
236
|
return strf(fmt)
|
204
237
|
end
|
205
238
|
|
206
239
|
#formated output of the angle.
|
207
|
-
#The default format is a signed deg
|
240
|
+
#The default format is a signed deg 0minutes'seconds" with leading 0's in the minutes and seconds and 4 decimal places for seconds.
|
208
241
|
#formats are:
|
209
242
|
# * %wd output the degrees as an integer.
|
210
243
|
# ** where w is 0, 1, 2 or 3 and represents the field width.
|
211
244
|
# *** 1 is the default, which indicates that at least 1 digit is displayed
|
212
|
-
# *** 2 indicates that at least 2 digits are displayed. 1 to 9 will be displayed as 01
|
213
|
-
# *** 3 indicates that at least 4 digits are displayed. 10 to 99 will be displayed as 010
|
245
|
+
# *** 2 indicates that at least 2 digits are displayed. 1 to 9 will be displayed as 01 0 to 09 0
|
246
|
+
# *** 3 indicates that at least 4 digits are displayed. 10 to 99 will be displayed as 010 0 to 099 0
|
214
247
|
#
|
215
248
|
# * %w.pD outputs degrees as a float.
|
216
|
-
#
|
249
|
+
# * p is the number of decimal places.
|
217
250
|
#
|
218
251
|
# * %wm output minutes as an integer.
|
219
|
-
#
|
252
|
+
# * where the width w is 0, 1 , 2 with similar meaning to %d. p is again the number of decimal places.
|
220
253
|
#
|
221
|
-
# * %w.pM outputs minutes as a float .e.g. 01.125
|
222
|
-
#
|
254
|
+
# * %w.pM outputs minutes as a float .e.g. 01.125'.
|
255
|
+
# * p is the number of decimal places.
|
223
256
|
#
|
224
257
|
# * %wW outputs secs/60 as a float without the leading '0.'.
|
225
|
-
# Used with %m like this %2m
|
226
|
-
# e.g. -37
|
227
|
-
#
|
258
|
+
# Used with %m like this %2m'%4W , to get minute marker before the decimal places.
|
259
|
+
# e.g. -37 001'.1167 rather than -37 001.1167'
|
260
|
+
# * p is the number of decimal places.
|
228
261
|
#
|
229
262
|
# * %w.ps output seconds as a float.
|
230
|
-
#
|
263
|
+
# * where the width w is 1 , 2 with similar meaning to %d. p is again the number of decimal places.
|
231
264
|
#
|
232
265
|
# * %N outputs N if the angle is positive and S if the angle is negative.
|
233
266
|
#
|
@@ -238,7 +271,8 @@ class Angle
|
|
238
271
|
# * %% outputs %
|
239
272
|
#
|
240
273
|
# Other strings in the format are printed as is.
|
241
|
-
|
274
|
+
# @return [String]
|
275
|
+
def strf(fmt="%d %2m'%2.4s\"")
|
242
276
|
tokens = fmt.scan(/%[0-9\.]*[%dmsDMNErW]|[^%]*/)
|
243
277
|
have_dir = have_dms = false
|
244
278
|
tokens.collect! do |t|
|
@@ -288,7 +322,7 @@ class Angle
|
|
288
322
|
when 'd'
|
289
323
|
s += s_int(deg, t[1], have_dir)
|
290
324
|
when 'D'
|
291
|
-
s += s_float(@
|
325
|
+
s += s_float(@angle.to_d, t[1], t[2], have_dir)
|
292
326
|
when 'm'
|
293
327
|
s += s_int(min, t[1], have_dir)
|
294
328
|
when 'M'
|
@@ -298,11 +332,11 @@ class Angle
|
|
298
332
|
when 's'
|
299
333
|
s += s_float(sec, t[1], t[2], have_dir)
|
300
334
|
when 'r'
|
301
|
-
s += s_float(@
|
335
|
+
s += s_float(@angle, t[1], t[2], have_dir)
|
302
336
|
when 'N'
|
303
|
-
s += (@
|
337
|
+
s += (@angle < 0 ? 'S' : 'N')
|
304
338
|
when 'E'
|
305
|
-
s += (@
|
339
|
+
s += (@angle < 0 ? 'W' : 'E')
|
306
340
|
end
|
307
341
|
else
|
308
342
|
s += t[1] #the fillers.
|
@@ -313,37 +347,50 @@ class Angle
|
|
313
347
|
end
|
314
348
|
|
315
349
|
private
|
316
|
-
|
350
|
+
#Output angle_dec as a string to the number of decimal places specified by places.
|
351
|
+
#Assumes the angle is 0 <= angle_dec < 1
|
352
|
+
#No leading '0' is output. The string starts with a '.'
|
353
|
+
#If ploces is -1, then all decimal places are returned.
|
354
|
+
# @return [String]
|
355
|
+
def s_places(angle_dec, places)
|
317
356
|
if places != -1
|
318
|
-
|
319
|
-
f = ".%0#{places > 0 ? places : ''}d"
|
320
|
-
f % dec_p
|
357
|
+
places > 0 ? ".%0#{places}d" % (angle_dec * 10 ** places).round : ''
|
321
358
|
else
|
322
|
-
|
359
|
+
angle_dec.to_s[1..-1] #Output all decimal places stripping the leading 0
|
323
360
|
end
|
324
361
|
end
|
325
362
|
|
326
|
-
|
327
|
-
|
328
|
-
s_places(v_dec, places)
|
329
|
-
end
|
330
|
-
|
331
|
-
#Prints fixed width decimal portion with leading 0s to get at least the width specified
|
363
|
+
#return the angle as a string with fixed width decimal portion with leading 0s
|
364
|
+
#to get at least the width specified
|
332
365
|
#Prints the number of places after the decimal point rounded to places places.
|
333
366
|
#-1 width means no width format
|
334
367
|
#-1 places means print all decimal places.
|
335
368
|
#abs means print the absolute value.
|
336
|
-
|
337
|
-
|
369
|
+
# @return [String]
|
370
|
+
def s_float(angle, width, places, abs)
|
371
|
+
angle_int, angle_dec = angle.abs.divmod(1)
|
338
372
|
f = "%0#{width > 0 ? width : ''}d"
|
339
|
-
s = (abs == false &&
|
340
|
-
s += f %
|
373
|
+
s = (abs == false && angle.sign == -1) ? '-' : '' #catch the case of -0
|
374
|
+
s += f % angle.abs + s_places(angle_dec, places)
|
341
375
|
end
|
342
376
|
|
343
|
-
|
377
|
+
#Return the integer part of angle as a string of fixed width
|
378
|
+
#If abs == true, then return the absolute value.
|
379
|
+
# @return [Fixnum]
|
380
|
+
def s_int(angle, width, abs)
|
344
381
|
f = "%0#{width > 0 ? width : ''}d"
|
345
|
-
s = (abs == false &&
|
346
|
-
s += f %
|
382
|
+
s = (abs == false && angle.sign == -1) ? '-' : '' #catch the case of -0
|
383
|
+
s += f % angle.abs
|
384
|
+
end
|
385
|
+
|
386
|
+
#Return the fractional part of angle as a string,
|
387
|
+
#to the number of decimal places specified by 'places'.
|
388
|
+
#No leading '0' is output. The string starts with a '.'
|
389
|
+
#If ploces is -1, then all decimal places are returned.
|
390
|
+
# @return [String]
|
391
|
+
def s_only_places(angle, places)
|
392
|
+
angle_int, angle_dec = angle.abs.divmod(1)
|
393
|
+
s_places(angle_dec, places)
|
347
394
|
end
|
348
395
|
end
|
349
396
|
|
data/lib/coordinate.rb
CHANGED
@@ -1,29 +1,34 @@
|
|
1
|
-
#Holds both latitude and longitude, and the altitude at that point
|
2
1
|
|
3
2
|
require 'angle.rb'
|
4
3
|
|
4
|
+
#Holds the latitude, longitude, and the altitude for the coordinate
|
5
5
|
class Coordinate
|
6
6
|
attr_accessor :latitude, :longitude, :altitude
|
7
|
+
|
7
8
|
#latitude and longitude can be Strings or Numeric, or anything else with to_radians and to_f
|
8
|
-
#latitude and longitude are in degrees unless radians == true
|
9
|
+
#latitude and longitude are in degrees unless radians == true (or set to :radians)
|
9
10
|
def initialize(latitude=0, longitude=0, altitude=0, radians = false)
|
10
11
|
@latitude = Latitude.new(latitude,radians)
|
11
12
|
@longitude = Longitude.new(longitude,radians)
|
12
13
|
@altitude = altitude.to_f
|
13
14
|
end
|
14
15
|
|
15
|
-
#Should add a format string to this.
|
16
16
|
#Returns: Latitude longitude and altitude as a single string.
|
17
|
+
#Should add an optional format string to this.
|
17
18
|
def to_s
|
18
19
|
"#{@latitude.to_s } #{@longitude.to_s} #{@altitude}m"
|
19
20
|
end
|
20
21
|
|
22
|
+
#Return coordinate as a 3 member array, with
|
23
|
+
#members, latitude, longitude and altitude
|
21
24
|
def to_ary
|
22
25
|
[ @latitude, @longitude, @altitude ]
|
23
26
|
end
|
24
27
|
|
25
28
|
alias to_a to_ary
|
26
29
|
|
30
|
+
#return coordinate a 3 member hash with
|
31
|
+
#keys :latitude, :longitude, and :altitude
|
27
32
|
def to_hash
|
28
33
|
{ :latitude => @latitude, :longitude => @longitude, :altitude => @altitude }
|
29
34
|
end
|
data/lib/core_extensions.rb
CHANGED
@@ -13,6 +13,7 @@ class Numeric
|
|
13
13
|
self * 180 / Math::PI
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
16
17
|
#Converts degrees to Radians
|
17
18
|
#if optional argument mod == true, then applies % Math::PI
|
18
19
|
#Returns: radians
|
@@ -49,22 +50,22 @@ end
|
|
49
50
|
#Extends String to to_dec_degrees, add to_r and to_d
|
50
51
|
class String
|
51
52
|
#string expected to be degrees, returns decimal degrees.
|
52
|
-
#common forms are S37
|
53
|
+
#common forms are S37 001'7.5'', 37 001'7.5''S , -37 001'7.5'', -37 0 1.512'. -37.01875 0, 37 001'.512S, S37 001'.512, ...
|
53
54
|
#Returns: angle in decimal degrees
|
54
55
|
def to_dec_degrees
|
55
|
-
#reorder 37
|
56
|
-
s = self.gsub(/([0-9])([
|
57
|
-
#add in minutes and seconds to get 3 values 'deg 0 0'from S37
|
56
|
+
#reorder 37 001'.512S, S37 001'.512 into 37 001.512'S, S37 001.512' respectively
|
57
|
+
s = self.gsub(/([0-9])([''])\.([0-9]+)/, '\1.\3\2')
|
58
|
+
#add in minutes and seconds to get 3 values 'deg 0 0'from S37 0, 37 0S
|
58
59
|
s.gsub!(/^([^0-9\.\-]*)([0-9\-\.]+)([^0-9\-\.]*)$/, '\1\2\3 0 0\5')
|
59
|
-
#add in seconds get 3 values 'deg min 0' from S37
|
60
|
+
#add in seconds get 3 values 'deg min 0' from S37 01.512', 37 01.512'S
|
60
61
|
s.gsub!(/^([^0-9\.\-]*)([0-9\-\.]+)([^0-9\-\.]+)([0-9\-\.]+)([^0-9\-\.]*)$/, '\1\2\3\4 0\5')
|
61
62
|
|
62
|
-
#look for anything of the form S37
|
63
|
+
#look for anything of the form S37 001'7.5'', S37 01.512', S37.01875 0, ...
|
63
64
|
s.scanf("%[NSEW]%f%[^0-9-]%f%[^0-9-]%f") do |direction, deg, sep1, min, sep2, sec|
|
64
65
|
return Angle.decimal_deg( deg, min, sec, direction)
|
65
66
|
end
|
66
67
|
|
67
|
-
#look for anything of the form 37
|
68
|
+
#look for anything of the form 37 001'7.5''S , -37 001'7.5'', -37 0 1.512'. -37.01875 0, ...
|
68
69
|
s.scanf("%f%[^0-9-]%f%[^0-9-]%f%[^NSEW]%[NSEW]") do |deg, sep1, min, sep2, sec, sep3, direction|
|
69
70
|
return Angle.decimal_deg( deg, min, sec, direction)
|
70
71
|
end
|
data/lib/latitude.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
#Subclass of Angle to add in special treatment of to_d, to_r , to_s
|
2
1
|
|
3
2
|
require 'angle.rb'
|
4
3
|
|
4
|
+
#Subclass of Angle to add in special treatment of to_d, to_r , to_s
|
5
|
+
#Latitude degrees are between -PI and PI, South to North (+/- 90 degrees)
|
6
|
+
|
5
7
|
class Latitude < Angle
|
6
|
-
|
8
|
+
|
7
9
|
#Returns angle as degrees in range -90 and 90
|
8
10
|
def to_degrees
|
9
|
-
#longitude's are -180 to 180 for west to east
|
10
11
|
degrees = super
|
11
12
|
case
|
12
13
|
when degrees > 270 ; -(360 - degrees)
|
@@ -17,22 +18,20 @@ class Latitude < Angle
|
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
|
-
#Latitude degrees are between -PI and PI, South to North
|
21
21
|
#Returns: angle as degrees in range -PI and PI
|
22
22
|
def to_radians
|
23
|
-
#longitude's are -180 to 180 for west to east
|
24
23
|
case
|
25
|
-
when @
|
26
|
-
when @
|
27
|
-
when @
|
28
|
-
when @
|
29
|
-
else @
|
24
|
+
when @angle > 3*Math::PI/2 ; @angle - Math::PI * 2
|
25
|
+
when @angle > Math::PI ; Math::PI - @angle
|
26
|
+
when @angle > Math::PI/2 ; Math::PI - @angle
|
27
|
+
when @angle < -Math::PI/2 ; -Math::PI - @angle
|
28
|
+
else @angle
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
33
32
|
#Returns: angle as string in degrees minutes seconds direction.
|
34
33
|
#A South angle is negative, North is Positive.
|
35
|
-
def to_s(fmt=
|
34
|
+
def to_s(fmt="%2d %2m'%2.4s\"%N")
|
36
35
|
super(fmt)
|
37
36
|
end
|
38
37
|
|
data/lib/longitude.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
#Subclass of Angle to add in special treatment of to_d, to_r and to_s
|
2
1
|
|
3
2
|
require 'angle.rb'
|
4
3
|
|
4
|
+
#Subclass of Angle to add in special treatment of to_d, to_r and to_s
|
5
|
+
#Longitude degrees are between -2PI and 2PI, West to East (+/- 180 degrees)
|
6
|
+
|
5
7
|
class Longitude < Angle
|
6
|
-
|
8
|
+
|
7
9
|
#Returns angle as degrees in range -180 and 180
|
8
10
|
def to_degrees
|
9
11
|
degrees = super
|
@@ -13,18 +15,17 @@ class Longitude < Angle
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
#Longitude degrees are between -2PI and 2PI, West to East
|
17
18
|
#Returns: angle as degrees in range -2PI and 2PI
|
18
19
|
def to_radians
|
19
20
|
case
|
20
|
-
when @
|
21
|
-
else @
|
21
|
+
when @angle > Math::PI ; @angle - 2 * Math::PI
|
22
|
+
else @angle
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
26
|
#Returns: angle as string in degrees minutes seconds direction.
|
26
27
|
#A West angle is negative, East is Positive.
|
27
|
-
def to_s(fmt=
|
28
|
+
def to_s(fmt="%3d %2m'%2.4s\"%E")
|
28
29
|
super(fmt)
|
29
30
|
end
|
30
31
|
|
data/lib/track_and_distance.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
#Holds a bearing and distance
|
2
1
|
|
3
2
|
require 'angle.rb'
|
4
3
|
|
4
|
+
#Holds a bearing and distance
|
5
5
|
class TrackAndDistance
|
6
6
|
attr_accessor :bearing, :distance
|
7
|
-
|
7
|
+
|
8
|
+
#Bearing is in degrees unless radians == true (or set to :radians).
|
8
9
|
#Bearing can be a String or Numeric or any object with to_radians and to_f
|
9
10
|
def initialize(bearing, distance, radians=false)
|
10
11
|
@bearing = Angle.new(bearing, radians)
|
@@ -23,10 +24,12 @@ class TrackAndDistance
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
#Returns an array with members bearing and distance.
|
26
28
|
def to_ary
|
27
29
|
[ @bearing, @distance ]
|
28
30
|
end
|
29
31
|
|
32
|
+
#Returns a hash with keys :bearing and :distance
|
30
33
|
def to_hash
|
31
34
|
{ :bearing => @bearing, :distance => @distance }
|
32
35
|
end
|
data/lib/vincenty.rb
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
#Vincenty's algorithms for finding the bearing and distance between two coordinates and
|
2
|
-
#for finding the latitude and longitude, given a start coordinate, distance and bearing.
|
3
|
-
#
|
4
|
-
# Coded from formulae from Wikipedia http://en.wikipedia.org/wiki/Vincenty%27s_formulae
|
5
|
-
# Modified to incorporate corrections to formulae as found in script on http://www.movable-type.co.uk/scripts/LatLongVincenty.html
|
6
|
-
# Added my Modification of the distanceAndAngle formulae to correct the compass bearing.
|
7
1
|
require 'core_extensions.rb'
|
8
2
|
require 'angle.rb'
|
9
3
|
require 'latitude.rb'
|
@@ -11,8 +5,14 @@ require 'longitude.rb'
|
|
11
5
|
require 'track_and_distance.rb'
|
12
6
|
require 'coordinate.rb'
|
13
7
|
|
8
|
+
#Vincenty's algorithms for finding the bearing and distance between two coordinates and
|
9
|
+
#for finding the latitude and longitude, given a start coordinate, distance and bearing.
|
10
|
+
#
|
11
|
+
# Coded from formulae from Wikipedia http://en.wikipedia.org/wiki/Vincenty%27s_formulae
|
12
|
+
# Modified to incorporate corrections to formulae as found in script on http://www.movable-type.co.uk/scripts/LatLongVincenty.html
|
13
|
+
# Added my Modification of the distanceAndAngle formulae to correct the compass bearing.
|
14
14
|
class Vincenty < Coordinate
|
15
|
-
VERSION = '1.0.
|
15
|
+
VERSION = '1.0.4'
|
16
16
|
|
17
17
|
def version
|
18
18
|
VERSION
|
@@ -24,7 +24,7 @@ class Vincenty < Coordinate
|
|
24
24
|
#Takes: argument p2 is target coordinate that we want the bearing to.
|
25
25
|
#Returns: TrackAndDistance object with the compass bearing and distance in meters to P2
|
26
26
|
def sphericalDistanceAndAngle( p2 )
|
27
|
-
a = 6378137 #equatorial radius in meters (
|
27
|
+
a = 6378137 #equatorial radius in meters (+/-2 m)
|
28
28
|
b = 6356752.31424518 #polar radius in meters
|
29
29
|
r = (a+b)/2 #average diametre as a rough estimate for our tests.
|
30
30
|
|
@@ -52,7 +52,7 @@ class Vincenty < Coordinate
|
|
52
52
|
#Returns: TrackAndDistance object with the compass bearing and distance in meters to P2
|
53
53
|
def distanceAndAngle( p2 )
|
54
54
|
# a, b = major & minor semiaxes of the ellipsoid
|
55
|
-
a = 6378137 #equatorial radius in meters (
|
55
|
+
a = 6378137 #equatorial radius in meters (+/-2 m)
|
56
56
|
b = 6356752.31424518 #polar radius in meters
|
57
57
|
f = (a-b)/a # flattening
|
58
58
|
|
@@ -67,7 +67,7 @@ class Vincenty < Coordinate
|
|
67
67
|
|
68
68
|
l = (lon2 - lon1).abs #difference in longitude
|
69
69
|
l = 2*Math::PI - l if l > Math::PI
|
70
|
-
u1 = Math.atan( ( 1 - f) * Math.tan( lat1 ) ) #U is
|
70
|
+
u1 = Math.atan( ( 1 - f) * Math.tan( lat1 ) ) #U is 'reduced latitude'
|
71
71
|
u2 = Math.atan( ( 1 - f) * Math.tan( lat2 ) )
|
72
72
|
sin_u1 = Math.sin(u1)
|
73
73
|
cos_u1 = Math.cos(u1)
|
@@ -121,7 +121,7 @@ class Vincenty < Coordinate
|
|
121
121
|
#Takes: TrackAndDistance object with bearing and distance.
|
122
122
|
#Returns new Vincenty object with the destination coordinates.
|
123
123
|
def sphereDestination( track_and_distance )
|
124
|
-
a = 6378137 #equatorial radius in meters (
|
124
|
+
a = 6378137 #equatorial radius in meters (+/-2 m)
|
125
125
|
b = 6356752.31424518 #polar radius in meters
|
126
126
|
r = (a+b)/2 #average diametre as a rough estimate for our tests.
|
127
127
|
|
@@ -141,10 +141,9 @@ class Vincenty < Coordinate
|
|
141
141
|
#Assumes earth is a WGS-84 Ellipsod.
|
142
142
|
#Takes: TrackAndDistance object with bearing and distance.
|
143
143
|
#Returns: new Vincenty object with the destination coordinates.
|
144
|
-
|
145
144
|
def destination( track_and_distance )
|
146
145
|
# a, b = major & minor semiaxes of the ellipsoid
|
147
|
-
a = 6378137 #equatorial radius in meters (
|
146
|
+
a = 6378137 #equatorial radius in meters (+/-2 m)
|
148
147
|
b = 6356752.31424518 #polar radius in meters
|
149
148
|
f = (a-b)/a # flattening
|
150
149
|
|
data/test/ts_angle.rb
CHANGED
@@ -5,14 +5,14 @@ class TestAngle< Test::Unit::TestCase
|
|
5
5
|
#test Angle creation
|
6
6
|
def test_angle
|
7
7
|
assert_equal(Angle.new(), 0)
|
8
|
-
assert_equal(Angle.new("S37
|
9
|
-
assert_equal(Angle.new("37
|
10
|
-
assert_equal(Angle.new("-37
|
11
|
-
assert_equal(Angle.new("-37
|
12
|
-
assert_equal(Angle.new("-37
|
13
|
-
assert_equal(Angle.new("S37
|
14
|
-
assert_equal(Angle.new("37
|
15
|
-
assert_equal(Angle.new("-37.01875
|
8
|
+
assert_equal(Angle.new("S37 01'7.5\"").to_d, -37.01875) #Leading NSEW
|
9
|
+
assert_equal(Angle.new("37 01'7.5\"S").to_d , -37.01875) #Trailing NSEW
|
10
|
+
assert_equal(Angle.new("-37 01'7.5\"").to_d, -37.01875) #Use of - rather than S or W
|
11
|
+
assert_equal(Angle.new("-37 1.125'").to_d, -37.01875) #Decimal minutes, rather than minutes and seconds.
|
12
|
+
assert_equal(Angle.new("-37 01'.125").to_d, -37.01875) #Nb. the minute marker ' between the minutes and fraction
|
13
|
+
assert_equal(Angle.new("S37 01'.125").to_d, -37.01875) #Nb. the minute marker ' between the minutes and fraction
|
14
|
+
assert_equal(Angle.new("37 01'.125S").to_d, -37.01875) #Nb. the minute marker ' between the minutes and fraction
|
15
|
+
assert_equal(Angle.new("-37.01875 ").to_d, -37.01875) #decimal degrees, rather than deg, min, sec.
|
16
16
|
assert_equal(Angle.new([-37, 1, 7.5]).to_d, -37.01875) #an array of deg,min,sec
|
17
17
|
assert_equal(Angle.new(-37.01875).to_d, -37.01875)
|
18
18
|
assert_equal(Angle.degrees(-37.01875).to_d, -37.01875)
|
@@ -20,25 +20,32 @@ class TestAngle< Test::Unit::TestCase
|
|
20
20
|
assert_equal(Angle.new(-0.646099072472651, true).to_d.round(5), -37.01875)
|
21
21
|
assert_equal(Angle.new(-0.646099072472651, true).to_r, -0.646099072472651)
|
22
22
|
assert_equal(Angle.radians(-0.646099072472651).to_d.round(5), -37.01875)
|
23
|
+
assert_equal(Angle.radians(-0.646099072472651).value, Angle.radians(-0.646099072472651).angle)
|
24
|
+
assert_equal(Angle.decimal_deg(1,2,3,'S'), -(1.0 + 2/60.0 + 3/3600.0))
|
25
|
+
assert_equal(Angle.decimal_deg(1,2,3,'E'), (1.0 + 2/60.0 + 3/3600.0))
|
26
|
+
assert_equal(Angle.decimal_deg(1,2,4,'N'), (1.0 + 2/60.0 + 4/3600.0))
|
27
|
+
assert_equal(Angle.decimal_deg(1,5,4,'W'), -(1.0 + 5/60.0 + 4/3600.0))
|
28
|
+
assert_equal(Angle.decimal_deg_from_ary([1,5,4,'W']), -(1.0 + 5/60.0 + 4/3600.0))
|
29
|
+
assert_equal(Angle.decimal_deg_from_ary(Angle.dms( -(1.0 + 5/60.0 + 1.0/3600.0) )),-(1.0 + 5/60.0 + 1.0/3600.0)) #double call, rounding error always produced a failure.
|
23
30
|
end
|
24
31
|
|
25
32
|
def test_strf
|
26
|
-
a = Angle.new("S37
|
27
|
-
assert_equal("-37
|
28
|
-
assert_equal("37
|
29
|
-
assert_equal("37
|
30
|
-
assert_equal("-37
|
31
|
-
assert_equal("-37
|
32
|
-
assert_equal("*** -37
|
33
|
-
assert_equal("-37.01875
|
33
|
+
a = Angle.new("S37 01'7.5\"")
|
34
|
+
assert_equal("-37 01'07.5000\"", a.strf) #default format of strf
|
35
|
+
assert_equal("37 01'07.50000\"S", a.strf( "%d %2m'%2.5s\"%N" ))
|
36
|
+
assert_equal("37 01'07.50000\"W", a.strf("%d %2m'%2.5s\"%E" ))
|
37
|
+
assert_equal("-37 01'07.5000\"", a.strf("%d %2m'%2.4s\"" ))
|
38
|
+
assert_equal("-37 01.1250'\n", a.strf("%d %2.4M'\n" ))
|
39
|
+
assert_equal("*** -37 01'.1250", a.strf( "*** %d %2m'%4W" )) #puting the minute ' before decimal point.
|
40
|
+
assert_equal("-37.01875 ", a.strf("%0.5D " ))
|
34
41
|
assert_equal("-0.64610 radians\n", a.strf("%0.5r radians\n" ))
|
35
42
|
|
36
|
-
assert_equal("-037
|
37
|
-
assert_equal("00
|
38
|
-
assert_equal("00
|
39
|
-
assert_equal("00
|
40
|
-
assert_equal("000
|
41
|
-
assert_equal("00
|
43
|
+
assert_equal("-037 01'7.5000\"", Angle.new("-37 01'7.5\"").to_s('%3d %2m\'%1.4s"')) #testing leading 0 with -deg, no leading 0 %s
|
44
|
+
assert_equal("00 01'07.5000\"S", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"%N')) #testing 0 degrees and leading 0 %s
|
45
|
+
assert_equal("00 -01'07.5000\"", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"')) #testing 0 degrees and -min
|
46
|
+
assert_equal("00 -01'07.5000\"", Angle.new("0 01'7.5\"S").to_s('%2d %2m\'%2.4s"') ) #test of 0 degrees, -min, no NSEW
|
47
|
+
assert_equal("000 00'07.5000\"W", Angle.new("0 0'7.5\"W").to_s('%3d %2m\'%2.4s"%E') ) #testing E W 0 deg and 0 min and -sec
|
48
|
+
assert_equal("00 00'-07.5000\"", Angle.new("0 0'7.5\"S").to_s('%2d %2m\'%2.4s"') ) #testing 0 deg and 0 min and -sec no NSEW
|
42
49
|
end
|
43
50
|
|
44
51
|
def test_operators
|
data/test/ts_coordinate.rb
CHANGED
@@ -13,6 +13,6 @@ class TestAngle< Test::Unit::TestCase
|
|
13
13
|
assert_equal(-36.9923293459124, ch[:latitude].to_d)
|
14
14
|
assert_equal(174.485341187381, ch[:longitude].to_d)
|
15
15
|
assert_equal(13.5, ch[:altitude])
|
16
|
-
assert_equal("36
|
16
|
+
assert_equal("36 59'32.3856\"S 174 29'07.2283\"E 13.5m", c.to_s)
|
17
17
|
end
|
18
18
|
end
|
data/test/ts_latitude.rb
CHANGED
@@ -3,12 +3,12 @@ require 'vincenty.rb'
|
|
3
3
|
|
4
4
|
class TestAngle< Test::Unit::TestCase
|
5
5
|
def test_strf
|
6
|
-
assert_equal("37
|
7
|
-
assert_equal("37
|
8
|
-
assert_equal("37
|
9
|
-
assert_equal("37
|
10
|
-
assert_equal("37
|
11
|
-
assert_equal("37
|
6
|
+
assert_equal("37 01'07.5000\"S", Latitude.new("S37 01'7.5\"").to_s)
|
7
|
+
assert_equal("37 01'07.5000\"S", Latitude.new("-37 01'7.5\"").to_s)
|
8
|
+
assert_equal("37 01'07.5000\"S", Latitude.new("37 01'7.5\"S").to_s)
|
9
|
+
assert_equal("37 01'07.5000\"N", Latitude.new("N37 01'7.5\"").to_s)
|
10
|
+
assert_equal("37 01'07.5000\"N", Latitude.new("37 01'7.5\"").to_s)
|
11
|
+
assert_equal("37 01'07.5000\"N", Latitude.new("37 01'7.5\"N").to_s)
|
12
12
|
end
|
13
13
|
def test_to_radians
|
14
14
|
assert_equal(Math::PI/4, Latitude.degrees(45).to_r)
|
data/test/ts_longitude.rb
CHANGED
@@ -3,12 +3,12 @@ require 'vincenty.rb'
|
|
3
3
|
|
4
4
|
class TestAngle< Test::Unit::TestCase
|
5
5
|
def test_strf
|
6
|
-
assert_equal("037
|
7
|
-
assert_equal("037
|
8
|
-
assert_equal("037
|
9
|
-
assert_equal("037
|
10
|
-
assert_equal("037
|
11
|
-
assert_equal("037
|
6
|
+
assert_equal("037 01'07.5000\"W", Longitude.new("W37 01'7.5\"").to_s)
|
7
|
+
assert_equal("037 01'07.5000\"W", Longitude.new("-37 01'7.5\"").to_s)
|
8
|
+
assert_equal("037 01'07.5000\"W", Longitude.new("37 01'7.5\"W").to_s)
|
9
|
+
assert_equal("037 01'07.5000\"E", Longitude.new("E37 01'7.5\"").to_s)
|
10
|
+
assert_equal("037 01'07.5000\"E", Longitude.new("37 01'7.5\"").to_s)
|
11
|
+
assert_equal("037 01'07.5000\"E", Longitude.new("37 01'7.5\"E").to_s)
|
12
12
|
end
|
13
13
|
def test_to_radians
|
14
14
|
assert_equal(Math::PI/4, Longitude.degrees(45).to_r)
|
@@ -4,13 +4,13 @@ require 'vincenty.rb'
|
|
4
4
|
class TestAngle< Test::Unit::TestCase
|
5
5
|
#test TrackAndDistance
|
6
6
|
def test_track_and_distance
|
7
|
-
assert_equal("140
|
8
|
-
assert_equal("215
|
7
|
+
assert_equal("140 14'10.0000\" 12.0m", TrackAndDistance.new(Angle.new("320,14,10").reverse, 12.0).to_s)
|
8
|
+
assert_equal("215 03'00.0000\" 19.73m", TrackAndDistance.new("215,3,0", 19.73 ).to_s)
|
9
9
|
a = TrackAndDistance.new("215,3,0", 19.73 ).to_ary
|
10
|
-
assert_equal("215
|
10
|
+
assert_equal("215 03'00.0000\"", a[0].strf)
|
11
11
|
assert_equal("19.73", a[1].to_s)
|
12
12
|
a = TrackAndDistance.new("215,3,0", 19.73 ).to_hash
|
13
|
-
assert_equal("215
|
13
|
+
assert_equal("215 03'00.0000\"", a[:bearing].strf)
|
14
14
|
assert_equal("19.73", a[:distance].to_s)
|
15
15
|
end
|
16
16
|
end
|
data/test/ts_vincenty.rb
CHANGED
@@ -82,13 +82,13 @@ class TestVincenty< Test::Unit::TestCase
|
|
82
82
|
|
83
83
|
#Run the Australian Geoscience site example.
|
84
84
|
def test_geoscience_au
|
85
|
-
flindersPeak = Vincenty.new("-37
|
86
|
-
buninyong = Vincenty.new("-37
|
85
|
+
flindersPeak = Vincenty.new("-37 57'3.72030″", "144 25'29.52440″" )
|
86
|
+
buninyong = Vincenty.new("-37 39 ' 10.15610 ''", "143 55 ' 35.38390 ''") #Buninyong
|
87
87
|
track_and_bearing = flindersPeak.distanceAndAngle( buninyong )
|
88
|
-
assert_equal(Angle.new("306
|
88
|
+
assert_equal(Angle.new("306 52 ' 5.37 ''").to_d.round(4), track_and_bearing.bearing.to_d.round(4))
|
89
89
|
assert_equal(54972.271, track_and_bearing.distance.round(3))
|
90
90
|
|
91
|
-
destination = flindersPeak.destination(TrackAndDistance.new("306
|
91
|
+
destination = flindersPeak.destination(TrackAndDistance.new("306 52 ' 5.37 ''", 54972.271))
|
92
92
|
assert_equal(buninyong.latitude.to_d.round(4), destination.latitude.to_d.round(4))
|
93
93
|
assert_equal(buninyong.longitude.to_d.round(4), destination.longitude.to_d.round(4))
|
94
94
|
end
|
metadata
CHANGED
@@ -1,32 +1,82 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: vincenty
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.4
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
|
-
authors:
|
7
|
+
authors:
|
7
8
|
- Rob Burrowes
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
date: 2013-01-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: hoe-yard
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.1.2
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.1.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hoe
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.1'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.1'
|
46
|
+
description: ! "* Vincenty wrote an algorithm for calculating the bearing and distance
|
47
|
+
between two coordinates on the earth\n and an algorithm for finding a second coordinate,
|
48
|
+
given a starting coordinate, bearing and destination.\n The algorithms model the
|
49
|
+
earth as an ellipsoid, using the WGS-84 model. This is the common GPS model for\n
|
50
|
+
\ mapping to latitudes and longitudes.\n\n This is a Ruby implementation of Vincenty's
|
51
|
+
algorithms, and the Vincenty class includes two methods for \n modeling the earth
|
52
|
+
as a sphere. These were added as a reference for testing the Vincenty algorithm,
|
53
|
+
but\n could be used on their own. \n\n The package also makes use of several other
|
54
|
+
classes that may be useful in their own Right. These include\n class Angle, class
|
55
|
+
Latitude (subclass of Angle), class Longitude (subclass of Angle), \n class TrackAndBearing
|
56
|
+
and class coordinate (which class Vincenty is a subclass)\n\n Angle requires extensions
|
57
|
+
to Numeric and String to provide to_radians (to_r) and to_degrees (to_d). String
|
58
|
+
also includes a to_decimal_degrees(), which converts most string forms of Latitude
|
59
|
+
and Longitude to decimal form. These extensions are included in the package in core_extensions.rb.
|
60
|
+
Float has also been extended to change round to have an optional argument specifying
|
61
|
+
the number of decimal places to round to. This is fully compatible with the Float.round,
|
62
|
+
as the default is to round to 0 decimal places.\n\n* The Vincenty code is based
|
63
|
+
on the wikipedia presentation of the Vincenty algorithm http://en.wikipedia.org/wiki/Vincenty%27s_formulae
|
64
|
+
.\n* The algorithm was modified to include changes I found at http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html.\n*
|
65
|
+
\ I also altered the formulae to correctly return the bearing for angles greater
|
66
|
+
than 180. \n \n* Vincenty's original publication\n\n** T Vincenty, \"Direct and
|
67
|
+
Inverse Solutions of Geodesics on the Ellipsoid with application of nested equations\",
|
68
|
+
Survey Review, vol XXII no 176, 1975 http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf"
|
69
|
+
email:
|
70
|
+
- r.burrowes@auckland.ac.nz
|
18
71
|
executables: []
|
19
|
-
|
20
72
|
extensions: []
|
21
|
-
|
22
|
-
extra_rdoc_files:
|
73
|
+
extra_rdoc_files:
|
23
74
|
- History.txt
|
24
75
|
- Manifest.txt
|
25
|
-
|
26
|
-
files:
|
76
|
+
files:
|
27
77
|
- History.txt
|
28
78
|
- Manifest.txt
|
29
|
-
- README.
|
79
|
+
- README.md
|
30
80
|
- Rakefile
|
31
81
|
- lib/angle.rb
|
32
82
|
- lib/coordinate.rb
|
@@ -42,34 +92,37 @@ files:
|
|
42
92
|
- test/ts_vincenty.rb
|
43
93
|
- test/ts_coordinate.rb
|
44
94
|
- test/ts_track_and_distance.rb
|
45
|
-
|
46
|
-
homepage: http://
|
95
|
+
- .gemtest
|
96
|
+
homepage: http://rbur004.github.com/vincenty/
|
47
97
|
licenses: []
|
48
|
-
|
49
98
|
post_install_message:
|
50
|
-
rdoc_options:
|
51
|
-
- --
|
52
|
-
-
|
53
|
-
|
99
|
+
rdoc_options:
|
100
|
+
- --markup
|
101
|
+
- markdown
|
102
|
+
- --protected
|
103
|
+
- --title
|
104
|
+
- Vincenty
|
105
|
+
- --quiet
|
106
|
+
require_paths:
|
54
107
|
- lib
|
55
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
67
120
|
requirements: []
|
68
|
-
|
69
121
|
rubyforge_project: vincenty
|
70
|
-
rubygems_version: 1.
|
122
|
+
rubygems_version: 1.8.24
|
71
123
|
signing_key:
|
72
|
-
specification_version:
|
73
|
-
summary:
|
124
|
+
specification_version: 3
|
125
|
+
summary: ! '* Vincenty wrote an algorithm for calculating the bearing and distance
|
126
|
+
between two coordinates on the earth and an algorithm for finding a second coordinate,
|
127
|
+
given a starting coordinate, bearing and destination'
|
74
128
|
test_files: []
|
75
|
-
|