haversine 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,36 @@
1
- = haversine
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
- == Contributing to haversine
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
- == Copyright
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
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.1.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}
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.rdoc"
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.rdoc",
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
- class << self
49
-
50
- [:miles, :km, :feet, :meters].each do |unit|
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
- dlon_rad = dlon * RAD_PER_DEG
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
- dMi = Rmiles * c # delta between the two points in miles
99
- dKm = Rkm * c # delta in kilometers
100
- dFeet = Rfeet * c # delta in feet
101
- dMeters = Rmeters * c # delta in meters
146
+ Distance.new c
147
+ end
102
148
 
103
- distances[:miles] = Distance.in_miles dMi
104
- distances[:km] = Distance.in_km dKm
105
- distances[:feet] = Distance.in_feet dFeet
106
- distances[:meters] = Distance.in_meters dMeters
107
- distances
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
@@ -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[:meters]}"
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
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.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-12 00:00:00 +01:00
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.rdoc
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.rdoc
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: -2632364842243364495
117
+ hash: 3157735557663503756
117
118
  segments:
118
119
  - 0
119
120
  version: "0"