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