Skip to content

Commit

Permalink
primitive closest and within radius works for collection of points
Browse files Browse the repository at this point in the history
  • Loading branch information
kristianmandrup committed Jan 13, 2011
1 parent 6a26412 commit 3874cd6
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 19 deletions.
1 change: 1 addition & 0 deletions lib/geo_magic.rb
@@ -1,4 +1,5 @@
require 'geo-distance'
require 'geo_magic/remote'
require 'geo_magic/calculate'
require 'geo_magic/distance'
require 'geo_magic/meta'
2 changes: 0 additions & 2 deletions lib/geo_magic/calculate.rb
Expand Up @@ -12,9 +12,7 @@ def self.included(base)
module ClassMethods
def distance from_point, to_point, options = { :unit => :meters }
points = extract_points from_point, to_point
puts "points: #{points.inspect}"
dist = ::GeoDistance.distance( *points )[options[:unit]]
puts "the distance from (#{points[0]}, #{points[1]}) to (#{points[2]}, #{points[3]}) is: #{dist}"
dist.number
end

Expand Down
12 changes: 6 additions & 6 deletions lib/geo_magic/distance.rb
@@ -1,26 +1,26 @@
module GeoDistance
class Haversine < DistanceFormula
def self.point_distance( point_a, point_b, units = :meters )
def self.point_distance point_a, point_b
points = GeoMagic::Util.extract_points point_a, point_b
distance *points, units
distance *points
end
end
end

module GeoDistance
class Spherical < DistanceFormula
def self.point_distance( point_a, point_b, units = :meters )
def self.point_distance point_a, point_b
points = GeoMagic::Util.extract_points point_a, point_b
distance *points, units
distance *points
end
end
end

module GeoDistance
class Vincenty < DistanceFormula
def self.point_distance( point_a, point_b, units = :meters )
def self.point_distance point_a, point_b
points = GeoMagic::Util.extract_points point_a, point_b
distance *points, units
distance *points
end
end
end
10 changes: 6 additions & 4 deletions lib/geo_magic/location.rb
@@ -1,7 +1,9 @@
require 'geo_magic/map_point'

module GeoMagic
class Location #:nodoc:
attr_accessor :latitude, :longitude, :ip
attr_writer :city, :region, :country
class Location < MapPoint
attr_accessor :ip
attr_writer :city, :region, :country

def initialize raw_location
raw_location.each_pair do |key, value|
Expand All @@ -13,7 +15,7 @@ def initialize raw_location
def [] key
send key
end

class City
attr_accessor :name, :metrocode, :zipcode

Expand Down
16 changes: 16 additions & 0 deletions lib/geo_magic/map_point.rb
@@ -0,0 +1,16 @@
require 'geo_magic/radius'

module GeoMagic
class MapPoint
attr_accessor :latitude, :longitude, :dist

def initialize latitude, longitude
@latitude = latitude
@longitude = longitude
end

def within(distance)
GeoMagic::Radius.new self, distance
end
end
end
15 changes: 9 additions & 6 deletions lib/geo_magic/point.rb
@@ -1,12 +1,12 @@
require 'geo_magic/map_point'

module GeoMagic
class Point #:nodoc:
attr_accessor :latitude, :longitude
class Point < MapPoint

def initialize latitude, longitude
@latitude = latitude
@longitude = longitude
super
end

def to_hash mode= :long
case mode
when :short
Expand All @@ -15,9 +15,12 @@ def to_hash mode= :long
{:latitude => latitude, :longitude => longitude}
end
end

def to_location
end

def to_s
"(lat: #{latitude}, long: #{longitude})"
"(lat: #{latitude}, long: #{longitude}, dist: #{dist})"
end
end
end
43 changes: 43 additions & 0 deletions lib/geo_magic/radius.rb
@@ -0,0 +1,43 @@
module GeoMagic
class Radius
attr_accessor :center, :distance

RAD_PER_DEG = 0.017453293

# radius of the great circle in miles
# radius in kilometers...some algorithms use 6367
def rad
{:km => 6371, :miles => 3956, :feet => 20895592, :meters => 6371000}
end

def initialize center, distance
@center = center
@distance = distance
end

def create_points number
conversion = (RAD_PER_DEG * rad[distance.unit])

max_radius_rad = distance.distance / conversion

range = (max_radius_rad * normalize).to_i

number.times.inject([]) do |res, n|
dlong = (get_randowm_radiant(range) / normalize) * conversion
dlat = (get_randowm_radiant(range) / normalize) * conversion

point = GeoMagic::Point.new @center.latitude + dlat, @center.longitude + dlong
res << point
res
end
end

def get_randowm_radiant range
rand(range) - (range / 2)
end

def normalize
1000000.0
end
end
end
79 changes: 78 additions & 1 deletion lib/geo_magic/util.rb
Expand Up @@ -10,7 +10,84 @@ def self.extract_point point
[point.latitude, point.longitude]
when Array
[point[0], point[1]]
end
end

def self.extract_points from_point, to_point
[extract_point(from_point), extract_point(to_point)].flatten.map(&:to_f)
end
end
end

class Array
def as_locations
self.extend GeoMagic::LocationsList
end

def sort_by_distance
self.sort_by { |item| item.dist }
end
end

module GeoMagic
module LocationsList
RAD_PER_DEG = 0.017453293

def rad
{:km => 6371, :miles => 3956, :feet => 20895592, :meters => 6371000}
end

def get_within dist_obj, options = {:precision => :lowest}
calc_method = get_proc(options[:precision] || :normal)
from_loc = get_location options[:from]

dist = dist_obj.distance / (RAD_PER_DEG * rad[dist_obj.unit])

res = []
spots = populate_distance(calc_method, from_loc)
spots.sort_by_distance.select do |item|
if item.dist <= dist
res << item
else
break
end
end
res
end

def get_closest number, options = {}
calc_method = get_proc(options[:precision] || :normal)
from_loc = get_location options[:from]
populate_distance(calc_method, from_loc).sort_by_distance[0..number]
end

protected

def populate_distance calc_method, from_loc
self.map! do |item|
dist_obj = calc_method.call(from_loc, item)
item.dist = dist_obj.distance
item
end
end

def get_location location
raise ArgumentError, "You must specify a :from location" if !location
raise ArgumentError, "You must specify a :from location that is a kind of GeoMagic::MapPoint" if !location.kind_of?(GeoMagic::MapPoint)
location
end

def get_proc precision
case precision
when :low
Proc.new {|point_a, point_b| ::GeoMagic::Calculate.plane_distance point_a, point_b }
when :normal
Proc.new {|point_a, point_b| ::GeoDistance::Haversine.point_distance point_a, point_b }
when :high
Proc.new {|point_a, point_b| ::GeoDistance::Vincenty.point_distance point_a, point_b }
else
raise ArgumentError, "Unknown precision: #{precision}"
end
end
end
end
end
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
30 changes: 30 additions & 0 deletions spec/geo_magic/select_nearest_spec.rb
@@ -0,0 +1,30 @@
require 'spec_helper'
require 'geo_magic'
require 'geo_magic/remote'

describe "GeoMagic closest" do
before do
@long1 = -104.88544
@lat1 = 39.06546

@center_point = GeoMagic::Point.new @long1, @lat1
@radius = @center_point.within(10.km)
@points = @radius.create_points 10
end


it "should select the closest 3 points" do
# puts "radius: #{@radius.inspect}"
# puts "points: #{@points.inspect}"
closest = @points.as_locations.get_closest 3, :from => @center_point
puts "3 closest points: #{closest.inspect}"
end

it "should select all points within 4 km" do
# puts "radius: #{@radius.inspect}"
# puts "points: #{@points.inspect}"
closest = @points.as_locations.get_within 4.km, :from => @center_point
puts "points within 4 km: #{closest.inspect}"
end

end

0 comments on commit 3874cd6

Please sign in to comment.