haversine 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{README.rdoc → README.textile} +28 -3
- data/VERSION +1 -1
- data/haversine.gemspec +5 -4
- data/lib/haversine.rb +84 -37
- data/lib/haversine/core_ext.rb +19 -0
- data/spec/haversine_spec.rb +4 -7
- metadata +7 -6
@@ -1,11 +1,36 @@
|
|
1
|
-
|
1
|
+
h1. Haversine - geo distance calculations
|
2
2
|
|
3
3
|
Calculates the haversine distance between two locations using longitude and latitude.
|
4
4
|
This is done using Math formulas without resorting to Active Record or SQL DB functionality
|
5
5
|
|
6
6
|
This is meant to ba a replacement for all those geocode and geolocation utils out there that use built in SQL DB functionality for their calculations!
|
7
7
|
|
8
|
-
|
8
|
+
h2. Install & Usage
|
9
|
+
|
10
|
+
<pre>require 'haversine'
|
11
|
+
|
12
|
+
it "should work" do
|
13
|
+
lon1 = -104.88544
|
14
|
+
lat1 = 39.06546
|
15
|
+
|
16
|
+
lon2 = -104.80
|
17
|
+
lat2 = lat1
|
18
|
+
|
19
|
+
dist = Haversine.distance( lat1, lon1, lat2, lon2 )
|
20
|
+
|
21
|
+
puts "the distance from #{lat1}, #{lon1} to #{lat2}, #{lon2} is: #{dist[:meters].number} meters"
|
22
|
+
|
23
|
+
puts "#{dist[:feet]}"
|
24
|
+
puts "#{dist.meters}"
|
25
|
+
puts "#{dist[:km]}"
|
26
|
+
puts "#{dist[:miles]}"
|
27
|
+
dist[:km].to_s.should match(/7\.376*/)
|
28
|
+
end
|
29
|
+
</pre>
|
30
|
+
|
31
|
+
Note: Haversine is used in the "geo_magic":https://github.com/kristianmandrup/geo_magic gem
|
32
|
+
|
33
|
+
h2. Contributing to haversine
|
9
34
|
|
10
35
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
11
36
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
@@ -15,7 +40,7 @@ This is meant to ba a replacement for all those geocode and geolocation utils ou
|
|
15
40
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
16
41
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
17
42
|
|
18
|
-
|
43
|
+
h2. Copyright
|
19
44
|
|
20
45
|
Copyright (c) 2011 Kristian Mandrup. See LICENSE.txt for
|
21
46
|
further details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/haversine.gemspec
CHANGED
@@ -5,17 +5,17 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{haversine}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kristian Mandrup"]
|
12
|
-
s.date = %q{2011-01-
|
12
|
+
s.date = %q{2011-01-13}
|
13
13
|
s.description = %q{Calculates the haversine distance between two locations using longitude and latitude.
|
14
14
|
This is done using Math formulas without resorting to Active Record or SQL DB functionality}
|
15
15
|
s.email = %q{kmandrup@gmail.com}
|
16
16
|
s.extra_rdoc_files = [
|
17
17
|
"LICENSE.txt",
|
18
|
-
"README.
|
18
|
+
"README.textile"
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".document",
|
@@ -23,11 +23,12 @@ This is done using Math formulas without resorting to Active Record or SQL DB fu
|
|
23
23
|
"Gemfile",
|
24
24
|
"Gemfile.lock",
|
25
25
|
"LICENSE.txt",
|
26
|
-
"README.
|
26
|
+
"README.textile",
|
27
27
|
"Rakefile",
|
28
28
|
"VERSION",
|
29
29
|
"haversine.gemspec",
|
30
30
|
"lib/haversine.rb",
|
31
|
+
"lib/haversine/core_ext.rb",
|
31
32
|
"spec/haversine_spec.rb",
|
32
33
|
"spec/spec_helper.rb"
|
33
34
|
]
|
data/lib/haversine.rb
CHANGED
@@ -30,24 +30,72 @@
|
|
30
30
|
|
31
31
|
# PI = 3.1415926535
|
32
32
|
|
33
|
+
require 'haversine/core_ext'
|
34
|
+
|
33
35
|
class Haversine
|
34
36
|
|
35
37
|
RAD_PER_DEG = 0.017453293 # PI/180
|
36
38
|
|
37
|
-
# the great circle distance d will be in whatever units R is in
|
38
|
-
|
39
|
-
Rmiles = 3956 # radius of the great circle in miles
|
40
|
-
Rkm = 6371 # radius in kilometers...some algorithms use 6367
|
41
|
-
Rfeet = Rmiles * 5282 # radius in feet
|
42
|
-
Rmeters = Rkm * 1000 # radius in meters
|
43
|
-
|
44
39
|
# this is global because if computing lots of track point distances, it didn't make
|
45
40
|
# sense to new a Hash each time over potentially 100's of thousands of points
|
46
|
-
|
41
|
+
|
42
|
+
class << self
|
43
|
+
def units
|
44
|
+
[:miles, :km, :feet, :meters]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
47
48
|
class Distance
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
attr_reader :distance
|
50
|
+
|
51
|
+
def initialize distance
|
52
|
+
@distance = distance
|
53
|
+
end
|
54
|
+
|
55
|
+
def [] key
|
56
|
+
method = :"delta_#{key}"
|
57
|
+
raise ArgumentError, "Invalid unit key #{key}" if !respond_to? method
|
58
|
+
Distance.send "in_#{key}", send(method)
|
59
|
+
end
|
60
|
+
|
61
|
+
Haversine.units.each do |unit|
|
62
|
+
class_eval %{
|
63
|
+
def #{unit}
|
64
|
+
self[:#{unit}]
|
65
|
+
end
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
# the great circle distance d will be in whatever units R is in
|
72
|
+
|
73
|
+
Rmiles = 3956 # radius of the great circle in miles
|
74
|
+
Rkm = 6371 # radius in kilometers...some algorithms use 6367
|
75
|
+
Rfeet = Rmiles * 5282 # radius in feet
|
76
|
+
Rmeters = Rkm * 1000 # radius in meters
|
77
|
+
|
78
|
+
# delta between the two points in miles
|
79
|
+
def delta_miles
|
80
|
+
Rmiles * distance
|
81
|
+
end
|
82
|
+
|
83
|
+
# delta in kilometers
|
84
|
+
def delta_km
|
85
|
+
Rkm * distance
|
86
|
+
end
|
87
|
+
|
88
|
+
def delta_feet
|
89
|
+
Rfeet * distance
|
90
|
+
end
|
91
|
+
|
92
|
+
def delta_meters
|
93
|
+
Rmeters * distance
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
class << self
|
98
|
+
Haversine.units.each do |unit|
|
51
99
|
class_eval %{
|
52
100
|
def in_#{unit} number
|
53
101
|
Unit.new :#{unit}, number
|
@@ -63,47 +111,46 @@ class Haversine
|
|
63
111
|
@name = name
|
64
112
|
@number = number
|
65
113
|
end
|
114
|
+
|
115
|
+
def number
|
116
|
+
@number.round_to(precision[name])
|
117
|
+
end
|
66
118
|
|
67
119
|
def to_s
|
68
120
|
"#{number} #{name}"
|
69
121
|
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def precision
|
126
|
+
{
|
127
|
+
:feet => 0,
|
128
|
+
:meters => 2,
|
129
|
+
:km => 4,
|
130
|
+
:miles => 4
|
131
|
+
}
|
132
|
+
end
|
70
133
|
end
|
71
134
|
end
|
72
|
-
|
73
|
-
def self.distances
|
74
|
-
@distances ||= Hash.new
|
75
|
-
end
|
76
135
|
|
77
136
|
# given two lat/lon points, compute the distance between the two points using the haversine formula
|
78
137
|
# the result will be a Hash of distances which are key'd by 'mi','km','ft', and 'm'
|
79
138
|
|
80
|
-
def self.distance( lat1, lon1, lat2, lon2 )
|
139
|
+
def self.distance( lat1, lon1, lat2, lon2, units = :meters )
|
81
140
|
dlon = lon2 - lon1
|
82
141
|
dlat = lat2 - lat1
|
83
142
|
|
84
|
-
|
85
|
-
dlat_rad = dlat * RAD_PER_DEG
|
86
|
-
|
87
|
-
lat1_rad = lat1 * RAD_PER_DEG
|
88
|
-
lon1_rad = lon1 * RAD_PER_DEG
|
89
|
-
|
90
|
-
lat2_rad = lat2 * RAD_PER_DEG
|
91
|
-
lon2_rad = lon2 * RAD_PER_DEG
|
92
|
-
|
93
|
-
# puts "dlon: #{dlon}, dlon_rad: #{dlon_rad}, dlat: #{dlat}, dlat_rad: #{dlat_rad}"
|
94
|
-
|
95
|
-
a = (Math.sin(dlat_rad/2))**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * (Math.sin(dlon_rad/2))**2
|
143
|
+
a = calc(dlat, lat1, lat2, dlon)
|
96
144
|
c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
|
97
145
|
|
98
|
-
|
99
|
-
|
100
|
-
dFeet = Rfeet * c # delta in feet
|
101
|
-
dMeters = Rmeters * c # delta in meters
|
146
|
+
Distance.new c
|
147
|
+
end
|
102
148
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
149
|
+
def self.calc dlat, lat1, lat2, dlon
|
150
|
+
(Math.sin(dlat.rpd/2))**2 + Math.cos(lat1.rpd) * Math.cos((lat2.rpd)) * (Math.sin(dlon.rpd/2))**2
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.wants? unit_opts, unit
|
154
|
+
unit_opts == unit || unit_opts[unit]
|
108
155
|
end
|
109
156
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Float
|
2
|
+
def round_to(x)
|
3
|
+
(self * 10**x).round.to_f / 10**x
|
4
|
+
end
|
5
|
+
|
6
|
+
def ceil_to(x)
|
7
|
+
(self * 10**x).ceil.to_f / 10**x
|
8
|
+
end
|
9
|
+
|
10
|
+
def floor_to(x)
|
11
|
+
(self * 10**x).floor.to_f / 10**x
|
12
|
+
end
|
13
|
+
|
14
|
+
RAD_PER_DEG = 0.017453293 # PI/180
|
15
|
+
|
16
|
+
def rpd
|
17
|
+
self * RAD_PER_DEG
|
18
|
+
end
|
19
|
+
end
|
data/spec/haversine_spec.rb
CHANGED
@@ -10,15 +10,12 @@ describe "Haversine" do
|
|
10
10
|
|
11
11
|
dist = Haversine.distance( lat1, lon1, lat2, lon2 )
|
12
12
|
|
13
|
-
puts "the distance from #{lat1}, #{lon1} to #{lat2}, #{lon2} is:"
|
13
|
+
puts "the distance from #{lat1}, #{lon1} to #{lat2}, #{lon2} is: #{dist[:meters].number} meters"
|
14
14
|
|
15
|
-
puts dist[:meters].number
|
16
|
-
|
17
|
-
puts "#{dist[:miles]}"
|
18
|
-
puts "#{dist[:km]}"
|
19
15
|
puts "#{dist[:feet]}"
|
20
|
-
puts "#{dist
|
21
|
-
|
16
|
+
puts "#{dist.meters}"
|
17
|
+
puts "#{dist[:km]}"
|
18
|
+
puts "#{dist[:miles]}"
|
22
19
|
dist[:km].to_s.should match(/7\.376*/)
|
23
20
|
end
|
24
21
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Kristian Mandrup
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-13 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -85,18 +85,19 @@ extensions: []
|
|
85
85
|
|
86
86
|
extra_rdoc_files:
|
87
87
|
- LICENSE.txt
|
88
|
-
- README.
|
88
|
+
- README.textile
|
89
89
|
files:
|
90
90
|
- .document
|
91
91
|
- .rspec
|
92
92
|
- Gemfile
|
93
93
|
- Gemfile.lock
|
94
94
|
- LICENSE.txt
|
95
|
-
- README.
|
95
|
+
- README.textile
|
96
96
|
- Rakefile
|
97
97
|
- VERSION
|
98
98
|
- haversine.gemspec
|
99
99
|
- lib/haversine.rb
|
100
|
+
- lib/haversine/core_ext.rb
|
100
101
|
- spec/haversine_spec.rb
|
101
102
|
- spec/spec_helper.rb
|
102
103
|
has_rdoc: true
|
@@ -113,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
114
|
requirements:
|
114
115
|
- - ">="
|
115
116
|
- !ruby/object:Gem::Version
|
116
|
-
hash:
|
117
|
+
hash: 3157735557663503756
|
117
118
|
segments:
|
118
119
|
- 0
|
119
120
|
version: "0"
|