geodesics 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9fb097bb88b35fc4b47610fbc4781aadebadbab28f6b4c207b144645515d9a19
4
+ data.tar.gz: 1f1c7ccb9105b0c3dcd8be45fb5bd512028721affd3cf65d59509f358cee92c5
5
+ SHA512:
6
+ metadata.gz: e102a7f5f1158ae93228e37c1e667ac518f23b47683cf8626ca833f3482c94feb6002bc514c58ac91ebcc2c4e52083f32256c91e1fb4a6b7b615f539c657206b
7
+ data.tar.gz: 39a076d912a139be79544998462b247e6a425fd4ac7667e2a2dee6bafab64855fbbad0c7d18d21a2d2008b92da8627c6a13b9efc726175cc70b40a15664caf66
data/lib/geodesics.rb ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'geodesics/strategies/lambert'
4
+ require 'geodesics/point'
5
+ require 'forwardable'
6
+
7
+ class Geodesics
8
+ class << self
9
+ extend Forwardable
10
+ def_delegators :new, :distance, :distance_degree, :distance_radian
11
+ end
12
+
13
+ STRATEGY = Strategies::Lambert.new
14
+
15
+ def initialize(strategy: STRATEGY)
16
+ @strategy = strategy
17
+ end
18
+
19
+ def distance_radian(latitude1, longitude1, latitude2, longitude2)
20
+ @strategy.distance(
21
+ Radian.new(latitude1, longitude1),
22
+ Radian.new(latitude2, longitude2)
23
+ )
24
+ end
25
+
26
+ def distance_degree(latitude1, longitude1, latitude2, longitude2)
27
+ @strategy.distance(
28
+ Degree.new(latitude1, longitude1).to_radian,
29
+ Degree.new(latitude2, longitude2).to_radian
30
+ )
31
+ end
32
+
33
+ alias distance distance_degree
34
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Geodesics
4
+ module CentralAngles
5
+ # https://en.wikipedia.org/wiki/Versine#Haversine
6
+ class Haversine
7
+ def call(point1, point2)
8
+ 2 * Math.asin(
9
+ Math.sqrt(
10
+ Math.sin((point1.latitude - point2.latitude).abs / 2)**2 +
11
+ Math.cos(point1.latitude) * Math.cos(point2.latitude) *
12
+ Math.sin((point1.longitude - point2.longitude).abs / 2)**2
13
+ )
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Geodesics
4
+ module CentralAngles
5
+ # https://en.wikipedia.org/wiki/Spherical_law_of_cosines
6
+ class Spherical
7
+ def call(point1, point2)
8
+ Math.acos(
9
+ Math.sin(point1.latitude) * Math.sin(point2.latitude) +
10
+ Math.cos(point1.latitude) * Math.cos(point2.latitude) *
11
+ Math.cos((point1.longitude - point2.longitude).abs)
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Geodesics
4
+ module CentralAngles
5
+ # https://en.wikipedia.org/wiki/Vincenty%27s_formulae
6
+ class Vincenty
7
+ def call(point1, point2)
8
+ delta_longitude = (point1.longitude - point2.longitude).abs
9
+ Math.atan(
10
+ Math.sqrt(
11
+ (Math.cos(point2.latitude) * Math.sin(delta_longitude))**2 +
12
+ (Math.cos(point1.latitude) * Math.sin(point2.latitude) - Math.sin(point1.latitude) * Math.cos(point2.latitude) * Math.cos(delta_longitude))**2
13
+ ) / (
14
+ Math.sin(point1.latitude) * Math.sin(point2.latitude) + Math.cos(point1.latitude) * Math.cos(point2.latitude) * Math.cos(delta_longitude)
15
+ )
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Geodesics
4
+ class Point
5
+ RADIAN = Math::PI / 180
6
+ DEGREE = 180 / Math::PI
7
+
8
+ attr_reader :latitude, :longitude
9
+
10
+ def initialize(latitude, longitude)
11
+ @latitude = latitude
12
+ @longitude = longitude
13
+ end
14
+
15
+ def ==(other)
16
+ latitude == other.latitude && longitude == other.longitude
17
+ end
18
+
19
+ def to_radian
20
+ self
21
+ end
22
+
23
+ def to_degree
24
+ self
25
+ end
26
+ end
27
+
28
+ class Radian < Point
29
+ def to_degree
30
+ Degree.new(@latitude * DEGREE, @longitude * DEGREE)
31
+ end
32
+
33
+ def ==(other)
34
+ other.is_a?(Degree) ? super(other.to_radian) : super
35
+ end
36
+ end
37
+
38
+ class Degree < Point
39
+ def to_radian
40
+ Radian.new(@latitude * RADIAN, @longitude * RADIAN)
41
+ end
42
+
43
+ def ==(other)
44
+ other.is_a?(Radian) ? super(other.to_degree) : super
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'geodesics/central_angles/haversine'
4
+
5
+ class Geodesics
6
+ module Strategies
7
+ class Lambert
8
+ INVERSE_FLATTENING = 298.257223563 # WGS 84
9
+ FLATTENING = 1.0 / INVERSE_FLATTENING
10
+ EQUATORIAL_RADIUS = 6_378_137
11
+ CENTRAL_ANGLE = CentralAngles::Haversine.new
12
+
13
+ def initialize(
14
+ equatorial_radius: EQUATORIAL_RADIUS,
15
+ flattening: FLATTENING,
16
+ central_angle: CENTRAL_ANGLE
17
+ )
18
+ @equatorial_radius = equatorial_radius
19
+ @flattening = flattening
20
+ @central_angle = central_angle
21
+ end
22
+
23
+ def distance(point1, point2)
24
+ sigma = @central_angle.call(point1, point2)
25
+ beta1 = parametric_latitude(point1.latitude)
26
+ beta2 = parametric_latitude(point2.latitude)
27
+ p = (beta1 + beta2) / 2
28
+ q = (beta2 - beta1) / 2
29
+ x = (sigma - sin(sigma)) * sin(p)**2 * cos(q)**2 / cos(sigma / 2)**2
30
+ y = (sigma + sin(sigma)) * cos(p)**2 * sin(q)**2 / sin(sigma / 2)**2
31
+ @equatorial_radius * (sigma - @flattening * (x + y) / 2)
32
+ end
33
+
34
+ private
35
+
36
+ def parametric_latitude(latitude)
37
+ Math.atan((1 - @flattening) * Math.tan(latitude))
38
+ end
39
+
40
+ def sin(radian)
41
+ v = Math.sin(radian)
42
+ v < 1e-10 ? 1e-10 : v
43
+ end
44
+
45
+ def cos(radian)
46
+ v = Math.cos(radian)
47
+ v < 1e-10 ? 1e-10 : v
48
+ end
49
+ end
50
+ end
51
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geodesics
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jian Weihang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.11.3
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.11.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 12.3.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 12.3.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.68.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.68.1
55
+ description: geodesics calculates the geodesic distance between 2 points with latitude
56
+ and longitude on ellipsoid Earth using Lambert's formula.
57
+ email: tonytonyjan@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/geodesics.rb
63
+ - lib/geodesics/central_angles/haversine.rb
64
+ - lib/geodesics/central_angles/spherical.rb
65
+ - lib/geodesics/central_angles/vincenty.rb
66
+ - lib/geodesics/point.rb
67
+ - lib/geodesics/strategies/lambert.rb
68
+ homepage: https://github.com/tonytonyjan/geodesics
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 3.0.2
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: geodesics calculates the geodesic distance between 2 points with latitude
91
+ and longitude on ellipsoid Earth using Lambert's formula.
92
+ test_files: []