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