vincenty 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,99 +1,97 @@
1
1
  require 'scanf'
2
2
 
3
- #Extends Numeric, hence Fixed & Float to_r & to_d
4
- #Also adds in sign.
3
+ # Extends Numeric, hence Fixed & Float to_r & to_d
4
+ # Also adds in sign.
5
5
  class Numeric
6
- #Convert Radians to Degrees
6
+ # Convert Radians to Degrees
7
7
  # @return [Numeric] degrees
8
8
  # @param [true,false] mod Optional argument mod == true, then applies % 360
9
- def to_degrees(mod=false)
10
- if mod
11
- (self * 180 / Math::PI) % 360
12
- else
13
- self * 180 / Math::PI
9
+ def to_degrees(mod = false)
10
+ if mod
11
+ (self * 180 / Math::PI) % 360
12
+ else
13
+ self * 180 / Math::PI
14
14
  end
15
15
  end
16
-
17
- #Converts degrees to Radians
16
+
17
+ # Converts degrees to Radians
18
18
  # @return [Numeric] radians
19
19
  # @param [true,false] mod Optional argument mod == true, then applies % Math::PI
20
- def to_radians(mod=false)
21
- if mod
20
+ def to_radians(mod = false)
21
+ if mod
22
22
  (self * Math::PI / 180) % Math::PI
23
- else
23
+ else
24
24
  self * Math::PI / 180
25
25
  end
26
26
  end
27
-
27
+
28
28
  alias to_r to_radians
29
29
  alias to_rad to_radians
30
30
  alias to_deg to_degrees
31
-
31
+
32
32
  # @return [Fixnum] 1 if number is positive, -1 if negative.
33
33
  def sign
34
34
  self < 0 ? -1 : 1
35
35
  end
36
-
37
36
  end
38
37
 
39
- #Alters round method to have an optional number of decimal places.
38
+ # Alters round method to have an optional number of decimal places.
40
39
  class Float
41
-
42
- #Replace default round, so we can reference it later.
40
+ # Replace default round, so we can reference it later.
43
41
  # @return [Float]
44
42
  alias round0 round
45
-
46
- #Compatible Replacement for Float.round
43
+
44
+ # Compatible Replacement for Float.round
47
45
  # @return [Float] float rounded to n decimal places.
48
46
  # @param [Numeric] n Optional argument n is the number of decimal places to round to.
49
47
  def round(n = 0)
50
48
  if n == 0
51
- self.round0 #This is to preserve the default behaviour, which is to return a Fixnum, not a float.
49
+ self.round0 # This is to preserve the default behaviour, which is to return a Fixnum, not a float.
52
50
  else
53
51
  m = 10.0**n
54
- (self * m).round0 / m
55
- end
52
+ (self * m).round0 / m
53
+ end
56
54
  end
57
55
  end
58
56
 
59
- #Extends String to to_dec_degrees, add to_r and to_d
57
+ # Extends String to to_dec_degrees, add to_r and to_d
60
58
  class String
61
- #string expected to be degrees, returns decimal degrees.
62
- #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, ...
59
+ # string expected to be degrees, returns decimal degrees.
60
+ # 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, ...
63
61
  # @return [Float] angle in decimal degrees
64
- def to_dec_degrees
65
- #reorder 37 001'.512S, S37 001'.512 into 37 001.512'S, S37 001.512' respectively
66
- s = self.gsub(/([0-9])([''])\.([0-9]+)/, '\1.\3\2')
67
- #add in minutes and seconds to get 3 values 'deg 0 0'from S37 0, 37 0S
68
- s.gsub!(/^([^0-9\.\-]*)([0-9\-\.]+)([^0-9\-\.]*)$/, '\1\2\3 0 0\5')
69
- #add in seconds get 3 values 'deg min 0' from S37 01.512', 37 01.512'S
70
- s.gsub!(/^([^0-9\.\-]*)([0-9\-\.]+)([^0-9\-\.]+)([0-9\-\.]+)([^0-9\-\.]*)$/, '\1\2\3\4 0\5')
71
-
72
- #look for anything of the form S37 001'7.5'', S37 01.512', S37.01875 0, ...
73
- s.scanf("%[NSEW]%f%[^0-9-]%f%[^0-9-]%f") do |direction, deg, sep1, min, sep2, sec|
62
+ def to_dec_degrees
63
+ # reorder 37 001'.512S, S37 001'.512 into 37 001.512'S, S37 001.512' respectively
64
+ s = self.gsub(/([0-9])(')\.([0-9]+)/, '\1.\3\2')
65
+ # add in minutes and seconds to get 3 values 'deg 0 0'from S37 0, 37 0S
66
+ s.gsub!(/^([^0-9.\-]*)([0-9\-.]+)([^0-9\-.]*)$/, '\1\2\3 0 0\5')
67
+ # add in seconds get 3 values 'deg min 0' from S37 01.512', 37 01.512'S
68
+ s.gsub!(/^([^0-9.\-]*)([0-9\-.]+)([^0-9\-.]+)([0-9\-.]+)([^0-9\-.]*)$/, '\1\2\3\4 0\5')
69
+
70
+ # look for anything of the form S37 001'7.5'', S37 01.512', S37.01875 0, ...
71
+ s.scanf('%[NSEW]%f%[^0-9-]%f%[^0-9-]%f') do |direction, deg, _sep1, min, _sep2, sec|
74
72
  return Angle.decimal_deg( deg, min, sec, direction)
75
73
  end
76
-
77
- #look for anything of the form 37 001'7.5''S , -37 001'7.5'', -37 0 1.512'. -37.01875 0, ...
78
- s.scanf("%f%[^0-9-]%f%[^0-9-]%f%[^NSEW]%[NSEW]") do |deg, sep1, min, sep2, sec, sep3, direction|
74
+
75
+ # look for anything of the form 37 001'7.5''S , -37 001'7.5'', -37 0 1.512'. -37.01875 0, ...
76
+ s.scanf('%f%[^0-9-]%f%[^0-9-]%f%[^NSEW]%[NSEW]') do |deg, _sep1, min, _sep2, sec, _sep3, direction|
79
77
  return Angle.decimal_deg( deg, min, sec, direction)
80
78
  end
81
79
  end
82
-
83
- #Convert String number in Radians to Degrees
80
+
81
+ # Convert String number in Radians to Degrees
84
82
  # @return [Float] degrees
85
83
  # @param [true,false] mod Optional argument mod == true, then applies % 360
86
- def to_degrees(mod=false) #string expected to be radians, returns degrees
87
- self.to_f.to_degrees(mod)
84
+ def to_degrees(mod = false)
85
+ return self.to_f.to_degrees(mod)
88
86
  end
89
-
90
- #Converts string degrees to to_decimal_degrees, then to Radians
87
+
88
+ # Converts string degrees to to_decimal_degrees, then to Radians
91
89
  # @return [Float] radians
92
90
  # @param [true,false] mod Optional argument mod == true, then applies % Math::PI
93
- def to_radians(mod=false) #string expected to be degrees, returns radians
94
- self.to_dec_degrees.to_radians(mod)
91
+ def to_radians(mod = false)
92
+ return self.to_dec_degrees.to_radians(mod)
95
93
  end
96
-
94
+
97
95
  alias to_rad to_radians
98
96
  alias to_r to_radians
99
97
  alias to_deg to_degrees
data/lib/latitude.rb CHANGED
@@ -1,44 +1,42 @@
1
-
2
1
  require_relative 'angle.rb'
3
2
 
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
-
3
+ # Subclass of Angle to add in special treatment of to_d, to_r , to_s
4
+ # Latitude degrees are between -PI and PI, South to North (+/- 90 degrees)
7
5
  class Latitude < Angle
8
-
9
6
  # @return [Float] angle as degrees in range -90 and 90
10
7
  def to_degrees
11
8
  degrees = super
12
- case
13
- when degrees > 270 ; -(360 - degrees)
14
- when degrees > 180 ; 180 - degrees
15
- when degrees > 90 ; 180 - degrees
16
- when degrees < -90 ; 180 - degrees
17
- else degrees
9
+ if degrees > 270
10
+ -(360 - degrees)
11
+ elsif degrees > 180 || degrees > 90 || degrees < -90
12
+ 180 - degrees
13
+ else
14
+ degrees
18
15
  end
19
16
  end
20
17
 
21
18
  # @return [Float] angle as degrees in range -PI and PI
22
19
  def to_radians
23
- case
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
20
+ if @angle > 3 * Math::PI / 2
21
+ @angle - Math::PI * 2
22
+ elsif @angle > Math::PI || @angle > Math::PI / 2
23
+ Math::PI - @angle
24
+ elsif @angle < -Math::PI / 2
25
+ -Math::PI - @angle
26
+ else
27
+ @angle
29
28
  end
30
29
  end
31
30
 
32
31
  # @return [String] angle as string in degrees minutes seconds direction.
33
- #A South angle is negative, North is Positive.
32
+ # A South angle is negative, North is Positive.
34
33
  # @param [String] fmt Optional format string passed to Angle#to_s
35
- def to_s(fmt="%2d %2m'%2.4s\"%N")
36
- super(fmt)
34
+ def to_s(fmt = "%2d %2m'%2.4s\"%N")
35
+ super(fmt)
37
36
  end
38
37
 
39
38
  alias to_r to_radians
40
39
  alias to_rad to_radians
41
- #alias to_d to_degrees
40
+ # alias to_d to_degrees
42
41
  alias to_deg to_degrees
43
-
44
42
  end
data/lib/longitude.rb CHANGED
@@ -1,38 +1,34 @@
1
-
2
1
  require_relative 'angle.rb'
3
2
 
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
-
3
+ # Subclass of Angle to add in special treatment of to_d, to_r and to_s
4
+ # Longitude degrees are between -2PI and 2PI, West to East (+/- 180 degrees)
7
5
  class Longitude < Angle
8
-
9
6
  # @return [Float] angle as degrees in range -180 and 180
10
7
  def to_degrees
11
8
  degrees = super
12
- case
13
- when degrees > 180 ; degrees - 360
14
- else degrees
9
+ if degrees > 180 then degrees - 360
10
+ else
11
+ degrees
15
12
  end
16
13
  end
17
14
 
18
15
  # @return [Float] angle as degrees in range -2PI and 2PI
19
16
  def to_radians
20
- case
21
- when @angle > Math::PI ; @angle - 2 * Math::PI
22
- else @angle
17
+ if @angle > Math::PI then @angle - 2 * Math::PI
18
+ else
19
+ @angle
23
20
  end
24
21
  end
25
22
 
26
23
  # @return [String] angle as string in degrees minutes seconds direction.
27
- #A West angle is negative, East is Positive.
24
+ # A West angle is negative, East is Positive.
28
25
  # @param [String] fmt Optional format string passed to Angle#to_s
29
- def to_s(fmt="%3d %2m'%2.4s\"%E")
26
+ def to_s(fmt = "%3d %2m'%2.4s\"%E")
30
27
  super(fmt)
31
28
  end
32
29
 
33
30
  alias to_r to_radians
34
31
  alias to_rad to_radians
35
- #alias to_d to_degrees
32
+ # alias to_d to_degrees
36
33
  alias to_deg to_degrees
37
-
38
34
  end
@@ -1,41 +1,40 @@
1
-
2
1
  require_relative 'angle.rb'
3
2
 
4
- #Holds a bearing and distance
3
+ # Holds a bearing and distance
5
4
  class TrackAndDistance
6
- # @return [Angle]
7
- attr_accessor :bearing
8
- # @return [Float]
9
- attr_accessor :distance
5
+ # @return [Angle]
6
+ attr_accessor :bearing
7
+ # @return [Float]
8
+ attr_accessor :distance
10
9
 
11
- # @param [String, Numeric, #to_radian, #to_f] Bearing can be a String or Numeric or any object with to_radians and to_f
12
- # @param [Numeric] distance
13
- # @param [true,false, :radians] radians Bearing is in degrees unless radians == true (or set to :radians).
14
- def initialize(bearing, distance, radians=false)
15
- @bearing = Angle.new(bearing, radians)
16
- @distance = distance
17
- end
10
+ # @param [String, Numeric, #to_radian, #to_f] Bearing can be a String or Numeric or any object with to_radians and to_f
11
+ # @param [Numeric] distance
12
+ # @param [true,false, :radians] radians Bearing is in degrees unless radians == true (or set to :radians).
13
+ def initialize(bearing, distance, radians = false)
14
+ @bearing = Angle.new(bearing, radians)
15
+ @distance = distance
16
+ end
18
17
 
19
- #format string fmt is currently just for the bearing angle.
20
- #Need to change this to include the distance is single format string.
21
- # @return [String] Bearing angle and distance in meters.
22
- # @param [String] fmt Optional format string passed to Coordinate#strf
23
- def to_s(fmt = nil)
24
- if(fmt)
25
- #needs work to include distance as well as angle fmt.
26
- "#{@bearing.strf(fmt)} #{distance.round(4)}m"
27
- else
28
- "#{@bearing.strf} #{distance.round(4)}m"
29
- end
18
+ # format string fmt is currently just for the bearing angle.
19
+ # Need to change this to include the distance is single format string.
20
+ # @return [String] Bearing angle and distance in meters.
21
+ # @param [String] fmt Optional format string passed to Coordinate#strf
22
+ def to_s(fmt = nil)
23
+ if fmt
24
+ # needs work to include distance as well as angle fmt.
25
+ return "#{@bearing.strf(fmt)} #{distance.round(4)}m"
26
+ else
27
+ return "#{@bearing.strf} #{distance.round(4)}m"
30
28
  end
29
+ end
31
30
 
32
- # @return [Array] with members bearing and distance.
33
- def to_ary
34
- [ @bearing, @distance ]
35
- end
31
+ # @return [Array] with members bearing and distance.
32
+ def to_ary
33
+ return [ @bearing, @distance ]
34
+ end
36
35
 
37
- # @return [Hash] with keys :bearing and :distance
38
- def to_hash
39
- { :bearing => @bearing, :distance => @distance }
40
- end
36
+ # @return [Hash] with keys :bearing and :distance
37
+ def to_hash
38
+ return { bearing: @bearing, distance: @distance }
39
+ end
41
40
  end
data/lib/vincenty.rb CHANGED
@@ -5,199 +5,197 @@ require_relative 'longitude.rb'
5
5
  require_relative 'track_and_distance.rb'
6
6
  require_relative 'coordinate.rb'
7
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.
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
10
  #
11
11
  # Coded from formulae from Wikipedia http://en.wikipedia.org/wiki/Vincenty%27s_formulae
12
12
  # Modified to incorporate corrections to formulae as found in script on http://www.movable-type.co.uk/scripts/LatLongVincenty.html
13
13
  # Added my Modification of the distanceAndAngle formulae to correct the compass bearing.
14
14
  class Vincenty < Coordinate
15
- VERSION = '1.0.8'
15
+ VERSION = '1.0.9'
16
16
 
17
17
  # @return [String] constant VERSION
18
18
  def version
19
19
  VERSION
20
20
  end
21
21
 
22
- WGS84_ER = 6378137 #Equatorial Radius of earth
23
- WGS84_IF = 298.257223563 #Inverse Flattening
24
- GRS80_ER = 6378137 #Equatorial Radius of earth
25
- GRS80_IF = 298.25722210882711 #Inverse Flattening
22
+ WGS84_ER = 6378137 # Equatorial Radius of earth
23
+ WGS84_IF = 298.257223563 # Inverse Flattening
24
+ GRS80_ER = 6378137 # Equatorial Radius of earth
25
+ GRS80_IF = 298.25722210882711 # Inverse Flattening
26
26
 
27
- #Great Circle formulae http://en.wikipedia.org/wiki/Great-circle_distance
28
- #Reference calculation for testing, assumes the earth is a sphere, which it isn't.
29
- #This gives us an approximation to verify Vincenty algorithm.
27
+ # Great Circle formulae http://en.wikipedia.org/wiki/Great-circle_distance
28
+ # Reference calculation for testing, assumes the earth is a sphere, which it isn't.
29
+ # This gives us an approximation to verify Vincenty algorithm.
30
30
  # @param [Coordinate] p2 is target coordinate that we want the bearing to.
31
31
  # @return [TrackAndDistance] with the compass bearing and distance in meters to P2
32
32
  def sphericalDistanceAndAngle( p2, equatorial_radius = WGS84_ER, inverse_flattening = WGS84_IF )
33
33
  if self.latitude == p2.latitude && self.longitude == p2.longitude
34
- return TrackAndDistance.new(0, 0, true) #No calculations necessary
34
+ return TrackAndDistance.new(0, 0, true) # No calculations necessary
35
35
  end
36
36
 
37
- a = equatorial_radius #equatorial radius in meters (+/-2 m)
38
- f = inverse_flattening.
39
- b = a - a/f #WGS84 = 6356752.314245179 polar radius in meters
40
- r = (a+b)/2 #average diametre as a rough estimate for our tests.
37
+ a = equatorial_radius # equatorial radius in meters (+/-2 m)
38
+ f = inverse_flattening
39
+ b = a - a / f # WGS84 = 6356752.314245179 polar radius in meters
40
+ r = (a + b) / 2 # average diametre as a rough estimate for our tests.
41
41
 
42
42
  sin_lat1 = Math.sin(@latitude.to_rad)
43
43
  sin_lat2 = Math.sin(p2.latitude.to_rad)
44
44
  cos_lat1 = Math.cos(@latitude.to_rad)
45
45
  atan1_2 = Math.atan(1) * 2
46
- t1 = cos_lat1 * Math.cos(p2.latitude.to_rad) * ( Math.cos(@longitude.to_rad - p2.longitude.to_rad) ) + sin_lat1 * sin_lat2
47
- angular_distance = Math.atan(-t1/Math.sqrt(-t1 * t1 +1)) + atan1_2 #central angle in radians so we can calculate the arc length.
46
+ t1 = cos_lat1 * Math.cos(p2.latitude.to_rad) * Math.cos(@longitude.to_rad - p2.longitude.to_rad) + sin_lat1 * sin_lat2
47
+ angular_distance = Math.atan(-t1 / Math.sqrt(-t1 * t1 + 1)) + atan1_2 # central angle in radians so we can calculate the arc length.
48
48
 
49
49
  t2 = (sin_lat2 - sin_lat1 * Math.cos(angular_distance)) / (cos_lat1 * Math.sin(angular_distance))
50
- if(Math.sin(p2.longitude.to_rad - @longitude.to_rad) < 0)
51
- bearing = 2 * Math::PI - (Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2) #Compass Bearing in radians (clockwise)
52
- else
53
- bearing = Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2 #Compass Bearing in radians (clockwise)
54
- end
50
+ bearing = if Math.sin(p2.longitude.to_rad - @longitude.to_rad) < 0
51
+ 2 * Math::PI - (Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2) # Compass Bearing in radians (clockwise)
52
+ else
53
+ Math.atan(-t2 / Math.sqrt(-t2 * t2 + 1)) + atan1_2 # Compass Bearing in radians (clockwise)
54
+ end
55
55
 
56
- #Note that the bearing is a compass angle. That is angles are positive clockwise.
56
+ # Note that the bearing is a compass angle. That is angles are positive clockwise.
57
57
  return TrackAndDistance.new(bearing, angular_distance * r, true)
58
58
  end
59
59
 
60
- #Vincenty's algorithm for finding bearing and distance between to coordinates.
61
- #Assumes earth is a WGS-84 Ellipsod.
60
+ # Vincenty's algorithm for finding bearing and distance between to coordinates.
61
+ # Assumes earth is a WGS-84 Ellipsod.
62
62
  # @param [Coordinate] p2 is target coordinate that we want the bearing to.
63
63
  # @return [TrackAndDistance] with the compass bearing and distance in meters to P2
64
64
  def distanceAndAngle( p2 )
65
65
  if self.latitude == p2.latitude && self.longitude == p2.longitude
66
- return TrackAndDistance.new(0, 0, true) #No calculations necessary. Solv NAN issue
66
+ return TrackAndDistance.new(0, 0, true) # No calculations necessary. Solv NAN issue
67
67
  end
68
68
 
69
69
  # a, b = major & minor semiaxes of the ellipsoid
70
- a = 6378137 #equatorial radius in meters (+/-2 m)
71
- b = 6356752.31424518 #polar radius in meters
72
- f = (a-b)/a # flattening
70
+ a = 6378137 # equatorial radius in meters (+/-2 m)
71
+ b = 6356752.31424518 # polar radius in meters
72
+ f = (a - b) / a # flattening
73
73
 
74
- lat1 = @latitude.to_rad
75
- lon1 = @longitude.to_rad
76
- lat2 = p2.latitude.to_rad
77
- lon2 = p2.longitude.to_rad
78
- lat1 = lat1.sign * (Math::PI/2-(1e-10)) if (Math::PI/2-lat1.abs).abs < 1.0e-10
79
- lat2 = lat2.sign * (Math::PI/2-(1e-10)) if (Math::PI/2-lat2.abs).abs < 1.0e-10
74
+ lat1 = @latitude.to_rad
75
+ lon1 = @longitude.to_rad
76
+ lat2 = p2.latitude.to_rad
77
+ lon2 = p2.longitude.to_rad
78
+ lat1 = lat1.sign * (Math::PI / 2 - 1e-10) if (Math::PI / 2 - lat1.abs).abs < 1.0e-10
79
+ lat2 = lat2.sign * (Math::PI / 2 - 1e-10) if (Math::PI / 2 - lat2.abs).abs < 1.0e-10
80
80
 
81
81
  # lat1, lat2 = geodetic latitude
82
82
 
83
- l = (lon2 - lon1).abs #difference in longitude
84
- l = 2*Math::PI - l if l > Math::PI
85
- u1 = Math.atan( ( 1 - f) * Math.tan( lat1 ) ) #U is 'reduced latitude'
86
- u2 = Math.atan( ( 1 - f) * Math.tan( lat2 ) )
87
- sin_u1 = Math.sin(u1)
88
- cos_u1 = Math.cos(u1)
89
- sin_u2 = Math.sin(u2)
90
- cos_u2 = Math.cos(u2)
91
-
92
- lambda_v = l
93
- lambda_dash = Math::PI * 2
94
- while( (lambda_v - lambda_dash).abs > 1.0e-12 ) #i.e. 0.06 mm error
95
- sin_lambda_v = Math.sin(lambda_v)
96
- cos_lambda_v = Math.cos(lambda_v)
97
- sin_sigma = Math.sqrt( ( cos_u2 * sin_lambda_v ) ** 2 + ( cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v ) ** 2 )
98
- cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda_v
99
- sigma = Math.atan2(sin_sigma, cos_sigma)
100
- sin_alpha= cos_u1 * cos_u2 * sin_lambda_v / sin_sigma
101
- cos_2_alpha = 1 - sin_alpha * sin_alpha #trig identity
102
- cos_2_sigma_m = cos_sigma - 2 * sin_u1 * sin_u2/cos_2_alpha
103
- c = f / 16 * cos_2_alpha * (4 + f*(4-3*cos_2_alpha))
104
- lambda_dash = lambda_v
105
- lambda_v = l + (1-c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) ) ) # use cos_2_sigma_m=0 when over equatorial lines
106
- if lambda_v > Math::PI
107
- lambda_v = Math::PI
108
- break
109
- end
110
- end
111
-
112
- u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
113
- a1 = 1 + u_2 / 16384 * (4096 + u_2 * (-768 + u_2 * (320 - 175 * u_2)))
114
- b1 = u_2 / 1024 * (256 + u_2 * (-128 + u_2 * (74 - 47 * u_2)))
115
- delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1 / 4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1 / 6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
116
- s = b * a1 * (sigma - delta_sigma)
83
+ l = (lon2 - lon1).abs # difference in longitude
84
+ l = 2 * Math::PI - l if l > Math::PI
85
+ u1 = Math.atan( ( 1 - f) * Math.tan( lat1 ) ) # U is 'reduced latitude'
86
+ u2 = Math.atan( ( 1 - f) * Math.tan( lat2 ) )
87
+ sin_u1 = Math.sin(u1)
88
+ cos_u1 = Math.cos(u1)
89
+ sin_u2 = Math.sin(u2)
90
+ cos_u2 = Math.cos(u2)
91
+
92
+ lambda_v = l
93
+ lambda_dash = Math::PI * 2
94
+ while (lambda_v - lambda_dash).abs > 1.0e-12 # i.e. 0.06 mm error
117
95
  sin_lambda_v = Math.sin(lambda_v)
118
96
  cos_lambda_v = Math.cos(lambda_v)
119
-
120
- #This test isn't in original formulae, and fixes the problem of all angles returned being between 0 - PI (0-180)
121
- #Also converts the result to compass bearing, rather than the mathmatical anticlockwise angles.
122
- if(Math.sin(p2.longitude.to_rad - @longitude.to_rad) < 0)
123
- alpha_1 = Math::PI*2-Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
124
- #alpha_2 = Math::PI*2-Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
125
- else
126
- alpha_1 = Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
127
- #alpha_2 = Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
97
+ sin_sigma = Math.sqrt( ( cos_u2 * sin_lambda_v )**2 + ( cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v )**2 )
98
+ cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda_v
99
+ sigma = Math.atan2(sin_sigma, cos_sigma)
100
+ sin_alpha = cos_u1 * cos_u2 * sin_lambda_v / sin_sigma
101
+ cos_2_alpha = 1 - sin_alpha * sin_alpha # trig identity
102
+ cos_2_sigma_m = cos_sigma - 2 * sin_u1 * sin_u2 / cos_2_alpha
103
+ c = f / 16 * cos_2_alpha * (4 + f * (4 - 3 * cos_2_alpha))
104
+ lambda_dash = lambda_v
105
+ lambda_v = l + (1 - c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) ) ) # use cos_2_sigma_m=0 when over equatorial lines
106
+ if lambda_v > Math::PI
107
+ lambda_v = Math::PI
108
+ break
128
109
  end
110
+ end
129
111
 
130
- #Note that the bearing is a compass (i.e. clockwise) angle.
131
- return TrackAndDistance.new(alpha_1, s, true) #What to do with alpha_2?
112
+ u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
113
+ a1 = 1 + u_2 / 16384 * (4096 + u_2 * (-768 + u_2 * (320 - 175 * u_2)))
114
+ b1 = u_2 / 1024 * (256 + u_2 * (-128 + u_2 * (74 - 47 * u_2)))
115
+ delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1 / 4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1 / 6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
116
+ s = b * a1 * (sigma - delta_sigma)
117
+ sin_lambda_v = Math.sin(lambda_v)
118
+ cos_lambda_v = Math.cos(lambda_v)
119
+
120
+ # This test isn't in original formulae, and fixes the problem of all angles returned being between 0 - PI (0-180)
121
+ # Also converts the result to compass bearing, rather than the mathmatical anticlockwise angles.
122
+ alpha_1 = if Math.sin(p2.longitude.to_rad - @longitude.to_rad) < 0
123
+ Math::PI * 2 - Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
124
+ # alpha_2 = Math::PI*2-Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
125
+ else
126
+ Math.atan2( cos_u2 * sin_lambda_v, cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda_v)
127
+ # alpha_2 = Math.atan2(cos_u1 * sin_lambda_v, -sin_u1 * cos_u2 + cos_u1 * sin_u2 * cos_lambda_v)
128
+ end
129
+
130
+ # Note that the bearing is a compass (i.e. clockwise) angle.
131
+ return TrackAndDistance.new(alpha_1, s, true) # What to do with alpha_2?
132
132
  end
133
133
 
134
- #spherical earth estimate of calculation for finding target coordinate from start coordinate, bearing and distance
135
- #Used to run checks on the Vincenty algorithm
134
+ # spherical earth estimate of calculation for finding target coordinate from start coordinate, bearing and distance
135
+ # Used to run checks on the Vincenty algorithm
136
136
  # @param [TrackAndDistance] track_and_distance specifying bearing and distance.
137
137
  # @return [Vincenty] with the destination coordinates.
138
138
  def sphereDestination( track_and_distance )
139
- a = 6378137 #equatorial radius in meters (+/-2 m)
140
- b = 6356752.31424518 #polar radius in meters
141
- r = (a+b)/2 #average diametre as a rough estimate for our tests.
139
+ a = 6378137 # equatorial radius in meters (+/-2 m)
140
+ b = 6356752.31424518 # polar radius in meters
141
+ r = (a + b) / 2 # average diametre as a rough estimate for our tests.
142
142
 
143
143
  d = track_and_distance.distance.abs
144
- sin_dor = Math.sin(d/r)
145
- cos_dor = Math.cos(d/r)
144
+ sin_dor = Math.sin(d / r)
145
+ cos_dor = Math.cos(d / r)
146
146
  sin_lat1 = Math.sin(@latitude.to_rad)
147
147
  cos_lat1 = Math.cos(@latitude.to_rad)
148
148
  lat2 = Math.asin( sin_lat1 * cos_dor + cos_lat1 * sin_dor * Math.cos(track_and_distance.bearing.to_rad) )
149
- lon2 = @longitude.to_rad + Math.atan2(Math.sin(track_and_distance.bearing.to_rad) * sin_dor * cos_lat1, cos_dor-sin_lat1 * Math.sin(lat2))
149
+ lon2 = @longitude.to_rad + Math.atan2(Math.sin(track_and_distance.bearing.to_rad) * sin_dor * cos_lat1, cos_dor - sin_lat1 * Math.sin(lat2))
150
150
 
151
- Vincenty.new(lat2, lon2, 0, true);
151
+ Vincenty.new(lat2, lon2, 0, true)
152
152
  end
153
153
 
154
154
  #
155
155
  # Calculate destination point given start point lat/long, bearing and distance.
156
- #Assumes earth is a WGS-84 Ellipsod.
156
+ # Assumes earth is a WGS-84 Ellipsod.
157
157
  # @param [TrackAndDistance] specifying bearing and distance.
158
158
  # @return [Vincenty] with the destination coordinates.
159
159
  def destination( track_and_distance )
160
160
  # a, b = major & minor semiaxes of the ellipsoid
161
- a = 6378137 #equatorial radius in meters (+/-2 m)
162
- b = 6356752.31424518 #polar radius in meters
163
- f = (a-b)/a # flattening
161
+ a = 6378137 # equatorial radius in meters (+/-2 m)
162
+ b = 6356752.31424518 # polar radius in meters
163
+ f = (a - b) / a # flattening
164
164
 
165
- s = track_and_distance.distance.abs;
165
+ s = track_and_distance.distance.abs
166
166
  alpha1 = track_and_distance.bearing.to_rad
167
167
  sin_alpha1 = Math.sin(alpha1)
168
168
  cos_alpha1 = Math.cos(alpha1)
169
169
 
170
- tanU1 = (1-f) * Math.tan(@latitude.to_rad);
171
- cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1))
170
+ tanU1 = (1 - f) * Math.tan(@latitude.to_rad)
171
+ cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1))
172
172
  sinU1 = tanU1 * cosU1
173
173
  sigma1 = Math.atan2(tanU1, cos_alpha1)
174
- sin_alpha = cosU1 * sin_alpha1
175
- cos_2_alpha = 1 - sin_alpha * sin_alpha #Trig identity
176
- u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
177
- a1 = 1 + u_2/16384 * (4096 + u_2 * (-768 + u_2 * (320-175 * u_2)))
178
- b1 = u_2/1024 * (256 + u_2 * (-128 + u_2 * (74-47 * u_2)))
174
+ sin_alpha = cosU1 * sin_alpha1
175
+ cos_2_alpha = 1 - sin_alpha * sin_alpha # Trig identity
176
+ u_2 = cos_2_alpha * (a * a - b * b) / (b * b)
177
+ a1 = 1 + u_2 / 16384 * (4096 + u_2 * (-768 + u_2 * (320 - 175 * u_2)))
178
+ b1 = u_2 / 1024 * (256 + u_2 * (-128 + u_2 * (74 - 47 * u_2)))
179
179
 
180
180
  sigma = s / (b * a1)
181
181
  sigma_dash = 2 * Math::PI
182
- while ((sigma-sigma_dash).abs > 1.0e-12) #i.e 0.06mm
183
- cos_2_sigma_m = Math.cos(2 * sigma1 + sigma)
182
+ while (sigma - sigma_dash).abs > 1.0e-12 # i.e 0.06mm
183
+ cos_2_sigma_m = Math.cos(2 * sigma1 + sigma)
184
184
  sin_sigma = Math.sin(sigma)
185
185
  cos_sigma = Math.cos(sigma)
186
- delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1/4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1/6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
186
+ delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1 / 4 * (cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1 / 6 * cos_2_sigma_m * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
187
187
  sigma_dash = sigma
188
- sigma = s / (b * a1) + delta_sigma
188
+ sigma = s / (b * a1) + delta_sigma
189
189
  end
190
190
 
191
191
  tmp = sinU1 * sin_sigma - cosU1 * cos_sigma * cos_alpha1
192
- lat2 = Math.atan2(sinU1 * cos_sigma + cosU1 * sin_sigma * cos_alpha1, (1-f) * Math.sqrt(sin_alpha * sin_alpha + tmp * tmp))
192
+ lat2 = Math.atan2(sinU1 * cos_sigma + cosU1 * sin_sigma * cos_alpha1, (1 - f) * Math.sqrt(sin_alpha * sin_alpha + tmp * tmp))
193
193
  lambda_v = Math.atan2(sin_sigma * sin_alpha1, cosU1 * cos_sigma - sinU1 * sin_sigma * cos_alpha1)
194
- c = f/16 * cos_2_alpha * (4 + f * (4-3 * cos_2_alpha))
195
- l = lambda_v - (1-c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m))) #difference in longitude
194
+ c = f / 16 * cos_2_alpha * (4 + f * (4 - 3 * cos_2_alpha))
195
+ l = lambda_v - (1 - c) * f * sin_alpha * (sigma + c * sin_sigma * (cos_2_sigma_m + c * cos_sigma * (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m))) # difference in longitude
196
196
 
197
- #sigma2 = Math.atan2(sin_alpha, -tmp) # reverse azimuth
197
+ # sigma2 = Math.atan2(sin_alpha, -tmp) # reverse azimuth
198
198
 
199
- return Vincenty.new(lat2, @longitude + l, 0, true);
199
+ return Vincenty.new(lat2, @longitude + l, 0, true)
200
200
  end
201
-
202
-
203
201
  end