Skip to content
Browse files

better unit and distance handling

  • Loading branch information...
1 parent 1976bb9 commit 609b232c0f0ca330bc95e7f01dee117f5d8e48c7 @kristianmandrup committed Feb 24, 2011
View
1 lib/geo_magic.rb
@@ -11,5 +11,6 @@ module GeoMagic
require 'geo_magic/calculate'
require 'geo_magic/distance'
require 'geo_magic/radius'
+require 'geo_magic/vector'
require 'geo_magic/meta'
require 'geo_magic/geocoder'
View
20 lib/geo_magic/core_ext.rb
@@ -58,12 +58,24 @@ def radians_ratio
class Array
def is_point?
+ return true if self.first.kind_of? GeoMagic::Point
(0..1).all? {|n| self[n].is_a?(Numeric) }
end
def to_point
raise "For an array to be converted to a point, it must consist of two numbers, was: #{self}" if !is_point?
- GeoMagic::Point.new self[0], self[1]
+ point_args = if self.first.kind_of? GeoMagic::Point
+ self.first
+ else
+ self[0..1]
+ end
+ GeoMagic::Point.new point_args
+ end
+
+ def to_points
+ res = []
+ each_slice(2) {|point| res << point.to_point }
+ res
end
def are_points?
@@ -72,11 +84,7 @@ def are_points?
end
true
end
-
- def as_map_points
- self.extend GeoMagic::MapPoints
- end
-
+
def sort_by_distance
self.sort_by { |item| get_dist_obj(item).dist }
end
View
7 lib/geo_magic/distance.rb
@@ -3,6 +3,7 @@
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
@@ -65,10 +66,14 @@ def self.valid_radius_types
end
GeoMagic::Distance.units.each do |unit|
- class_eval %{
+ class_eval %{
def #{unit}
self[:#{unit}]
end
+
+ def in_#{unit}
+ GeoMagic::Distance::Unit.new(unit, distance).in_#{unit}
+ end
}
end
View
18 lib/geo_magic/distance/point_distance.rb
@@ -0,0 +1,18 @@
+module GeoMagic
+ class PointDistance
+ attr_accessor :distance, :point
+
+ def initialize distance, point
+ raise ArgumentError, "First argument must be a GeoMagic::Distance, was #{distance}" if !distance.kind_of? GeoMagic::Distance
+ # raise ArgumentError, "First argument must be a collection of GeoMagic::Point, was #{points}" if !points.
+ self.distance = distance
+ self.point = point
+ end
+
+ # return whether the distance between this point and a given center is within the given distance
+ def of center
+ GeoMagic::Vector.new(point, center).distance <= distance
+ end
+ end
+end
+
View
37 lib/geo_magic/distance/unit.rb
@@ -8,6 +8,43 @@ def initialize name, number = 0
@number = number
end
+ def meters_map
+ {:miles => 0.0062,
+ :feet => 32.8,
+ :km => 0.01,
+ :meters => 1
+ }
+ end
+
+ def in_meters
+ meters_map[name.to_sym] * number
+ end
+
+ def to_meters!
+ self.number = meters_map[name] * number
+ end
+
+ [:miles, :feet, :km].each do |unit|
+ class_eval %{
+ def in_#{unit}
+ to_meters * meters_map[#{unit}]
+ end
+
+ def to_#{unit}!
+ self.number = to_meters * meters_map[#{unit}]
+ self.name = :#{unit}
+ end
+ }
+ end
+
+ [:<, :<=, :>, :>=, :==].each do |op|
+ class_eval %{
+ def #{op} dist_unit
+ in_meters #{op} dist_unit.in_meters
+ end
+ }
+ end
+
def number
@number.round_to(precision[name])
end
View
4 lib/geo_magic/point.rb
@@ -30,6 +30,10 @@ def within? shape
shape.contains? point
end
+ def within distance
+ GeoMagic::PointDistance.new distance, self
+ end
+
# factory method
def self.create_from *args
latitude, longitude = case args.size
View
10 lib/geo_magic/point/map_points.rb
@@ -9,6 +9,16 @@ def rad
def within_radius radius_obj, options = {:precision => :lowest}
end
+ # return Points Center
+ def near center
+ GeoMagic::PointsCenter.new center, self
+ end
+
+ # return PointsDistance
+ def within distance
+ GeoMagic::PointsDistance.new distance, self
+ end
+
# todo: should make radius from distance!
def within_distance dist_obj, options = {:precision => :lowest}
calc_method = get_proc(options[:precision] || :normal)
View
17 lib/geo_magic/point/points_center.rb
@@ -0,0 +1,17 @@
+module GeoMagic
+ class PointsCenter
+ attr_accessor :distance, :center
+
+ def initialize distance, point
+ raise ArgumentError, "First argument must be a GeoMagic::Distance, was #{distance}" if !distance.kind_of? GeoMagic::Distance
+ # raise ArgumentError, "First argument must be a collection of GeoMagic::Point, was #{points}" if !points.
+ self.distance = distance
+ self.point = point
+ end
+
+ # return whether the distance between this point and a given center is within the given distance
+ def of center
+ GeoMagic::Vector.new(point, center).distance <= distance
+ end
+ end
+end
View
99 lib/geo_magic/vector.rb
@@ -1,60 +1,59 @@
module GeoMagic
- 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?
- @p0 = p0
- @p1 = p1
- end
+ 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?
+ @p0 = p0
+ @p1 = p1
+ end
- def create_at center, vector
- new center, center.move(vector)
- end
+ def create_at center, vector
+ new center, center.move(vector)
+ end
- def length type = nil
- case type
- when nil
- GeoMagic::Distance.distance(p0, p1)
- when :latitude
- (p0.latitude - p1.latitude).abs
- when :longitude
- (p0.longitude - p1.longitude).abs
- else
- raise ArgumentError, "Bad argument for calculating lenght, valid args are: nil, :latitude or :longitude"
- end
+ def length type = nil
+ case type
+ when nil
+ GeoMagic::Distance.distance(p0, p1)
+ when :latitude
+ (p0.latitude - p1.latitude).abs
+ when :longitude
+ (p0.longitude - p1.longitude).abs
+ else
+ raise ArgumentError, "Bad argument for calculating lenght, valid args are: nil, :latitude or :longitude"
end
+ end
- def vector_distance
- GeoMagic::Distance::Vector.new length(:latitude), length(:longitude)
- end
+ def vector_distance
+ GeoMagic::Distance::Vector.new length(:latitude), length(:longitude)
+ end
- def distance options = { :unit => :meters }
- dist = Math.sqrt((delta_longitude(vector) + delta_latitude).abs)
- unit_dist = ::GeoMagic::Distance.new(dist).send unit
- unit_dist.number
- end
-
- def [] key
- case key
- when 0, :p0
- p0
- when 1, :p1
- p1
- else
- raise "Vector key must be either 0/1 or :p0/:p1"
- end
- end
+ def distance options = { :unit => :meters }
+ dist = Math.sqrt((delta_longitude + delta_latitude).abs)
+ unit = options[:unit] || :meters
+ ::GeoMagic::Distance.new(dist).send unit
+ end
- protected
-
- def delta_longitude
- (p0.longitude - p1.longitude)**2
+ def [] key
+ case key
+ when 0, :p0
+ p0
+ when 1, :p1
+ p1
+ else
+ raise "Vector key must be either 0/1 or :p0/:p1"
end
-
- def delta_latitude vector
- (p0.latitude - p1.latitude)**2
- end
end
+
+ protected
+
+ def delta_longitude
+ (p0.longitude - p1.longitude)**2
+ end
+
+ def delta_latitude
+ (p0.latitude - p1.latitude)**2
+ end
end
-end
+end
View
27 spec/geo_magic/distance/point_distance_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe GeoMagic::PointDistance do
+ before do
+ @a = [45.1, 11].to_point
+ @b = [48, 11].to_point
+ @center = [45, 11].to_point
+ end
+
+ context '5km from point A and B' do
+ before do
+ a5 = GeoMagic::PointDistance.new 5.km, @a
+ b5 = GeoMagic::PointDistance.new 5.km, @b
+ end
+
+ describe '#of' do
+ it "should be that A is within 5km of center" do
+ a5.of(@center).should be_true
+ end
+
+ it "should NOT be that B is within 5km of center" do
+ b5.of(@center).should_not be_true
+ end
+ end
+ end
+end
+
View
6 spec/geo_magic/distance/points_distance_spec.rb
@@ -5,13 +5,17 @@
@a = [45.1, 11].to_point
@b = [48, 11].to_point
@center = [45, 11].to_point
+
+ GeoMagic::PointsDistance.new 5.km, [@a, @b].to_points
end
context 'Distance vector' do
subject { GeoMagic::PointsDistance.new 5.km, [@a, @b].to_points }
describe '#near' do
- subject.near(@center).should include?(@a)
+ it "should only select points near center" do
+ subject.near(@center).should include(@a)
+ end
end
end
end
View
8 spec/geo_magic/distance_spec.rb
@@ -12,21 +12,21 @@
subject { 5.km }
describe 'Select subset of points within distance' do
- describe '#select_within'
+ describe '#select_within' do
subject.select_within @points, @center
end
- describe '#select_all #near'
+ describe '#select_all #near' do
subject.select_all(@points).near(@center)
end
end
describe 'Reject subset of points within distance' do
- describe '#reject_within'
+ describe '#reject_within' do
subject.reject_near @points, @center
end
- describe '#reject_all #within'
+ describe '#reject_all #within' do
subject.reject_all(@points).near(@center)
end
end

0 comments on commit 609b232

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