lib/haversine.rb in haversine-0.1.0 vs lib/haversine.rb in haversine-0.2.0
- old
+ new
@@ -28,28 +28,76 @@
# This formula can compute accurate distances between two points given latitude and longitude, even for
# short distances.
# PI = 3.1415926535
+require 'haversine/core_ext'
+
class Haversine
RAD_PER_DEG = 0.017453293 # PI/180
- # 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
-
# 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
-
+
+ class << self
+ def units
+ [:miles, :km, :feet, :meters]
+ end
+ end
+
class Distance
- class << self
-
- [:miles, :km, :feet, :meters].each do |unit|
+ 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
}
@@ -61,49 +109,48 @@
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
-
- def self.distances
- @distances ||= Hash.new
- 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 )
+ def self.distance( lat1, lon1, lat2, lon2, units = :meters )
dlon = lon2 - lon1
dlat = lat2 - lat1
- dlon_rad = dlon * RAD_PER_DEG
- dlat_rad = dlat * RAD_PER_DEG
-
- lat1_rad = lat1 * RAD_PER_DEG
- lon1_rad = lon1 * RAD_PER_DEG
-
- lat2_rad = lat2 * RAD_PER_DEG
- lon2_rad = lon2 * RAD_PER_DEG
-
- # puts "dlon: #{dlon}, dlon_rad: #{dlon_rad}, dlat: #{dlat}, dlat_rad: #{dlat_rad}"
-
- a = (Math.sin(dlat_rad/2))**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * (Math.sin(dlon_rad/2))**2
+ a = calc(dlat, lat1, lat2, dlon)
c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
- dMi = Rmiles * c # delta between the two points in miles
- dKm = Rkm * c # delta in kilometers
- dFeet = Rfeet * c # delta in feet
- dMeters = Rmeters * c # delta in meters
+ Distance.new c
+ end
- distances[:miles] = Distance.in_miles dMi
- distances[:km] = Distance.in_km dKm
- distances[:feet] = Distance.in_feet dFeet
- distances[:meters] = Distance.in_meters dMeters
- distances
+ 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
+ end
+
+ def self.wants? unit_opts, unit
+ unit_opts == unit || unit_opts[unit]
end
end
\ No newline at end of file