Reputation: 13949
I need to generate a spec for my search engine to test that, when I specify an origin point and a radius, results beyond results of my source origin are not included in the response, while radius within the given radius in kilometers are included.
Given the origin and the radius, I want to test two "extreme" cases where I create two test data points,
For instance if my radius is 10km, I'd like to generate a point 10.1km away and 9.9 km away from the source.
Then, I want to run many tests where I pick random points on earth (either within or outside the radius) to check that all corner cases are handled well by my search engine
I am checking the code actually works with Geocoder.distance_between
before do
# Ensure our example is correct
expect(
Geocoder::Calculations.distance_between(origin, point_outside_radius_of_origin)
).to be > radius_in_km
expect(
Geocoder::Calculations.distance_between(origin, point_within_radius_of_origin)
).to be < radius_in_km
end
For instance the following fixed example passes
Using a fixed example of Paris
let(:origin) { [48.856614, 2.3522219] } # Geocoder.coordinates('Paris')
let(:radius_in_km) { 10 }
let(:point_within_radius_of_origin) { [48.801148, 2.429443] } # Geocoder.coordinates('Maisons-Alfort')
let(:point_outside_radius_of_origin) { [48.790367, 2.455572] } # Geocoder.coordinates('Créteil')
So I'm trying to implement function to generate random points from an origin.
From https://stackoverflow.com/a/43202522/2832282 I was somehow able to generate a point within the given radius : (this function seems to work perfectly)
# Thanks to https://stackoverflow.com/a/43202522/2832282
#
# @param lon [Float]
# @param lat [Float]
# @param max_radius [FLoat] in km
#
# @return [Pair<Float>] [Lng, lat]
def random_point_within_radius_of_origin(lng:, lat:, max_radius:)
dx, dy = Utility.random_point_in_disk(max_radius)
random_lat = lat + dy / OneDegree
random_lng = lng + dx / ( OneDegree * Math::cos(lat * Math::PI / 180) )
[random_lng, random_lat]
end
# @param max_radius [Float] Distance in km
#
# @return [Pair<Float>]
def random_point_in_disk(max_radius)
r = max_radius * rand**0.5
theta = rand * 2 * Math::PI
[r * Math.cos(theta), r * Math.sin(theta)]
end
I need to implement the function below to generate a random point outside the radius from the origin (I don't really care whether uniformly distributed or not) outside the given disk. I guess I could generate a fake point and retry until I hit the goal off.
Note that if you have some code that works without specifying a max_radius this is even better (I basically just want to exclude the coordinates generated by the aforementionned function)
# @param lon [Float] Longitude of origin
# @param lat [Float] Latitude of origin
# @param max_radius [FLoat] Max radius from origin in km
# @param min_radius [FLoat] Min radius from origin in km
#
# @return [Pair<Float>] [Lng, lat] of some random point between min_radius and max_radius of origin
def random_point_within_radius_band_of_origin(lng:, lat:, max_radius:, min_radius:)
# What this SO Question is about
end
Upvotes: 0
Views: 441
Reputation: 26823
It is straight forward to combine Severin's code for generating a random point within a band with the code from random_point_within_radius_of_origin
:
# @param min_radius [Float] Minimum radius, in km
# @param max_radius [Float] Maximum radius, in km
#
# @return [Pair<Float>]
def random_point_in_band(min_radius, max_radius)
r = min_radius + (max_radius - min_radius) * Math.sqrt(rand)
theta = 2.0 * Math::PI * rand
[r * Math.cos(theta), r * Math.sin(theta)]
end
EarthRadius = 6371 # km
OneDegree = EarthRadius * 2 * Math::PI / 360 * 1000 # 1° latitude in meters
# @param lon [Float] Longitude of origin
# @param lat [Float] Latitude of origin
# @param max_radius [FLoat] Max radius from origin in km
# @param min_radius [FLoat] Min radius from origin in km
#
# @return [Pair<Float>] [Lng, lat] of some random point between min_radius and max_radius of origin
def random_point_within_radius_band_of_origin(lng:, lat:, max_radius:, min_radius:)
dx, dy = random_point_in_band(min_radius, max_radius)
random_lat = lat + dy / OneDegree
random_lng = lng + dx / ( OneDegree * Math::cos(lat * Math::PI / 180) )
[random_lng, random_lat]
end
Upvotes: 0
Reputation: 20080
If you need code to generate uniform random point in the radial band, here it is (untested!)
# @param min_radius [Float] Minimum radius, in km
# @param max_radius [Float] Maximum radius, in km
#
# @return [Pair<Float>]
def random_point_in_band(min_radius, max_radius)
r = min_radius + (max_radius - min_radius) * Math.sqrt(rand)
theta = 2.0 * Math::PI * rand
[r * Math.cos(theta), r * Math.sin(theta)]
end
Upvotes: 1