diff --git a/lib/geocoder/calculations.rb b/lib/geocoder/calculations.rb index 5b47ed02417aaa2576413493c5e3cb3eee5d8ffd..b543e6629911bf4e8d42c53a3249e24486f89c58 100644 --- a/lib/geocoder/calculations.rb +++ b/lib/geocoder/calculations.rb @@ -216,6 +216,44 @@ module Geocoder ] end + ## + # Random point within a circle of provided radius centered + # around the provided point + # Takes one point, one radius, and an options hash. + # The points are given in the same way that points are given to all + # Geocoder methods that accept points as arguments. They can be: + # + # * an array of coordinates ([lat,lon]) + # * a geocodable address (string) + # * a geocoded object (one which implements a +to_coordinates+ method + # which returns a [lat,lon] array + # + # The options hash supports: + # + # * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt> + # Use Geocoder.configure(:units => ...) to configure default units. + def random_point_near(center, radius, options = {}) + + # set default options + options[:units] ||= Geocoder.config.units + + # convert to coordinate arrays + center = extract_coordinates(center) + + earth_circumference = 2 * Math::PI * earth_radius(options[:units]) + max_degree_delta = 360.0 * (radius / earth_circumference) + + # random bearing in radians + theta = 2 * Math::PI * rand + + # random radius, use the square root to ensure a uniform + # distribution of points over the circle + r = Math.sqrt(rand) * max_degree_delta + + delta_lat, delta_long = [r * Math.cos(theta), r * Math.sin(theta)] + [center[0] + delta_lat, center[1] + delta_long] + end + ## # Convert degrees to radians. # If an array (or multiple arguments) is passed, diff --git a/test/calculations_test.rb b/test/calculations_test.rb index f8d1ee3339968d3fff53a2dea240f09e56314396..7b3687260523d882d59b33ef8a93a45f3c21c4ae 100644 --- a/test/calculations_test.rb +++ b/test/calculations_test.rb @@ -100,6 +100,17 @@ class CalculationsTest < Test::Unit::TestCase end end + # --- random point --- + + def test_random_point_within_radius + 20.times do + center = [51, 7] # Cologne, DE + radius = 10 # miles + random_point = Geocoder::Calculations.random_point_near(center, radius) + distance = Geocoder::Calculations.distance_between(center, random_point) + assert distance <= radius + end + end # --- bearing ---