lib/haversine.rb in haversine-0.2.0 vs lib/haversine.rb in haversine-0.3.0
- old
+ new
@@ -8,11 +8,11 @@
# contact:
# http://www.esawdust.com/blog/businesscard/businesscard.html
#
# LICENSE: GNU Affero GPL v3
# The ruby implementation of the Haversine formula is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
+# it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License version 3 for more details. http://www.gnu.org/licenses/
#
@@ -25,132 +25,41 @@
# http://www.movable-type.co.uk/scripts/latlong.html
# http://en.wikipedia.org/wiki/Haversine_formula
#
# This formula can compute accurate distances between two points given latitude and longitude, even for
# short distances.
-
-# PI = 3.1415926535
-require 'haversine/core_ext'
+require 'haversine/distance'
-class Haversine
+module Haversine
- RAD_PER_DEG = 0.017453293 # PI/180
-
- # this is global because if computing lots of track point distances, it didn't make
- # sense to new a Hash each time over potentially 100's of thousands of points
+ RAD_PER_DEG = Math::PI / 180
- class << self
- def units
- [:miles, :km, :feet, :meters]
+ # given two lat/lon points, compute the distance between the two points using the haversine formula
+ def self.distance(lat1, lon1, lat2=nil, lon2=nil)
+ # Accept two arrays of points in addition to four coordinates
+ if lat1.is_a?(Array) && lon1.is_a?(Array)
+ lat2, lon2 = lon1
+ lat1, lon1 = lat1
+ elsif lat2.nil? || lon2.nil?
+ raise ArgumentError
end
- end
-
- class Distance
- attr_reader :distance
- def initialize distance
- @distance = distance
- end
-
- def [] key
- method = :"delta_#{key}"
- raise ArgumentError, "Invalid unit key #{key}" if !respond_to? method
- Distance.send "in_#{key}", send(method)
- end
-
- Haversine.units.each do |unit|
- class_eval %{
- def #{unit}
- self[:#{unit}]
- end
- }
- end
-
- protected
-
- # the great circle distance d will be in whatever units R is in
-
- Rmiles = 3956 # radius of the great circle in miles
- Rkm = 6371 # radius in kilometers...some algorithms use 6367
- Rfeet = Rmiles * 5282 # radius in feet
- Rmeters = Rkm * 1000 # radius in meters
-
- # delta between the two points in miles
- def delta_miles
- Rmiles * distance
- end
-
- # delta in kilometers
- def delta_km
- Rkm * distance
- end
-
- def delta_feet
- Rfeet * distance
- end
-
- def delta_meters
- Rmeters * distance
- end
-
-
- class << self
- Haversine.units.each do |unit|
- class_eval %{
- def in_#{unit} number
- Unit.new :#{unit}, number
- end
- }
- end
- end
-
- class Unit
- attr_accessor :name, :number
-
- def initialize name, number = 0
- @name = name
- @number = number
- end
-
- def number
- @number.round_to(precision[name])
- end
-
- def to_s
- "#{number} #{name}"
- end
-
- private
-
- def precision
- {
- :feet => 0,
- :meters => 2,
- :km => 4,
- :miles => 4
- }
- end
- end
- end
-
- # given two lat/lon points, compute the distance between the two points using the haversine formula
- # the result will be a Hash of distances which are key'd by 'mi','km','ft', and 'm'
-
- def self.distance( lat1, lon1, lat2, lon2, units = :meters )
dlon = lon2 - lon1
dlat = lat2 - lat1
a = calc(dlat, lat1, lat2, dlon)
c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
- Distance.new c
- end
+ Haversine::Distance.new(c)
+ end
- def self.calc dlat, lat1, lat2, dlon
- (Math.sin(dlat.rpd/2))**2 + Math.cos(lat1.rpd) * Math.cos((lat2.rpd)) * (Math.sin(dlon.rpd/2))**2
+ # TODO How can this be more descriptively named?
+ def self.calc(dlat, lat1, lat2, dlon)
+ (Math.sin(rpd(dlat)/2))**2 + Math.cos(rpd(lat1)) * Math.cos((rpd(lat2))) * (Math.sin(rpd(dlon)/2))**2
end
-
- def self.wants? unit_opts, unit
- unit_opts == unit || unit_opts[unit]
+
+ # Radians per degree
+ def self.rpd(num)
+ num * RAD_PER_DEG
end
-end
+end
\ No newline at end of file