Permalink
Browse files

Refined :nearest and :farthest handling. Added :within, :beyond, and …

…:range support.

git-svn-id: http://geokit.rubyforge.org/svn/trunk@23 9265c765-0211-4c68-b2df-6d1bd6e20c4d
  • Loading branch information...
1 parent de215e5 commit b68d9848c233807e197c724a61755aa3a4b34d12 bill_eisenhauer committed Feb 17, 2007
Showing with 56 additions and 26 deletions.
  1. +0 −18 TODO
  2. +36 −8 lib/geo_kit/acts_as_mappable.rb
  3. +20 −0 test/acts_as_mappable_test.rb
View
18 TODO
@@ -1,18 +0,0 @@
-TO-DO LIST
-==========
-
-Below is a list to to-do items that need to be completed prior to initial release. Task
-assignment is designated by initials enclosed in preceeding parenthesis. Those that are
-open are open for the taking:
-
-(BE) Test under PostgreSQL and refine SQL if necessary.
-
-(BE) Apply namespacing to classes and geocoders (e.g. GeoKit::Geocoders::YahooGeocoder)
-
-(BE) Sweep files for RDOC readiness. Apply refinements where necessary.
-
-(BE) Consider combining geocoders into a single file.
-
-( ) Review and revise README to ensure its up-to-date.
-
-( ) Review test coverage and ensure adequate for release.
@@ -69,36 +69,41 @@ def find(*args)
formula = extract_formula_from_options(options)
add_distance_to_select(options, origin, units, formula) if origin
apply_find_scope(args, options)
+ apply_distance_scope(options)
substitute_distance_in_conditions(options, origin, units, formula) if origin && options.has_key?(:conditions)
args.push(options)
super(*args)
end
# Finds within a distance radius.
def find_within(distance, options={})
- origin = extract_origin_from_options(options)
- find(:all, :origin => origin, :conditions => "#{distance_column_name} <= #{distance}", :order => "#{distance_column_name} ASC")
+ options[:within] = distance
+ find(:all, options)
end
alias find_inside find_within
# Finds beyond a distance radius.
def find_beyond(distance, options={})
- origin = extract_origin_from_options(options)
- find(:all, :origin => origin, :conditions => "#{distance_column_name} > #{distance}", :order => "#{distance_column_name} ASC")
+ options[:beyond] = distance
+ find(:all, options)
end
alias find_outside find_beyond
+ # Finds according to a range. Accepts inclusive or exclusive ranges.
+ def find_by_range(range, options={})
+ options[:range] = range
+ find(:all, options)
+ end
+
# Finds the closest to the origin.
def find_closest(options={})
- origin = extract_origin_from_options(options)
- find(:first, :origin => origin, :order => "#{distance_column_name} ASC")
+ find(:nearest, options)
end
alias find_nearest find_closest
# Finds the farthest from the origin.
def find_farthest(options={})
- origin = extract_origin_from_options(options)
- find(:first, :origin => origin, :order => "#{distance_column_name} DESC")
+ find(:farthest, options)
end
# Returns the distance calculation to be used as a display column or a condition. This
@@ -115,16 +120,39 @@ def distance_sql(origin, units=default_units, formula=default_formula)
private
+ # Looks for mapping-specific tokens and makes appropriate translations so that the
+ # original finder has its expected arguments. Resets the the scope argument to
+ # :first and ensures the limit is set to one.
def apply_find_scope(args, options)
case args.first
when :nearest
args[0] = :first
+ options[:limit] = 1
options[:order] = "#{distance_column_name} ASC"
when :farthest
args[0] = :first
+ options[:limit] = 1
options[:order] = "#{distance_column_name} DESC"
end
end
+
+ # Replace :within, :beyond and :range distance tokens with the appropriate distance
+ # where clauses. Removes these tokens from the options hash.
+ def apply_distance_scope(options)
+ distance_condition = "#{distance_column_name} <= #{options[:within]}" if options.has_key?(:within)
+ distance_condition = "#{distance_column_name} > #{options[:beyond]}" if options.has_key?(:beyond)
+ distance_condition = "#{distance_column_name} >= #{options[:range].first} AND #{distance_column_name} <= #{options[:range].last}" if options.has_key?(:range)
+ [:within, :beyond, :range].each { |option| options.delete(option) } if distance_condition
+ if distance_condition && options.has_key?(:conditions)
+ original_conditions = options[:conditions]
+ condition = original_conditions.is_a?(String) ? original_conditions : original_conditions.first
+ condition = "#{distance_condition} AND #{condition}"
+ original_conditions = condition if original_conditions.is_a?(String)
+ original_conditions[0] = condition if original_conditions.is_a?(Array)
+ elsif distance_condition
+ options[:conditions] = distance_condition
+ end
+ end
# Extracts the origin instance out of the options if it exists and returns
# it. If there is no origin, looks for latitude and longitude values to
@@ -114,6 +114,11 @@ def test_find_within
assert_equal 5, locations.size
end
+ def test_find_within_with_token
+ locations = Location.find(:all, :within => 3.97, :origin => @loc_a)
+ assert_equal 5, locations.size
+ end
+
def test_find_within_with_coordinates
locations = Location.find_within(3.97, :origin =>[@loc_a.lat,@loc_a.lng])
assert_equal 5, locations.size
@@ -134,11 +139,26 @@ def test_find_beyond
assert_equal 1, locations.size
end
+ def test_find_beyond_with_token
+ locations = Location.find(:all, :beyond => 3.95, :origin => @loc_a)
+ assert_equal 1, locations.size
+ end
+
def test_find_beyond_with_coordinates
locations = Location.find_beyond(3.95, :origin =>[@loc_a.lat, @loc_a.lng])
assert_equal 1, locations.size
end
+ def test_find_range_with_token
+ locations = Location.find(:all, :range => 0..10, :origin => @loc_a)
+ assert_equal 6, locations.size
+ end
+
+ def test_find_range_with_token_with_conditions
+ locations = Location.find(:all, :origin => @loc_a, :range => 0..10, :conditions => ["city = ?", 'Coppell'])
+ assert_equal 2, locations.size
+ end
+
def test_find_nearest
assert_equal @loc_a, Location.find_nearest(:origin => @loc_a)
end

0 comments on commit b68d984

Please sign in to comment.