cartier 0.0.1 → 0.0.2
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.
- data/lib/cartier/gps_location.rb +16 -16
- data/lib/cartier/navigation.rb +136 -61
- data/lib/cartier/version.rb +3 -3
- data/spec/naviation_spec.rb +59 -35
- metadata +2 -2
data/lib/cartier/gps_location.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
module Cartier
|
2
|
-
class GPSLocation
|
3
|
-
attr_accessor :latitude, :longitude, :decimal_notation, :imperial
|
1
|
+
module Cartier
|
2
|
+
class GPSLocation
|
3
|
+
attr_accessor :latitude, :longitude, :decimal_notation, :imperial
|
4
4
|
#
|
5
5
|
#
|
6
6
|
# * *Args* :
|
7
|
-
# - +latitude+ -> Latitude in Degree Notation (i.e. 37.0428759)
|
8
|
-
# - +longitude+ -> Longitude in Degree Notation (i.e.
|
9
|
-
# - +decimal_notation+ -> Defaulting to true for now
|
10
|
-
# - +imperial+ -> Defaulting
|
7
|
+
# - +latitude+ -> Latitude in Degree Notation (i.e. 37.0428759)
|
8
|
+
# - +longitude+ -> Longitude in Degree Notation (i.e. -79.0428759)
|
9
|
+
# - +decimal_notation+ -> Defaulting to true for now
|
10
|
+
# - +imperial+ -> Defaulting metric system
|
11
11
|
# * *Returns* :
|
12
12
|
# - a GPSLocation object
|
13
13
|
# * *Raises* :
|
14
14
|
# - ++ ->
|
15
|
-
#
|
16
|
-
def initialize(latitude=nil, longitude=nil, decimal_notation=true, imperial=
|
17
|
-
self.latitude = latitude
|
18
|
-
self.longitude = longitude
|
19
|
-
self.decimal_notation = decimal_notation
|
20
|
-
self.imperial = imperial
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
15
|
+
#
|
16
|
+
def initialize(latitude=nil, longitude=nil, decimal_notation=true, imperial=false)
|
17
|
+
self.latitude = latitude
|
18
|
+
self.longitude = longitude
|
19
|
+
self.decimal_notation = decimal_notation
|
20
|
+
self.imperial = imperial
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
24
|
end
|
data/lib/cartier/navigation.rb
CHANGED
@@ -1,62 +1,137 @@
|
|
1
|
-
module Cartier
|
2
|
-
|
3
|
-
# Encapsulating class for Navigation calculations
|
4
|
-
class Navigation
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# -
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# - +
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
def self.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
|
1
|
+
module Cartier
|
2
|
+
|
3
|
+
# Encapsulating class for Navigation calculations
|
4
|
+
class Navigation
|
5
|
+
#radius in km
|
6
|
+
EARTH_RADIUS = 6371
|
7
|
+
RADIANS = 57.29578
|
8
|
+
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# * *Args* :
|
12
|
+
# - +location+ -> GPSLocation object representing current location
|
13
|
+
# - +longitude+ -> GPSLocation object representing current destination
|
14
|
+
# * *Returns* :
|
15
|
+
# - distance in km
|
16
|
+
def self.haversine_distance(location, destination)
|
17
|
+
delta_lat = (destination.latitude.to_f - location.latitude.to_f) * Math::PI/180
|
18
|
+
delta_long = (destination.longitude.to_f - location.longitude.to_f) * Math::PI/180
|
19
|
+
|
20
|
+
latitude_1 = (location.latitude.to_f) * Math::PI/180
|
21
|
+
latitude_2 = (destination.latitude.to_f) * Math::PI/180
|
22
|
+
|
23
|
+
a = Math.sin(delta_lat/2)**2 + Math.sin(delta_long/2)**2 * Math.cos(latitude_1) * Math.cos(latitude_2)
|
24
|
+
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
|
25
|
+
EARTH_RADIUS * c
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
#
|
30
|
+
# * *Args* :
|
31
|
+
# - +location+ -> GPSLocation object representing current location
|
32
|
+
# - +longitude+ -> GPSLocation object representing current destination
|
33
|
+
# * *Returns* :
|
34
|
+
# - distance in km
|
35
|
+
def self.equirectangular_projection(location, destination)
|
36
|
+
latitude_1 = (location.latitude.to_f) * Math::PI/180
|
37
|
+
latitude_2 = (destination.latitude.to_f) * Math::PI/180
|
38
|
+
|
39
|
+
longitude_1 = (location.longitude.to_f) * Math::PI/180
|
40
|
+
longitude_2 = (destination.longitude.to_f) * Math::PI/180
|
41
|
+
|
42
|
+
x = (longitude_2 - longitude_1) * Math.cos((latitude_1+latitude_2)/2)
|
43
|
+
y = latitude_2 - latitude_1
|
44
|
+
Math.sqrt(x*x + y*y) * EARTH_RADIUS
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
#
|
49
|
+
# * *Args* :
|
50
|
+
# - +location_here+ -> GPSLocation object representing current location
|
51
|
+
# - +array_of_locations+ -> an Array of GPSLocation objects
|
52
|
+
# * *Returns* :
|
53
|
+
# - GPSLocation object representing closest location
|
54
|
+
#
|
55
|
+
def self.closest_point(location_here, array_of_locations=nil)
|
56
|
+
array_of_locations.sort do |a, b|
|
57
|
+
self.haversine_distance(location_here, a) <=> self.haversine_distance(location_here, b)
|
58
|
+
end.first
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
#
|
63
|
+
# * *Args* :
|
64
|
+
# - +location+ -> GPSLocation object representing current location
|
65
|
+
# - +destination+ -> GPSLocation object representing destination
|
66
|
+
# * *Returns* :
|
67
|
+
# - Bearing in Degrees (not radians)
|
68
|
+
#
|
69
|
+
def self.get_bearing(location, destination)
|
70
|
+
location_long = location.longitude.to_f * Math::PI/180
|
71
|
+
location_latitude = location.latitude.to_f * Math::PI/180
|
72
|
+
destination_long = destination.longitude.to_f
|
73
|
+
destination_latitude = destination.latitude.to_f
|
74
|
+
|
75
|
+
delta_long = (destination_long - location_long) * Math::PI/180
|
76
|
+
y = Math.sin(delta_long) * Math.cos(destination_latitude)
|
77
|
+
x = Math.cos(location_latitude) * Math.sin(destination_latitude) - Math.sin(location_latitude) * Math.cos(destination_latitude) * Math.cos(delta_long)
|
78
|
+
theta = (Math.atan2(y, x) / Math::PI) * 180
|
79
|
+
(theta + 360) % 360
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
#
|
84
|
+
#
|
85
|
+
# * *Args* :
|
86
|
+
# - +location+ -> GPSLocation object representing current location
|
87
|
+
# - +destination+ -> GPSLocation object representing destination
|
88
|
+
# * *Returns* :
|
89
|
+
# - Cartier::GPSLocation object representing midpoint
|
90
|
+
#
|
91
|
+
def self.get_midpoint(location, destination)
|
92
|
+
latitude_1 = location.latitude.to_f * Math::PI/180
|
93
|
+
longitude_1 = location.longitude.to_f * Math::PI/180
|
94
|
+
latitude_2 = destination.latitude.to_f * Math::PI/180
|
95
|
+
delta_long = (destination.longitude.to_f - location.longitude.to_f) * Math::PI/180
|
96
|
+
|
97
|
+
bx = Math.cos(latitude_2) * Math.cos(delta_long)
|
98
|
+
by = Math.cos(latitude_2) * Math.sin(delta_long)
|
99
|
+
|
100
|
+
latitude_3 = Math.atan2(Math.sin(latitude_1) + Math.sin(latitude_2),
|
101
|
+
Math.sqrt( (Math.cos(latitude_1)+bx)*(Math.cos(latitude_1)+bx) + by*by))
|
102
|
+
longitude_3 = longitude_1 + Math.atan2(by, Math.cos(latitude_1) + bx)
|
103
|
+
longitude_3 = (longitude_3+3*Math::PI) % (2*Math::PI) - Math::PI
|
104
|
+
|
105
|
+
latitude_3 = latitude_3 * RADIANS
|
106
|
+
longitude_3 = longitude_3 * RADIANS
|
107
|
+
Cartier::GPSLocation.new(latitude_3.to_s, longitude_3.to_s)
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
#
|
112
|
+
# * *Args* :
|
113
|
+
# - +location+ -> Cartier::GPSLocation
|
114
|
+
# - +bearing+ -> Bearing in Degrees
|
115
|
+
# - +distance+ -> Distance in km
|
116
|
+
# * *Returns* :
|
117
|
+
# - Cartier::GPSLocation object representing final point
|
118
|
+
#
|
119
|
+
def self.get_destination_point(location, bearing, distance)
|
120
|
+
distance_in_radians = distance / EARTH_RADIUS
|
121
|
+
bearing_in_radians = bearing * Math::PI/180
|
122
|
+
|
123
|
+
latitude_1 = location.latitude.to_f * Math::PI/180
|
124
|
+
longitude_1 = location.longitude.to_f * Math::PI/180
|
125
|
+
|
126
|
+
latitude_2 = Math.asin( Math.sin(latitude_1)*Math.cos(distance_in_radians) +
|
127
|
+
Math.cos(latitude_1)*Math.sin(distance_in_radians)*Math.cos(bearing_in_radians) )
|
128
|
+
longitude_2 = longitude_1 + Math.atan2(Math.sin(bearing_in_radians)*Math.sin(distance_in_radians)*Math.cos(latitude_1),
|
129
|
+
Math.cos(distance_in_radians)-Math.sin(latitude_1)*Math.sin(latitude_2))
|
130
|
+
longitude_2 = (longitude_2+3*Math::PI) % (2*Math::PI) - Math::PI
|
131
|
+
latitude_2 = latitude_2 * RADIANS
|
132
|
+
longitude_2 = longitude_2 * RADIANS
|
133
|
+
Cartier::GPSLocation.new(latitude_2.to_s, longitude_2.to_s)
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
62
137
|
end
|
data/lib/cartier/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module Cartier
|
2
|
-
VERSION = "0.0.
|
3
|
-
end
|
1
|
+
module Cartier
|
2
|
+
VERSION = "0.0.2"
|
3
|
+
end
|
data/spec/naviation_spec.rb
CHANGED
@@ -1,36 +1,60 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'factories/location_factory'
|
3
|
-
|
4
|
-
include Cartier
|
5
|
-
|
6
|
-
describe Cartier::Navigation do
|
7
|
-
it "
|
8
|
-
@location = FactoryGirl.build(:cn_tower)
|
9
|
-
Cartier::Navigation.haversine_distance(@location, @location).should == 0.0
|
10
|
-
end
|
11
|
-
|
12
|
-
it "
|
13
|
-
@cn_tower = FactoryGirl.build(:cn_tower)
|
14
|
-
@washington_monument = FactoryGirl.build(:washington_monument)
|
15
|
-
Cartier::Navigation.haversine_distance(@cn_tower, @washington_monument).should == 563.7658958719776
|
16
|
-
end
|
17
|
-
|
18
|
-
it ".
|
19
|
-
@
|
20
|
-
@
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@
|
33
|
-
@ago
|
34
|
-
|
35
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'factories/location_factory'
|
3
|
+
|
4
|
+
include Cartier
|
5
|
+
|
6
|
+
describe Cartier::Navigation do
|
7
|
+
it ".haversine_distance to self should be 0.0" do
|
8
|
+
@location = FactoryGirl.build(:cn_tower)
|
9
|
+
Cartier::Navigation.haversine_distance(@location, @location).should == 0.0
|
10
|
+
end
|
11
|
+
|
12
|
+
it ".haversine_distance to washington monument should not be zero" do
|
13
|
+
@cn_tower = FactoryGirl.build(:cn_tower)
|
14
|
+
@washington_monument = FactoryGirl.build(:washington_monument)
|
15
|
+
Cartier::Navigation.haversine_distance(@cn_tower, @washington_monument).should == 563.7658958719776
|
16
|
+
end
|
17
|
+
|
18
|
+
it ".equirectangular_projection to self should be 0.0" do
|
19
|
+
@location = FactoryGirl.build(:cn_tower)
|
20
|
+
Cartier::Navigation.equirectangular_projection(@location, @location).should == 0.0
|
21
|
+
end
|
22
|
+
|
23
|
+
it ".equirectangular_projection to washington monument should not be zero" do
|
24
|
+
@cn_tower = FactoryGirl.build(:cn_tower)
|
25
|
+
@washington_monument = FactoryGirl.build(:washington_monument)
|
26
|
+
Cartier::Navigation.equirectangular_projection(@cn_tower, @washington_monument).should == 563.8330522056333
|
27
|
+
end
|
28
|
+
|
29
|
+
it ".closest_point should find the closest point" do
|
30
|
+
@here = FactoryGirl.build(:cn_tower)
|
31
|
+
@ago = FactoryGirl.build(:ago)
|
32
|
+
@washington_monument = FactoryGirl.build(:washington_monument)
|
33
|
+
Cartier::Navigation.closest_point(@here, [@ago, @washington_monument]).should == @ago
|
34
|
+
end
|
35
|
+
|
36
|
+
it ".get_bearing should calculate the bearing between cn_tower and ago properly" do
|
37
|
+
@here = FactoryGirl.build(:cn_tower)
|
38
|
+
@ago = FactoryGirl.build(:ago)
|
39
|
+
Cartier::Navigation.get_bearing(@here, @ago).should == 248.24661021678037
|
40
|
+
end
|
41
|
+
|
42
|
+
it ".get_midpoint should find the proper location between two points" do
|
43
|
+
@here = FactoryGirl.build(:cn_tower)
|
44
|
+
@there = FactoryGirl.build(:washington_monument)
|
45
|
+
expected = Cartier::GPSLocation.new("41.277422062362376", "-78.16834424548739")
|
46
|
+
Cartier::Navigation.get_midpoint(@here, @there).latitude.should == expected.latitude
|
47
|
+
Cartier::Navigation.get_midpoint(@here, @there).longitude.should == expected.longitude
|
48
|
+
end
|
49
|
+
|
50
|
+
it ".get_destination_point will find the proper location from current location, bearing and distance" do
|
51
|
+
@here = FactoryGirl.build(:cn_tower)
|
52
|
+
@bearing = 180
|
53
|
+
@distance = 200
|
54
|
+
expected = Cartier::GPSLocation.new("43.64775274320249", "-79.3870789598738")
|
55
|
+
Cartier::Navigation.get_destination_point(@here, @bearing, @distance).latitude.should == expected.latitude
|
56
|
+
Cartier::Navigation.get_destination_point(@here, @bearing, @distance).longitude.should == expected.longitude
|
57
|
+
end
|
58
|
+
|
59
|
+
|
36
60
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cartier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|