简体   繁体   中英

Generate random coordinates around a location

I'd like to have a function that accepts a geo location (Latitude, Longitude) and generates random sets of coordinates around it but also takes these parameters as a part of the calculation:

  • Number Of Random Coordinates To Make
  • Radius to generate in
  • Min distance between the random coordinates in meters
  • The root coordinates to generate the locations around it.

Example of how the generation would be:

例

What's a good approach to achieve this?

A brute force method should be good enough.

for each point to generate "n"
  find a random angle
  get the x and y from the angle * a random radius up to max radius
  for each point already generated "p"
     calculate the distance between "n" and "p"
  if "n" satisfies the min distance
     add new point "n"

In PHP, generating a new point is easy

$angle = deg2rad(mt_rand(0, 359));
$pointRadius = mt_rand(0, $radius);
$point = array(
   'x' => sin($angle) * $pointRadius,
   'y' => cos($angle) * $pointRadius
);

Then calculating the distance between two points

$distance = sqrt(pow($n['x'] - $p['x'], 2) + pow($n['y'] - $p['y'], 2));

** Edit **

For the sake of clarifying what others have said, and after doing some further research (I'm not a mathematician, but the comments did make me wonder), here the most simple definition of a gaussian distribution :

If you were in 1 dimension, then $pointRadius = $x * mt_rand(0, $radius); would be OK since there is no distinction between $radius and $x when $x has a gaussian distribution.

In 2 or more dimensions, however, if the coordinates ($x,$y,...) have gaussian distributions then the radius $radius does not have a gaussian distribution.

In fact the distribution of $radius^2 in 2 dimensions [or k dimensions] is what is called the "chi-squared distribution with 2 [or k] degrees of freedom", provided the ($x,$y,...) are independent and have zero means and equal variances.

Therefore, to have a normal distribution, you'd have to change the line of the generated radius to

$pointRadius = sqrt(mt_rand(0, $radius*$radius));

as others have suggested.

Generate random coordinates around a location

function generateRandomPoint($centre, $radius) {
    $radius_earth = 3959; //miles

    //Pick random distance within $distance;
    $distance = lcg_value()*$radius;

    //Convert degrees to radians.
    $centre_rads = array_map( 'deg2rad', $centre );

    //First suppose our point is the north pole.
    //Find a random point $distance miles away
    $lat_rads = (pi()/2) -  $distance/$radius_earth;
    $lng_rads = lcg_value()*2*pi();


    //($lat_rads,$lng_rads) is a point on the circle which is
    //$distance miles from the north pole. Convert to Cartesian
    $x1 = cos( $lat_rads ) * sin( $lng_rads );
    $y1 = cos( $lat_rads ) * cos( $lng_rads );
    $z1 = sin( $lat_rads );


    //Rotate that sphere so that the north pole is now at $centre.

    //Rotate in x axis by $rot = (pi()/2) - $centre_rads[0];
    $rot = (pi()/2) - $centre_rads[0];
    $x2 = $x1;
    $y2 = $y1 * cos( $rot ) + $z1 * sin( $rot );
    $z2 = -$y1 * sin( $rot ) + $z1 * cos( $rot );

    //Rotate in z axis by $rot = $centre_rads[1]
    $rot = $centre_rads[1];
    $x3 = $x2 * cos( $rot ) + $y2 * sin( $rot );
    $y3 = -$x2 * sin( $rot ) + $y2 * cos( $rot );
    $z3 = $z2;


    //Finally convert this point to polar co-ords
    $lng_rads = atan2( $x3, $y3 );
    $lat_rads = asin( $z3 );

    return array_map( 'rad2deg', array( $lat_rads, $lng_rads ) );
}

Usage

generateRandomPoint(array(3.1528, 101.7038), 4);

as the other answer says, the simplest approach is going to be generating random points and then discarding ones that are too close to others (don't forget to check for min distance to central point too, if necessary).

however, generating the random points is harder than explained. first, you need to select the radius at random. second, you need to have more points at large radii (because there's "more room" out there). so you cannot just make radius a uniform random number.

instead, choose a number between 0 and $radius * $radius . then take the sqrt() of that to find the radius to plot at (this works because area is proportional to square of the radius).

i don't know php ( see the correction by Karolis in the comments ), but from the other answer i think that would mean:

$angle = deg2rad(mt_rand(0, 359));
$radius = sqrt(mt_rand(0, $max_radius * $max_radius));

then check that against the previous points as already described.

finally, don't forget that you can reach a state where you can generate no more points, so you may want to put an upper limit on the "try and discard" loop to avoid hitting an infinite loop when the space is (close to) full.

ps as a comment says on another answer, this is O(n^2) and so unsuitable for large numbers of points. you can address that to some extent by sorting the points by radius and only considering those within a difference of $min_distance , as long as $min_distance << $max_radius (as it is in your drawing); doing better than that requires a more complex solution (for example, at larger radii also using angle, or using a separate quad tree to store and compare positions). but for tens of points i imagine that would not be necessary.

Others have already explained the math you need. But I think the most problematic part is the performance. The brute force method to check the distances between the points can be good enough when you have 50 points only. But too slow when you have 1000 points or even more. For 1000 points this requires at least half a million operations.

Therefore my suggestion would be to save all randomly generated points into B-tree or binary search tree (by x value and by y value). Using an ordered tree you will be able to get the points which are in the area [x ± min_distance, y ± min_distance] efficiently. And these are the only points that need to be checked, drastically reducing the number of needed operations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM