haversine 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@ GEM
7
7
  bundler (~> 1.0.0)
8
8
  git (>= 1.2.5)
9
9
  rake
10
- rake (0.8.7)
10
+ rake (0.9.0)
11
11
  rcov (0.9.9)
12
12
  rspec (2.4.0)
13
13
  rspec-core (~> 2.4.0)
@@ -1,31 +1,52 @@
1
- h1. Haversine - geo distance calculations
1
+ h1. Haversine Distance
2
2
 
3
- Calculates the haversine distance between two locations using longitude and latitude.
4
- This is done using Math formulas without resorting to Active Record or SQL DB functionality
3
+ This gem calculates the Haversine distance between two points given their longitude and latitude. This is done using trigonometry without ActiveRecord or SQL. See http://en.wikipedia.org/wiki/Haversine_formula for details on the Haversine formula.
5
4
 
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!
5
+ This is a replacement for all geo libraries that use built-in SQL DB functionality for their calculations.
7
6
 
8
7
  h2. Install & Usage
9
8
 
10
- <pre>require 'haversine'
9
+ Install this gem with @gem install haversine@. Calling @Haversine.distance@ with four lat/lon coordinates returns a @Haversine::Distance@ object which can provide output in kilometers, meters, miles, or feet.
11
10
 
12
- it "should work" do
13
- lon1 = -104.88544
14
- lat1 = 39.06546
11
+ <pre>
12
+ require 'haversine'
15
13
 
16
- lon2 = -104.80
17
- lat2 = lat1
14
+ distance = Haversine.distance(35.61488, 139.5813, 48.85341, 2.3488)
18
15
 
19
- dist = Haversine.distance( lat1, lon1, lat2, lon2 )
16
+ distance.class => Haversine::Distance
17
+ distance.to_miles => 6032.71091869803
18
+ distance.to_kilometers => 9715.47049115903
19
+ distance.to_meters => 9715470.49115903
20
+ distance.to_feet => 31852713.6507256
21
+ </pre>
22
+
23
+ Convenience aliases for the measurements exist:
24
+ <pre>
25
+ distance.to_kilometers == distance.to_km
26
+ distance.to_meters == distance.to_m
27
+ distance.to_miles == distance.to_mi
28
+ distance.to_feet == distance.to_ft
29
+ </pre>
20
30
 
21
- puts "the distance from #{lat1}, #{lon1} to #{lat2}, #{lon2} is: #{dist[:meters].number} meters"
31
+ Note that @to_m@ is the distance in meters, not miles.
22
32
 
23
33
  puts "#{dist[:feet]}"
24
34
  puts "#{dist.meters}"
25
35
  puts "#{dist[:km]}"
26
36
  puts "#{dist[:miles]}"
37
+ puts "#{dist.to_mi}"
38
+ puts "#{dist.to_miles_}"
27
39
  dist[:km].to_s.should match(/7\.376*/)
40
+ dist.to_km.to_s.should match(/7\.376*/)
28
41
  end
42
+
43
+ If you have lat/lon pairs stored in an array, you can alternately provide two arrays when calling @Haversine.distance@:
44
+
45
+ <pre>
46
+ require 'haversine'
47
+ new_york_city = [40.71427, -74.00597]
48
+ santiago_chile = [-33.42628, -70.56656]
49
+ Haversine.distance(new_york_city, santiago_chile).to_miles => 5123.73
29
50
  </pre>
30
51
 
31
52
  Note: Haversine is used in the "geo_magic":https://github.com/kristianmandrup/geo_magic gem
@@ -43,5 +64,4 @@ h2. Contributing to haversine
43
64
  h2. Copyright
44
65
 
45
66
  Copyright (c) 2011 Kristian Mandrup. See LICENSE.txt for
46
- further details.
47
-
67
+ further details.
data/Rakefile CHANGED
@@ -8,6 +8,7 @@ rescue Bundler::BundlerError => e
8
8
  exit e.status_code
9
9
  end
10
10
  require 'rake'
11
+ include Rake::DSL if defined? Rake::DSL
11
12
 
12
13
  require 'jeweler'
13
14
  Jeweler::Tasks.new do |gem|
@@ -19,7 +20,7 @@ Jeweler::Tasks.new do |gem|
19
20
  gem.description = %Q{Calculates the haversine distance between two locations using longitude and latitude.
20
21
  This is done using Math formulas without resorting to Active Record or SQL DB functionality}
21
22
  gem.email = "kmandrup@gmail.com"
22
- gem.authors = ["Kristian Mandrup"]
23
+ gem.authors = ["Kristian Mandrup", "Ryan Greenberg"]
23
24
  # Include your dependencies below. Runtime dependencies are required when using your gem,
24
25
  # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
25
26
  # gem.add_runtime_dependency 'jabber4r', '> 0.1'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{haversine}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Kristian Mandrup"]
12
- s.date = %q{2011-01-13}
11
+ s.authors = [%q{Kristian Mandrup}, %q{Ryan Greenberg}]
12
+ s.date = %q{2011-06-05}
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}
@@ -28,22 +28,23 @@ This is done using Math formulas without resorting to Active Record or SQL DB fu
28
28
  "VERSION",
29
29
  "haversine.gemspec",
30
30
  "lib/haversine.rb",
31
- "lib/haversine/core_ext.rb",
31
+ "lib/haversine/distance.rb",
32
+ "spec/distance_spec.rb",
32
33
  "spec/haversine_spec.rb",
33
34
  "spec/spec_helper.rb"
34
35
  ]
35
36
  s.homepage = %q{http://github.com/kristianmandrup/haversine}
36
- s.licenses = ["MIT"]
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = %q{1.3.7}
37
+ s.licenses = [%q{MIT}]
38
+ s.require_paths = [%q{lib}]
39
+ s.rubygems_version = %q{1.8.5}
39
40
  s.summary = %q{Calculates the haversine distance between two locations using longitude and latitude}
40
41
  s.test_files = [
42
+ "spec/distance_spec.rb",
41
43
  "spec/haversine_spec.rb",
42
44
  "spec/spec_helper.rb"
43
45
  ]
44
46
 
45
47
  if s.respond_to? :specification_version then
46
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
48
  s.specification_version = 3
48
49
 
49
50
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # LICENSE: GNU Affero GPL v3
12
12
  # The ruby implementation of the Haversine formula is free software: you can redistribute it and/or modify
13
- # it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
13
+ # it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
14
14
  #
15
15
  # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
16
16
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
@@ -27,130 +27,39 @@
27
27
  #
28
28
  # This formula can compute accurate distances between two points given latitude and longitude, even for
29
29
  # short distances.
30
-
31
- # PI = 3.1415926535
32
30
 
33
- require 'haversine/core_ext'
31
+ require 'haversine/distance'
34
32
 
35
- class Haversine
33
+ module Haversine
36
34
 
37
- RAD_PER_DEG = 0.017453293 # PI/180
38
-
39
- # this is global because if computing lots of track point distances, it didn't make
40
- # sense to new a Hash each time over potentially 100's of thousands of points
35
+ RAD_PER_DEG = Math::PI / 180
41
36
 
42
- class << self
43
- def units
44
- [:miles, :km, :feet, :meters]
45
- end
46
- end
47
-
48
- class Distance
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|
99
- class_eval %{
100
- def in_#{unit} number
101
- Unit.new :#{unit}, number
102
- end
103
- }
104
- end
37
+ # given two lat/lon points, compute the distance between the two points using the haversine formula
38
+ def self.distance(lat1, lon1, lat2=nil, lon2=nil)
39
+ # Accept two arrays of points in addition to four coordinates
40
+ if lat1.is_a?(Array) && lon1.is_a?(Array)
41
+ lat2, lon2 = lon1
42
+ lat1, lon1 = lat1
43
+ elsif lat2.nil? || lon2.nil?
44
+ raise ArgumentError
105
45
  end
106
-
107
- class Unit
108
- attr_accessor :name, :number
109
-
110
- def initialize name, number = 0
111
- @name = name
112
- @number = number
113
- end
114
46
 
115
- def number
116
- @number.round_to(precision[name])
117
- end
118
-
119
- def to_s
120
- "#{number} #{name}"
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
133
- end
134
- end
135
-
136
- # given two lat/lon points, compute the distance between the two points using the haversine formula
137
- # the result will be a Hash of distances which are key'd by 'mi','km','ft', and 'm'
138
-
139
- def self.distance( lat1, lon1, lat2, lon2, units = :meters )
140
47
  dlon = lon2 - lon1
141
48
  dlat = lat2 - lat1
142
49
 
143
50
  a = calc(dlat, lat1, lat2, dlon)
144
51
  c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
145
52
 
146
- Distance.new c
147
- end
53
+ Haversine::Distance.new(c)
54
+ end
148
55
 
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
56
+ # TODO How can this be more descriptively named?
57
+ def self.calc(dlat, lat1, lat2, dlon)
58
+ (Math.sin(rpd(dlat)/2))**2 + Math.cos(rpd(lat1)) * Math.cos((rpd(lat2))) * (Math.sin(rpd(dlon)/2))**2
151
59
  end
152
-
153
- def self.wants? unit_opts, unit
154
- unit_opts == unit || unit_opts[unit]
60
+
61
+ # Radians per degree
62
+ def self.rpd(num)
63
+ num * RAD_PER_DEG
155
64
  end
156
- end
65
+ end
@@ -0,0 +1,40 @@
1
+ module Haversine
2
+ class Distance
3
+ include Comparable
4
+
5
+ GREAT_CIRCLE_RADIUS_MILES = 3956
6
+ GREAT_CIRCLE_RADIUS_KILOMETERS = 6371 # some algorithms use 6367
7
+ GREAT_CIRCLE_RADIUS_FEET = GREAT_CIRCLE_RADIUS_MILES * 5280
8
+ GREAT_CIRCLE_RADIUS_METERS = GREAT_CIRCLE_RADIUS_KILOMETERS * 1000
9
+
10
+ attr_reader :great_circle_distance
11
+
12
+ def initialize(great_circle_distance)
13
+ @great_circle_distance = great_circle_distance
14
+ end
15
+
16
+ def to_miles
17
+ @great_circle_distance * GREAT_CIRCLE_RADIUS_MILES
18
+ end
19
+ alias_method :to_mi, :to_miles
20
+
21
+ def to_kilometers
22
+ @great_circle_distance * GREAT_CIRCLE_RADIUS_KILOMETERS
23
+ end
24
+ alias_method :to_km, :to_kilometers
25
+
26
+ def to_meters
27
+ @great_circle_distance * GREAT_CIRCLE_RADIUS_METERS
28
+ end
29
+ alias_method :to_m, :to_meters
30
+
31
+ def to_feet
32
+ @great_circle_distance * GREAT_CIRCLE_RADIUS_FEET
33
+ end
34
+ alias_method :to_ft, :to_feet
35
+
36
+ def <=>(other)
37
+ great_circle_distance <=> other.great_circle_distance
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Haversine::Distance do
4
+ describe "<=>" do
5
+ context "equality" do
6
+ it "is true when the great circle distance is equal" do
7
+ dist1 = Haversine::Distance.new(0)
8
+ dist2 = Haversine::Distance.new(0)
9
+ (dist1 == dist2).should be_true
10
+ end
11
+
12
+ it "is false when the great circle distance is not equal" do
13
+ dist1 = Haversine::Distance.new(0)
14
+ dist2 = Haversine::Distance.new(1)
15
+ (dist1 == dist2).should be_false
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,21 +1,31 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Haversine" do
4
- it "should work" do
5
- lon1 = -104.88544
6
- lat1 = 39.06546
7
-
8
- lon2 = -104.80
9
- lat2 = lat1
10
-
11
- dist = Haversine.distance( lat1, lon1, lat2, lon2 )
12
-
13
- puts "the distance from #{lat1}, #{lon1} to #{lat2}, #{lon2} is: #{dist[:meters].number} meters"
14
-
15
- puts "#{dist[:feet]}"
16
- puts "#{dist.meters}"
17
- puts "#{dist[:km]}"
18
- puts "#{dist[:miles]}"
19
- dist[:km].to_s.should match(/7\.376*/)
3
+ describe Haversine do
4
+ describe "#self.distance" do
5
+ it "returns Haversine::Distance" do
6
+ Haversine.distance(0,0,0,0).should be_a(Haversine::Distance)
7
+ end
8
+
9
+ it "accepts 4 numbers or 2 arrays as arguments" do
10
+ new_york_city = [40.71427, -74.00597]
11
+ santiago_chile = [-33.42628, -70.56656]
12
+ point_dist = Haversine.distance(new_york_city[0], new_york_city[1], santiago_chile[0], santiago_chile[1])
13
+ array_dist = Haversine.distance(new_york_city, santiago_chile)
14
+
15
+ point_dist.should be_a(Haversine::Distance)
16
+ array_dist.should be_a(Haversine::Distance)
17
+ point_dist.to_m.should == array_dist.to_m
18
+ end
19
+
20
+ it "calculates the distance between the provided lat/lon pairs" do
21
+ Haversine.distance(0,0,0,0).to_miles.should == 0
22
+ round_to(6, Haversine.distance(0,0,0,360).to_miles).should == 0
23
+ round_to(6, Haversine.distance(0,0,360,0).to_miles).should == 0
24
+ end
25
+ end
26
+
27
+ # Helpers
28
+ def round_to(precision, num)
29
+ (num * 10**precision).round.to_f / 10**precision
20
30
  end
21
- end
31
+ end
metadata CHANGED
@@ -1,92 +1,71 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: haversine
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 2
8
- - 0
9
- version: 0.2.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Kristian Mandrup
9
+ - Ryan Greenberg
13
10
  autorequire:
14
11
  bindir: bin
15
12
  cert_chain: []
16
-
17
- date: 2011-01-13 00:00:00 +01:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
13
+ date: 2011-06-05 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
21
16
  name: rspec
22
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2154359680 !ruby/object:Gem::Requirement
23
18
  none: false
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 2
29
- - 3
30
- - 0
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
31
22
  version: 2.3.0
32
23
  type: :development
33
24
  prerelease: false
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
25
+ version_requirements: *2154359680
26
+ - !ruby/object:Gem::Dependency
36
27
  name: bundler
37
- requirement: &id002 !ruby/object:Gem::Requirement
28
+ requirement: &2154359180 !ruby/object:Gem::Requirement
38
29
  none: false
39
- requirements:
30
+ requirements:
40
31
  - - ~>
41
- - !ruby/object:Gem::Version
42
- segments:
43
- - 1
44
- - 0
45
- - 0
32
+ - !ruby/object:Gem::Version
46
33
  version: 1.0.0
47
34
  type: :development
48
35
  prerelease: false
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
36
+ version_requirements: *2154359180
37
+ - !ruby/object:Gem::Dependency
51
38
  name: jeweler
52
- requirement: &id003 !ruby/object:Gem::Requirement
39
+ requirement: &2154358680 !ruby/object:Gem::Requirement
53
40
  none: false
54
- requirements:
41
+ requirements:
55
42
  - - ~>
56
- - !ruby/object:Gem::Version
57
- segments:
58
- - 1
59
- - 5
60
- - 2
43
+ - !ruby/object:Gem::Version
61
44
  version: 1.5.2
62
45
  type: :development
63
46
  prerelease: false
64
- version_requirements: *id003
65
- - !ruby/object:Gem::Dependency
47
+ version_requirements: *2154358680
48
+ - !ruby/object:Gem::Dependency
66
49
  name: rcov
67
- requirement: &id004 !ruby/object:Gem::Requirement
50
+ requirement: &2154358200 !ruby/object:Gem::Requirement
68
51
  none: false
69
- requirements:
70
- - - ">="
71
- - !ruby/object:Gem::Version
72
- segments:
73
- - 0
74
- version: "0"
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
75
56
  type: :development
76
57
  prerelease: false
77
- version_requirements: *id004
78
- description: |-
79
- Calculates the haversine distance between two locations using longitude and latitude.
80
- This is done using Math formulas without resorting to Active Record or SQL DB functionality
58
+ version_requirements: *2154358200
59
+ description: ! "Calculates the haversine distance between two locations using longitude
60
+ and latitude. \nThis is done using Math formulas without resorting to Active Record
61
+ or SQL DB functionality"
81
62
  email: kmandrup@gmail.com
82
63
  executables: []
83
-
84
64
  extensions: []
85
-
86
- extra_rdoc_files:
65
+ extra_rdoc_files:
87
66
  - LICENSE.txt
88
67
  - README.textile
89
- files:
68
+ files:
90
69
  - .document
91
70
  - .rspec
92
71
  - Gemfile
@@ -97,42 +76,40 @@ files:
97
76
  - VERSION
98
77
  - haversine.gemspec
99
78
  - lib/haversine.rb
100
- - lib/haversine/core_ext.rb
79
+ - lib/haversine/distance.rb
80
+ - spec/distance_spec.rb
101
81
  - spec/haversine_spec.rb
102
82
  - spec/spec_helper.rb
103
- has_rdoc: true
104
83
  homepage: http://github.com/kristianmandrup/haversine
105
- licenses:
84
+ licenses:
106
85
  - MIT
107
86
  post_install_message:
108
87
  rdoc_options: []
109
-
110
- require_paths:
88
+ require_paths:
111
89
  - lib
112
- required_ruby_version: !ruby/object:Gem::Requirement
90
+ required_ruby_version: !ruby/object:Gem::Requirement
113
91
  none: false
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- hash: 3157735557663503756
118
- segments:
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ segments:
119
97
  - 0
120
- version: "0"
121
- required_rubygems_version: !ruby/object:Gem::Requirement
98
+ hash: -2758069868473656390
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
100
  none: false
123
- requirements:
124
- - - ">="
125
- - !ruby/object:Gem::Version
126
- segments:
127
- - 0
128
- version: "0"
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
129
105
  requirements: []
130
-
131
106
  rubyforge_project:
132
- rubygems_version: 1.3.7
107
+ rubygems_version: 1.8.5
133
108
  signing_key:
134
109
  specification_version: 3
135
- summary: Calculates the haversine distance between two locations using longitude and latitude
136
- test_files:
110
+ summary: Calculates the haversine distance between two locations using longitude and
111
+ latitude
112
+ test_files:
113
+ - spec/distance_spec.rb
137
114
  - spec/haversine_spec.rb
138
115
  - spec/spec_helper.rb
@@ -1,19 +0,0 @@
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