Skip to content
Browse files

added latitude factor for distance calcs

  • Loading branch information...
1 parent 3a2b0e7 commit 22912ada5a9049014ce6b44b14e262757a6b0c3f @kristianmandrup committed May 5, 2011
View
10 lib/geo_magic/core_ext.rb
@@ -60,7 +60,15 @@ def radians_ratio
end
-class Array
+class Array
+ def sum
+ inject( nil ) { |sum,x| sum ? sum+x : x }
+ end
+
+ def mean
+ sum.to_f / size.to_f
+ end
+
def is_point?
return true if self.first.kind_of? GeoMagic::Point
(0..1).all? {|n| self[n].is_a?(Numeric) }
View
116 lib/geo_magic/distance.rb
@@ -1,14 +1,13 @@
require 'geo_magic/calculate'
require 'geo_magic/distance/class_methods'
-# require 'geo_magic/distance/unit'
require 'geo_magic/distance/vector'
require 'geo_magic/distance/formula'
require 'geo_magic/distance/point_distance'
require 'geo_magic/distance/points_distance'
module GeoMagic
class Distance
- attr_accessor :distance, :unit
+ attr_accessor :distance, :unit, :lat_factor
extend ClassMethods
@@ -22,11 +21,12 @@ def reject_all points
GeoMagic::PointsDistance.new self, points, :reject
end
- def initialize distance, unit = :radians
+ def initialize distance, unit = :radians, options = {}
check_numeric! distance
@distance = distance
raise ArgumentError, "Invalid unit: #{unit} - must be one of #{GeoMagic::Distance.units}" if !GeoMagic::Distance.units.include?(unit.to_sym)
@unit = unit.to_sym
+ @lat_factor = options[:lat_factor] || 1
end
[:<, :<=, :>, :>=, :==].each do |op|
@@ -60,88 +60,6 @@ def radius center, type = :circular
raise ArgumentError, "Radius type must be one of: #{self.class.valid_radius_types}, was: #{type}" if !self.class.valid_radius_type? type
GeoMagic::Radius.send :"create_#{type}", center, self
end
-
- def [] key
- method = :"delta_#{key}"
- raise ArgumentError, "Invalid unit key #{key}" if !respond_to? method
- send(method) * distance.to_f
- end
-
- def self.valid_radius_type? type
- valid_radius_types.include? type
- end
-
- def self.valid_radius_types
- [:circular, :rectangular, :square]
- end
-
- (units - [:meters]).each do |unit|
- class_eval %{
- def in_#{unit}
- dist = (unit == :radians) ? in_radians : distance
- convert_to_meters(dist) * meters_map[:#{unit}]
- end
-
- def to_#{unit}!
- self.distance = in_meters * meters_map[:#{unit}]
- self.unit = :#{unit}
- self
- end
- }
- end
-
- def in_meters
- convert_to_meters distance
- end
-
- def convert_to_meters dist
- (unit == :radians) ? self[:meters] : distance / meters_map[unit]
- end
-
- def to_meters!
- @distance = in_meters
- @unit = :meters
- self
- end
-
- GeoMagic::Distance.units.each do |unit|
- class_eval %{
- def #{unit}
- as_#{unit}
- end
-
- def as_#{unit}
- in_#{unit}
- end
-
- def to_#{unit}
- cloned = self.dup
- cloned.distance = in_meters * meters_map[:#{unit}]
- cloned.unit = :#{unit}
- cloned
- end
- }
- end
-
- def to_radians
- cloned = self.dup
- cloned.distance = in_radians
- cloned.unit = :radians
- cloned
- end
-
- def to_radians!
- @distance = in_radians
- @unit = :radians
- end
-
- def radians_conversion_factor
- unit.radians_ratio
- end
-
- def in_radians
- (unit != :radians) ? distance.to_f / earth_factor : distance # radians_conversion_factor
- end
def to_s
"distance: #{distance} #{unit}"
@@ -153,30 +71,6 @@ def check_numeric! arg
raise ArgumentError, "Argument must be Numeric" if !arg.is_a? Numeric
end
- # delta between the two points in miles
- GeoMagic::Distance.units.each do |unit|
- class_eval %{
- def delta_#{unit}
- GeoMagic::Distance.earth_radius[:#{unit}]
- end
- }
- end
-
- private
-
- def earth_factor u = nil
- GeoMagic::Distance.earth_radius[u ||= unit]
- end
-
- def meters_map
- {
- :miles => 0.00062,
- :feet => 3.28,
- :km => 0.001,
- :meters => 1
- }
- end
-
def precision
{
:feet => 0,
@@ -187,4 +81,6 @@ def precision
}
end
end
-end
+end
+
+require 'geo_magic/distance/conversion'
View
14 lib/geo_magic/distance/class_methods.rb
@@ -4,9 +4,13 @@ module ClassMethods
# radius of the great circle in miles
# radius in kilometers...some algorithms use 6367
def earth_radius
- {:km => 6371, :miles => 3956, :feet => 20895592, :meters => 6371000}
+ {:km => 6371, :miles => 3956, :feet => 20895592, :meters => 6371000}
end
+ def radian_radius
+ {:km => 6371/180, :miles => 3956/180, :feet => 20895592/180, :meters => 6371000/180}
+ end
+
def radians_per_degree
0.017453293 # PI/180
end
@@ -15,6 +19,14 @@ def units
[:miles, :km, :feet, :meters, :radians]
end
+ def valid_radius_type? type
+ valid_radius_types.include? type
+ end
+
+ def valid_radius_types
+ [:circular, :rectangular, :square]
+ end
+
def radians_ratio unit
GeoMagic::Distance.radians_per_degree * earth_radius[unit]
end
View
110 lib/geo_magic/distance/conversion.rb
@@ -0,0 +1,110 @@
+module GeoMagic
+ class Distance
+ module Conversion
+ def [] key
+ raise ArgumentError, "Invalid unit key #{key}" if !respond_to? key
+ earth_factor(key) * distance.to_f
+ end
+
+ (::GeoMagic::Distance.units - [:meters]).each do |unit|
+ class_eval %{
+ def in_#{unit}
+ dist = (unit == :radians) ? in_radians : distance
+ convert_to_meters(dist) * meters_map[:#{unit}]
+ end
+
+ def to_#{unit}!
+ self.distance = in_meters * meters_map[:#{unit}]
+ self.unit = :#{unit}
+ self
+ end
+ }
+ end
+
+ def in_meters
+ convert_to_meters distance
+ end
+
+ def convert_to_meters dist
+ (unit == :radians) ? self[:meters] : distance / meters_map[unit]
+ end
+
+ def to_meters!
+ @distance = in_meters
+ @unit = :meters
+ self
+ end
+
+ ::GeoMagic::Distance.units.each do |unit|
+ class_eval %{
+ def #{unit}
+ as_#{unit}
+ end
+
+ def as_#{unit}
+ in_#{unit}
+ end
+
+ def to_#{unit}
+ cloned = self.dup
+ cloned.distance = in_meters * meters_map[:#{unit}]
+ cloned.unit = :#{unit}
+ cloned
+ end
+ }
+ end
+
+ def to_radians
+ cloned = self.dup
+ cloned.distance = in_radians
+ cloned.unit = :radians
+ cloned
+ end
+
+ def to_radians!
+ @distance = in_radians
+ @unit = :radians
+ end
+
+ def radians_conversion_factor
+ unit.radians_ratio
+ end
+
+ def in_radians
+ (unit != :radians) ? distance.to_f / earth_factor : distance # radians_conversion_factor
+ end
+
+ protected
+
+ # delta between the two points in miles
+ GeoMagic::Distance.units.each do |unit|
+ class_eval %{
+ def delta_#{unit}
+ GeoMagic::Distance.earth_radius[:#{unit}]
+ end
+ }
+ end
+
+ private
+
+ def earth_factor u = nil
+ (GeoMagic::Distance.earth_radius[u ||= unit] / 180) * lat_factor
+ end
+
+ def meters_map
+ {
+ :miles => 0.00062,
+ :feet => 3.28,
+ :km => 0.001,
+ :meters => 1
+ }
+ end
+ end
+ end
+end
+
+module GeoMagic
+ class Distance
+ include Conversion
+ end
+end
View
5 lib/geo_magic/distance/vector.rb
@@ -5,13 +5,14 @@
module GeoMagic
class Distance
class Vector
- attr_accessor :lat_distance, :long_distance
+ attr_accessor :lat_distance, :long_distance, :lat_factor
# should be Distance objects!
- def initialize lat_distance, long_distance
+ def initialize lat_distance, long_distance, options = {}
raise ArgumentError, "lat and long distance arguments must be instances of GeoMagic::Distance, was: #{lat_distance} #{long_distance}" if ![long_distance, lat_distance].only_kinds_of? GeoMagic::Distance
@lat_distance = lat_distance
@long_distance = long_distance
+ @lat_factor = options[:lat_factor] || 1
end
def * arg
View
10 lib/geo_magic/point.rb
@@ -25,6 +25,16 @@ def initialize *args
@longitude = points.last
end
+ def latitude_factor
+ 90 / (90 - latitude)
+ end
+
+ def middle_point p1
+ mp_lat = [latitude, p1.latitude].mean
+ mp_lng = [longitude, p1.longitude].mean
+ GeoMagic::Point.new mp_lat, mp_lng
+ end
+
def within? shape
raise ArgumentError, "Argument must be a GeoMagic::Shape, was #{shape}" if !shape.kind_of? GeoMagic::Shape
shape.contains? point
View
13 lib/geo_magic/vector.rb
@@ -3,7 +3,7 @@ class Vector
attr_accessor :p0, :p1
def initialize p0, p1
- raise "Vector must be initialized with a start end ending point, was: #{p0}, #{p1}" if ![p0, p1].are_points?
+ raise ArgumentError, "Vector must be initialized with a start end ending point, was: #{p0}, #{p1}" if ![p0, p1].are_points?
@p0 = p0
@p1 = p1
end
@@ -13,7 +13,7 @@ def create_at center, vector
end
def length type = nil
- case type
+ rad_dist = case type
when nil
GeoMagic::Distance.distance(p0, p1)
when :latitude
@@ -23,19 +23,18 @@ def length type = nil
else
raise ArgumentError, "Bad argument for calculating lenght, valid args are: nil, :latitude or :longitude"
end
+ d = GeoMagic::Distance.new rad_dist, :radians
+ d.lat_factor = p0.middle_point(p1).latitude_factor if type == :latitude
+ d
end
def vector_distance
- GeoMagic::Distance::Vector.new length(:latitude), length(:longitude)
+ GeoMagic::Distance::Vector.new length(:latitude), length(:longitude), :lat_factor => p0.middle_point(p1).latitude_factor
end
def distance unit = :meters
- puts "p0: #{p0}"
- puts "p1: #{p1}"
dist = Math.sqrt((delta_longitude + delta_latitude).abs)
- puts "dist: #{dist}"
dist = ::GeoMagic::Distance.new(dist, :radians)
- puts "dist rad: #{dist}"
dist.to_meters
end
View
104 spec/geo_magic/distance/conversion_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe GeoMagic::Distance::Conversion do
+ context 'a 5.km distance' do
+ before :each do
+ @dist = 5.km
+ end
+
+ describe '#in_radians' do
+ it "is < 1" do
+ @dist.in_radians.should < 1
+ end
+ end
+
+ describe '#radians_conversion_factor' do
+ it "is > 100" do
+ @dist.radians_conversion_factor.should > 100
+ end
+ end
+
+ describe '#in_feet' do
+ it "is > 12000" do
+ @dist.in_feet.should > 12000
+ end
+ end
+
+ describe '#in_meters' do
+ it "is 5000" do
+ @dist.in_meters.should == 5000
+ end
+ end
+
+ describe '#in_miles' do
+ it "is > 3 and < 5" do
+ @dist.in_miles.should < 5
+ @dist.in_miles.should > 3
+ end
+ end
+
+ describe '#in_radians' do
+ it "is > 0 and < 1" do
+ @dist.in_radians.should < 1
+ @dist.in_radians.should > 0
+ end
+ end
+
+ describe '#to_radians' do
+ it "is > 0 and < 1" do
+ @dist.to_radians.distance.should < 1
+ @dist.to_radians.distance.should > 0
+ end
+ end
+
+ describe '#to_meters!' do
+ it "converts to a meter distance of 5000" do
+ @dist.to_meters!
+ @dist.distance.should == 5000
+ end
+ end
+
+ describe '#to_feet!' do
+ it "converts to a feet distance > 12000" do
+ @dist.to_feet!
+ @dist.distance.should > 12000
+ end
+ end
+
+ describe '#loop conversion' do
+ it "converts to miles and then back to 5000 meters" do
+ @dist.to_miles!
+ @dist.to_meters.distance.should == 5000
+ end
+
+ it "converts to radians and back to 5000 meters" do
+ r = @dist.to_radians
+ r[:km].should <= 5.1
+ end
+
+ it "converts to radians and back to 5000 meters" do
+ @dist.to_radians.to_km.distance.should <= 5.1
+ end
+
+ it "converts to radians and then back to 5000 meters" do
+ @dist.to_radians!
+ @dist.unit.should == :radians
+ @dist.distance.should < 1
+ @dist.distance.should > 0
+ @dist.to_meters.distance.should <= 5100
+ end
+ end
+
+ context 'a 1.rad distance' do
+ before :each do
+ @dist = 1.radians
+ end
+
+ it 'should convert to km' do
+ @dist.in_km.should > 0
+ @dist.in_km.should <= 6371
+ @dist.in_km.should <= (6371/180) + 1
+ end
+ end
+ end
+end
View
86 spec/geo_magic/distance_spec.rb
@@ -22,90 +22,6 @@
it "is :km" do
@dist.unit.should == :km
end
- end
-
- describe '#in_radians' do
- it "is < 1" do
- @dist.in_radians.should < 1
- end
- end
-
- describe '#radians_conversion_factor' do
- it "is > 100" do
- @dist.radians_conversion_factor.should > 100
- end
- end
-
- describe '#in_feet' do
- it "is > 12000" do
- @dist.in_feet.should > 12000
- end
- end
-
- describe '#in_meters' do
- it "is 5000" do
- @dist.in_meters.should == 5000
- end
- end
-
- describe '#in_miles' do
- it "is > 3 and < 5" do
- @dist.in_miles.should < 5
- @dist.in_miles.should > 3
- end
- end
-
- describe '#in_radians' do
- it "is > 0 and < 1" do
- @dist.in_radians.should < 1
- @dist.in_radians.should > 0
- end
- end
-
- describe '#to_radians' do
- it "is > 0 and < 1" do
- puts @dist.to_radians
- @dist.to_radians.distance.should < 1
- @dist.to_radians.distance.should > 0
- end
- end
-
- describe '#to_meters!' do
- it "converts to a meter distance of 5000" do
- @dist.to_meters!
- @dist.distance.should == 5000
- end
- end
-
- describe '#to_feet!' do
- it "converts to a feet distance > 12000" do
- @dist.to_feet!
- @dist.distance.should > 12000
- end
- end
-
- describe '#loop conversion' do
- it "converts to miles and then back to 5000 meters" do
- @dist.to_miles!
- @dist.to_meters.distance.should == 5000
- end
-
- it "converts to radians and back to 5000 meters" do
- r = @dist.to_radians
- r[:km].should == 5
- end
-
- it "converts to radians and back to 5000 meters" do
- @dist.to_radians.to_km.distance.should == 5
- end
-
- it "converts to radians and then back to 5000 meters" do
- @dist.to_radians!
- @dist.unit.should == :radians
- @dist.distance.should < 1
- @dist.distance.should > 0
- @dist.to_meters.distance.should == 5000
- end
- end
+ end
end
end
View
23 spec/geo_magic/vector_spec.rb
@@ -1,15 +1,30 @@
require 'spec_helper'
+RAD_KM_LAT45 = GeoMagic::Distance.radian_radius[:km] * 2
+
describe GeoMagic::Vector do
- context 'a 5.km distance' do
+ context 'an almost 1 radian vector' do
before :each do
- @dist = 5.km
+ @a = [45.1, 11].to_point
+ @b = [46, 11.4].to_point
+ @vector = GeoMagic::Vector.new @a, @b
end
describe 'Class' do
- it "is a GeoMagic::Distance" do
- @dist.should be_a(GeoMagic::Distance)
+ it "is a GeoMagic::Vector" do
+ @vector.should be_a(GeoMagic::Vector)
end
end
+
+ describe '#vector_distance' do
+ it "is has a vector_distance" do
+ @vector.vector_distance.should be_a(GeoMagic::Distance::Vector)
+ end
+
+ it "is has a latitude distance < #{RAD_KM_LAT45}" do
+ lat_dist = @vector.vector_distance.lat_distance.km
+ lat_dist.should < RAD_KM_LAT45
+ end
+ end
end
end

0 comments on commit 22912ad

Please sign in to comment.
Something went wrong with that request. Please try again.