# kristianmandrup/geo_magic

lots of improvements

1 parent d26cdec commit 1976bb9807ce366fd8c6e38c1242bb6dfe3758a1 committed Feb 24, 2011
6 .gitignore
 @@ -27,11 +27,11 @@ pkg # # For MacOS: # -#.DS_Store +.DS_Store # # For TextMate -#*.tmproj -#tmtags +*.tmproj +tmtags # # For emacs: #*~
1 lib/geo_magic.rb
 @@ -10,5 +10,6 @@ module GeoMagic require 'geo_magic/remote' require 'geo_magic/calculate' require 'geo_magic/distance' +require 'geo_magic/radius' require 'geo_magic/meta' require 'geo_magic/geocoder'
9 lib/geo_magic/core_ext.rb
 @@ -57,6 +57,15 @@ def radians_ratio class Array + def is_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] + end + def are_points? each do |p| return false if !p.kind_of?(GeoMagic::Point)
6 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/points_distance' module GeoMagic class Distance @@ -19,7 +20,10 @@ def initialize distance, unit = nil end # select all points within radius - def select_within points, center + def select_within points, center + points.select do |p| + point.within? center + end end # reject all points within radius
2 lib/geo_magic/distance/formula/haversine.rb
 @@ -42,7 +42,7 @@ def self.point_distance point_a, point_b # 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 dlon = lon2 - lon1 dlat = lat2 - lat1
22 lib/geo_magic/distance/formula/plane.rb
 @@ -1,26 +1,8 @@ module GeoMagic class Distance class Plane < Formula - def distance from_point, to_point, options = { :unit => :meters } - vector = Vector.new :formula => self - dist = vector.distance options - GeoMagic::Distance.new vector_distance(vector, options) - end - - def vector_distance vector, options = { :unit => :meters } - dist = Math.sqrt((delta_longitude(vector) + delta_latitude).abs) - unit_dist = ::GeoMagic::Distance.new(dist).send unit - unit_dist.number - end - - protected - - def delta_longitude vector - (vector[0].longitude - vector[1].longitude)**2 - end - - def delta_latitude vector - (vector[0].latitude - vector[1].latitude)**2 + def distance *args + Vector.new(*args).distance end end end
2 lib/geo_magic/distance/formula/spherical.rb
 @@ -8,7 +8,7 @@ def self.point_distance point_a, point_b distance *points end - def self.distance( lat1, lon1, lat2, lon2) + def self.distance lat1, lon1, lat2, lon2 from_longitude = lon1.to_radians from_latitude = lat1.to_radians to_longitude = lon2.to_radians
2 lib/geo_magic/distance/formula/vincenty.rb
 @@ -17,7 +17,7 @@ def self.point_distance point_a, point_b # ) # #=> 101.070118000159 # - def self.distance(lat1, lon1, lat2, lon2) + def self.distance lat1, lon1, lat2, lon2 from_longitude = lon1.to_radians from_latitude = lat1.to_radians to_longitude = lon2.to_radians
18 lib/geo_magic/distance/points_distance.rb
 @@ -0,0 +1,18 @@ +module GeoMagic + class PointsDistance + attr_accessor :distance, :points + + def initialize distance, points + 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.points = points + end + + def near center + points.select {|point| point.within(distance).of(center) } + end + end +end + +
2 lib/geo_magic/distance/vector.rb
 @@ -1,5 +1,7 @@ require 'sugar-high/kind_of' +# This can be used to indicate a rectangle, with a latitude and logitude distance from the center in both directions to span out the rectangle. + module GeoMagic class Distance class Vector
5 lib/geo_magic/point.rb
 @@ -25,6 +25,11 @@ def initialize *args @longitude = points.last end + def within? shape + raise ArgumentError, "Argument must be a GeoMagic::Shape, was #{shape}" if !shape.kind_of? GeoMagic::Shape + shape.contains? point + end + # factory method def self.create_from *args latitude, longitude = case args.size
 @@ -0,0 +1,15 @@ +module GeoMagic + class WithinRadius + attr_reader :distance, :point + + def initialize point, distance + self.distance = distance + self.point = point + end + + def of? center + circle = CircularRadius.new center, distance + point.within? circle + end + end +end
1 lib/geo_magic/shape.rb
 @@ -4,5 +4,6 @@ class Shape def within? point raise "Subclass should implement this" end + alias_method :contains?, :within? end end
18 lib/geo_magic/vector.rb
 @@ -28,6 +28,12 @@ def length type = nil 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 @@ -38,7 +44,17 @@ def [] key else raise "Vector key must be either 0/1 or :p0/:p1" end - end + end + + protected + + def delta_longitude + (p0.longitude - p1.longitude)**2 + end + + def delta_latitude vector + (p0.latitude - p1.latitude)**2 + end end end end
379 rspec_examples.rb
32 spec/geo_magic/distance/class_methods_spec.rb
 @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe GeoMagic::Distance do + before do + @long1 = -104.88544 + @lat1 = 39.06546 + + @long2 = -104.80 + @lat2 = @lat1 + end + + subject { GeoMagic::Distance } + + it "calculates distance using array args - using algorithm haversine" do + dist = subject.calculate [@long1, @lat1], [@long2, @lat2] + puts dist + end + + it "calculates distance using array args - using algorithm haversine" do + dist = subject.calculate @long1, @lat1, @long2, @lat2 + km_dist = dist[:km] + puts dist + puts "km: #{km_dist}" + end + + it "calculates distance using array args - using algorithm haversine" do + dist = subject.calculate {:long => @long1, :lat => @lat1}, {:long => @long2, :lat => @lat2} + km_dist = dist[:km] + puts dist + puts "km: #{km_dist}" + end +end
30 spec/geo_magic/distance/formula/haversine_spec.rb
 @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe GeoMagic::Distance::Haversine do + + subject { GeoMagic::Distance::Haversine } + + describe '#point_distance' do + before do + @a = [45, 10].to_point + @b = [42, 11].to_point + end + + it 'should calculate the distance between points A and B' do + dist = subject.point_distance @a, @b + puts dist + end + end + + describe '#distance' do + before do + @a = [45, 10] + @b = [42, 11] + end + + it 'should calculate the distance between points A and B' do + dist = subject.distance [@a, @b].flatten + puts dist + end + end +end
39 spec/geo_magic/distance/formula/plane_spec.rb
 @@ -1,17 +1,32 @@ require 'spec_helper' -require 'geo_magic' -describe "GeoMagic Calculate" do - before do - @long1 = -104.88544 - @lat1 = 39.06546 - @long2 = -104.80 - @lat2 = @lat1 - end +describe GeoMagic::Distance::Plane do + + subject { GeoMagic::Distance::Plane } - it "calculates distance using array args" do - dist = GeoMagic::Calculate.plane_distance [@long1, @lat1], [@long2, @lat2] - puts dist + describe '#point_distance' do + before do + @a = [45, 10].to_point + @b = [42, 11].to_point + end + + it 'should calculate the distance between points A and B' do + dist = subject.point_distance @a, @b + puts dist + end end -end + + describe '#distance' do + before do + @a = [45, 10] + @b = [42, 11] + end + + it 'should calculate the distance between points A and B' do + dist = subject.distance [@a, @b].flatten + puts dist + end + end +end +
30 spec/geo_magic/distance/formula/spherical_spec.rb
 @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe GeoMagic::Distance::Spherical do + + subject { GeoMagic::Distance::Spherical } + + describe '#point_distance' do + before do + @a = [45, 10].to_point + @b = [42, 11].to_point + end + + it 'should calculate the distance between points A and B' do + dist = subject.point_distance @a, @b + puts dist + end + end + + describe '#distance' do + before do + @a = [45, 10] + @b = [42, 11] + end + + it 'should calculate the distance between points A and B' do + dist = subject.distance [@a, @b].flatten + puts dist + end + end +end
30 spec/geo_magic/distance/formula/vincenty_spec.rb
 @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe GeoMagic::Distance::Haversine do + + subject { GeoMagic::Distance::Haversine } + + describe '#point_distance' do + before do + @a = [45, 10].to_point + @b = [42, 11].to_point + end + + it 'should calculate the distance between points A and B' do + dist = subject.point_distance @a, @b + puts dist + end + end + + describe '#distance' do + before do + @a = [45, 10] + @b = [42, 11] + end + + it 'should calculate the distance between points A and B' do + dist = subject.distance [@a, @b].flatten + puts dist + end + end +end
18 spec/geo_magic/distance/points_distance_spec.rb
 @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe GeoMagic::PointsDistance do + before do + @a = [45.1, 11].to_point + @b = [48, 11].to_point + @center = [45, 11].to_point + end + + context 'Distance vector' do + subject { GeoMagic::PointsDistance.new 5.km, [@a, @b].to_points } + + describe '#near' do + subject.near(@center).should include?(@a) + end + end +end +
15 spec/geo_magic/distance/unit_spec.rb
 @@ -1,12 +1,11 @@ require 'spec_helper' -describe GeoMagic::Distance::Unit do - it "should create 5 km" do - km_5 = GeoMagic::Distance::Unit.new :km, 5 - km_5.number.should > 0 - km_5.radians_ratio.should > 0 - - puts "number: #{km_5.number}" - puts "radians_ratio: #{km_5.radians_ratio}" +describe GeoMagic::Distance::Unit do + context "5 km distance" do + + subject { GeoMagic::Distance::Unit.new :km, 5 } + + its(:number) { should > 0 } + its(:radians_ratio) { should > 0 } end end
42 spec/geo_magic/distance/vector_spec.rb
 @@ -5,34 +5,28 @@ @long_dist = -0.3.km @lat_dist = 0.05.km end + + context 'Distance vector' do + subject { GeoMagic::Distance::Vector.new @long_dist, @lat_dist } - it "should create a distance vector" do - dist_vector = GeoMagic::Distance::Vector.new @long_dist, @lat_dist - dist_vector.long_distance.should_not be_nil - dist_vector.lat_distance.should_not be_nil - puts dist_vector.inspect - end - - describe '#multiply!' do - let (:dvector) do - GeoMagic::Distance::Vector.new @long_dist, @lat_dist - end + its(:long_distance) { should > 0 } + its(:lat_distance) { should > 0 } - it "should multiply the radius distance" do - dvector.multiply!(5) - puts dvector.inspect + describe '#multiply' do + it "should multiply the radius distance and return new circle" do + old_dist = subject.long_distance + dvec = subject.multiply(0.5) + subject.long_distance.should == old_dist + dvec.long_distance.should < subject.long_distance + end end - end - describe '#multiply' do - let (:dvector) do - GeoMagic::Distance::Vector.new @long_dist, @lat_dist - end - - it "should multiply the radius distance and return new circle" do - dvector.multiply!(0.5) - puts dvector.inspect + describe '#multiply!' do + it "should multiply the radius distance" do + old_dist = subject.long_distance + subject.multiply!(5) + subject.long_distance.should > old_dist + end end end - end
45 spec/geo_magic/distance_spec.rb
 @@ -1,30 +1,33 @@ require 'spec_helper' describe GeoMagic::Distance do - before do - @long1 = -104.88544 - @lat1 = 39.06546 - - @long2 = -104.80 - @lat2 = @lat1 - end - - it "calculates distance using array args - using algorithm haversine" do - dist = GeoMagic::Distance.calculate [@long1, @lat1], [@long2, @lat2] - puts dist + before do + a = [45.1, 11].to_point + b = [45.1, 11.1].to_point + c = [48.1, 12.1].to_point + @center = [45, 11].to_point + @points = [a, b, c] end - it "calculates distance using array args - using algorithm haversine" do - dist = GeoMagic::Distance.calculate @long1, @lat1, @long2, @lat2 - km_dist = dist[:km] - puts dist - puts "km: #{km_dist}" + subject { 5.km } + + describe 'Select subset of points within distance' do + describe '#select_within' + subject.select_within @points, @center + end + + describe '#select_all #near' + subject.select_all(@points).near(@center) + end end - it "calculates distance using array args - using algorithm haversine" do - dist = GeoMagic::Distance.calculate {:long => @long1, :lat => @lat1}, {:long => @long2, :lat => @lat2} - km_dist = dist[:km] - puts dist - puts "km: #{km_dist}" + describe 'Reject subset of points within distance' do + describe '#reject_within' + subject.reject_near @points, @center + end + + describe '#reject_all #within' + subject.reject_all(@points).near(@center) + end end end