vincenty_distance 1.0.0

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.
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: []