haversine 0.2.0 → 0.3.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.
@@ -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