vincenty_distance 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/vincenty.rb +106 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ee895e61add3011146f78ddedcba155e59d63acd
4
+ data.tar.gz: 306e3f4f06f5080c094ce5287a9afb2285f2e447
5
+ SHA512:
6
+ metadata.gz: a39fe6439a86f5c6c2481d075746702ef3f5305d3f4365da695a643436899b03a03affd6c12ebf52a7b97af49627561ebe63dfc48823a065229953cbab3ed637
7
+ data.tar.gz: 218cb0ded3da4216f231a26c576a609d35a6e75c1ddbc4e2d6e7897cc34903096ed81ebf473f11d040af6db30888bcc18f5a9bda821ef62a37d6dc22048d6c96
data/lib/vincenty.rb ADDED
@@ -0,0 +1,106 @@
1
+ module Vincenty
2
+ class FailToConverge < StandardError; end
3
+
4
+ module Trigonometry
5
+ def deg_to_rad(angle_in_degrees)
6
+ angle_in_degrees * Math::PI / 180
7
+ end
8
+ end
9
+
10
+ extend Trigonometry
11
+
12
+ EQUATORIAL_RADIUS = 6_378_137.0
13
+ POLAR_RADIUS = 6_356_752.31424518
14
+ FLATTENING = (EQUATORIAL_RADIUS - POLAR_RADIUS) / EQUATORIAL_RADIUS
15
+
16
+ CONVERGENCE_THRESHOLD = 1e-12 # i.e. 0.06 mm error
17
+ MAX_ITERATIONS = 200
18
+
19
+ def distance_between_points(first, second)
20
+ lat1 = deg_to_rad(first[:latitude])
21
+ lon1 = deg_to_rad(first[:longitude])
22
+ lat2 = deg_to_rad(second[:latitude])
23
+ lon2 = deg_to_rad(second[:longitude])
24
+
25
+ return 0 if lat1 == lat2 && lon1 == lon2
26
+
27
+ lat1_sign = lat1.negative? ? -1 : 1
28
+ geodetic_lat1 = if (Math::PI / 2 - lat1.abs).abs < 1.0e-10
29
+ lat1_sign * (Math::PI / 2 - 1e-10)
30
+ else
31
+ lat1
32
+ end
33
+
34
+ lat2_sign = lat2.negative? ? -1 : 1
35
+ geodetic_lat2 = if (Math::PI / 2 - lat2.abs).abs < 1.0e-10
36
+ lat2_sign * (Math::PI / 2 - 1e-10)
37
+ else
38
+ lat2
39
+ end
40
+
41
+ difference_in_longitude = (lon2 - lon1).abs
42
+ if difference_in_longitude > Math::PI
43
+ difference_in_longitude = 2 * Math::PI - difference_in_longitude
44
+ end
45
+
46
+ # latitude on the auxiliary sphere
47
+ reduced_latitude1 = Math.atan((1 - FLATTENING) * Math.tan(geodetic_lat1))
48
+ reduced_latitude2 = Math.atan((1 - FLATTENING) * Math.tan(geodetic_lat2))
49
+
50
+ sin_reduced_latitude1 = Math.sin(reduced_latitude1)
51
+ cos_reduced_latitude1 = Math.cos(reduced_latitude1)
52
+ sin_reduced_latitude2 = Math.sin(reduced_latitude2)
53
+ cos_reduced_latitude2 = Math.cos(reduced_latitude2)
54
+
55
+ lambda_v = difference_in_longitude
56
+ iteration_index = 0
57
+
58
+ while iteration_index < MAX_ITERATIONS
59
+ sin_lambda_v = Math.sin(lambda_v)
60
+ cos_lambda_v = Math.cos(lambda_v)
61
+
62
+ sin_sigma = Math.sqrt(
63
+ (cos_reduced_latitude2 * sin_lambda_v)**2 +
64
+ (cos_reduced_latitude1 * sin_reduced_latitude2 -
65
+ sin_reduced_latitude1 * cos_reduced_latitude2 * cos_lambda_v)**2
66
+ )
67
+
68
+ cos_sigma =
69
+ sin_reduced_latitude1 * sin_reduced_latitude2 +
70
+ cos_reduced_latitude1 * cos_reduced_latitude2 * cos_lambda_v
71
+
72
+ sigma = Math.atan2(sin_sigma, cos_sigma)
73
+ sin_alpha = cos_reduced_latitude1 * cos_reduced_latitude2 * sin_lambda_v / sin_sigma
74
+ cos_sq_alpha = 1 - sin_alpha * sin_alpha
75
+ cos_2_sigma_m = cos_sigma - 2 * sin_reduced_latitude1 * sin_reduced_latitude2 / cos_sq_alpha
76
+ cos_2_sigma_m = 0 if cos_2_sigma_m.nan?
77
+ c = FLATTENING / 16 * cos_sq_alpha * (4 + FLATTENING * (4 - 3 * cos_sq_alpha))
78
+
79
+ lambda_prev = lambda_v
80
+ # use cos_2_sigma_m=0 when over equatorial lines
81
+ lambda_v =
82
+ difference_in_longitude +
83
+ (1 - c) * FLATTENING * sin_alpha * (sigma + c * sin_sigma *
84
+ (cos_2_sigma_m + c * cos_sigma *
85
+ (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m)))
86
+
87
+ break if (lambda_v - lambda_prev).abs < CONVERGENCE_THRESHOLD
88
+
89
+ iteration_index += 1
90
+ end
91
+
92
+ raise FailToConverge if (lambda_v - lambda_prev).abs > CONVERGENCE_THRESHOLD
93
+
94
+ u_sq = cos_sq_alpha * (EQUATORIAL_RADIUS**2 - POLAR_RADIUS**2) / POLAR_RADIUS**2
95
+ a1 = 1 + u_sq / 16_384 * (4096 + u_sq * (-768 + u_sq * (320 - 175 * u_sq)))
96
+ b1 = u_sq / 1024 * (256 + u_sq * (-128 + u_sq * (74 - 47 * u_sq)))
97
+ delta_sigma = b1 * sin_sigma * (cos_2_sigma_m + b1 / 4 * (cos_sigma *
98
+ (-1 + 2 * cos_2_sigma_m * cos_2_sigma_m) - b1 / 6 * cos_2_sigma_m *
99
+ (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2_sigma_m * cos_2_sigma_m)))
100
+
101
+ distance = POLAR_RADIUS * a1 * (sigma - delta_sigma)
102
+
103
+ return distance
104
+ end
105
+ module_function :distance_between_points
106
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vincenty_distance
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aleksandr Kunin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-28 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Calculate the geographical distance between 2 points with extreme accuracy.
14
+ email: skyksandr@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/vincenty.rb
20
+ homepage: https://github.com/skyderby/vincenty_distance
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.6.8
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Vincenty distance
44
+ test_files: []