Better way to pick random point on earth - php

I am using
$lat=rand(0-pi()*1000000,pi()*1000000)/1000000;
$lon=rand(0-pi()*1000000,pi()*1000000)/1000000;
to pick a random point on earth(in radians) however this method results in points being closer together near polls then near equator since 1 deg of $lon is a smaller distance near the polls then at the equator.
Any ideas on how to get a better distribution?

Based on this article on Wolfram you need something like this:
$long = pi() - 2 * pi() * rand(0, 1000000) / 1000000;
$lat = pi() / 2 - acos(2 * rand(0, 1000000) / 1000000 - 1);

Related

Check on which side a lat,lng is to another lat,lng on the map

I have a map whose South-West and North-East bounds I take and use it to get places between them.
Because of the date-line sometimes the bounds doesn't come as they should as Explained here.
So I thought of working on:
Get which side the North-East lat lng is compared to the South-West,
if its on the right side its fine, but if it's on the left, I have to
do something.
So to know the side, I calculated the bearing(the angle between the line connecting these SW and NE points and a vertical line). So, if the bearing is between 0-90 its proper or else not.
But the problem is:
Here the top point qualifies for a North-East with both lat and lng
positive: 78.52379, 158.70952
and
The bottom point qualifies for a South-West with both lat and lng
negative: -32.1087, -150.3139
Still the map tries to connect the points in reverse direction(may be tries for least distance) and give the bearing as 342 which I will consider as a improper points and try to reverse 1 of them :(
Looks like this an expected way to calculate the bearing, if so is there a way to solve/achieve what I wanted?
EDIT:
function _getBearing($lat1, $lon1, $lat2, $lon2) {
//difference in longitudinal coordinates
$dLon = deg2rad($lon2) - deg2rad($lon1);
//difference in the phi of latitudinal coordinates
$dPhi = log(tan(deg2rad($lat2) / 2 + pi() / 4) / tan(deg2rad($lat1) / 2 + pi() / 4));
//we need to recalculate $dLon if it is greater than pi
if(abs($dLon) > pi()) {
if($dLon > 0) {
$dLon = (2 * pi() - $dLon) * -1;
}
else {
$dLon = 2 * pi() + $dLon;
}
}
//return the angle, normalized
return (rad2deg(atan2($dLon, $dPhi)) + 360) % 360;
}

How to find intersection points between two circles?

We have two points (centers of two circles) and their radius in meters, those radius make the circle. We need to find intersection points. For example we have lat1 = 55.685025, lng1 = 21.118995, r1 = 150 and lat2 = 55.682393, lng2 = 21.121387, r2 = 250. Below you can find our current formula:
// Find a and h.
$a = ($circle_1_r * $circle_1_r - $circle_2_r * $circle_2_r + $distance * $distance) / (2 * $distance);
$h = sqrt($circle_1_r * $circle_1_r - $a * $a);
// Find P2.
$circle_3_x = $circle_1_x + $a * ($circle_2_x - $circle_1_x) / $distance;
$circle_3_y = $circle_1_y + $a * ($circle_2_y - $circle_1_y) / $distance;
// Get the points P3.
$intersection_1 = $this->newLatLngPoint(
($circle_3_x + $h * ($circle_2_y - $circle_1_y) / $distance),
($circle_3_y - $h * ($circle_2_x - $circle_1_x) / $distance)
);
$intersection_2 = $this->newLatLngPoint(
($circle_3_x - $h * ($circle_2_y - $circle_1_y) / $distance),
($circle_3_y + $h * ($circle_2_x - $circle_1_x) / $distance)
);
We find such intersection points (yellow markers), however those locations doesn't match in real world.
Someone, can help to find where the problem is and how to sort it ?
P.S.
Does the altitude (Height above mean sea level) affect the final result? I don't use it but may be should?

Implement Great Circle Algorithm

Below is a formula (Great Circle algorithm) that I want to be translated into PHP. I would really appreciate if someone would help me with that?
The input is to different sets of coordinates, e.g:
Place 1 Lat: 59.389057
Place 1 Long: 17.937422
Place 2 Lat: 59.388914
Place 2 Long: 17.920441
The wanted output is the distance between place 1 and place 2.
distance = ((115.1666667 * (lat2-lat1)) ^ 2 + (115.1666667 * (lng2 - lng1) * cos(lat2 / 57.3)) ^ 2) ^ .5
My try:
$distance = pow(((115.1666667 * ($lat2 - $lat1)), 2) + (115.1666667 * pow(($lng2 - $lng1) * cos($lat2 / 57.3)), 2)), 0.5)
As DCoder writes in the comments, temporary variables make things easier to read:
$latD = (691 / 6) * ($lat2 - $lat1);
$lngD = (691 / 6) * ($lng2 - $lng1) * cos($lat2 / 57.3);
$distance = sqrt( $latD * $latD + $lngD * $lngD );
(demo on codepad.org)
Here's the corrected code:
$distance = pow((pow((115.1666667 * ($lat2 - $lat1)), 2) + pow((115.1666667 * ($lng2 - $lng1) * cos($lat2 / 57.3)), 2)), 0.5);

Canadian Postal Codes Radius

I am using the following scripting that I found on the net to grab all postal codes between a given set coordinates.
When using it my concern is that when some postal codes being grab are greater than the distance entered; not by much - about 20 KM off.
function GetPostalCodes($latitude, $longitude, $range) {
$radius = 3959;
$north = rad2deg(asin(sin(deg2rad($latitude)) * cos($range / $radius) + cos(deg2rad($latitude)) * sin($range / $radius) * cos(deg2rad(0))));
$south = rad2deg(asin(sin(deg2rad($latitude)) * cos($range / $radius) + cos(deg2rad($latitude)) * sin($range / $radius) * cos(deg2rad(180))));
$east = rad2deg(deg2rad($longitude) + atan2(sin(deg2rad(90)) * sin($range / $radius) * cos(deg2rad($latitude)), cos($range / $radius) - sin(deg2rad($latitude)) * sin(deg2rad($north))));
$west = rad2deg(deg2rad($longitude) + atan2(sin(deg2rad(270)) * sin($range / $radius) * cos(deg2rad($latitude)), cos($range / $radius) - sin(deg2rad($latitude)) * sin(deg2rad($north))));
$return = DBSelectAllArrays("SELECT postal FROM postalcodes WHERE (latitude <= $north AND latitude >= $south AND longitude <= $east AND longitude >= $west)");
krsort($return);
if (empty($return)) return false;
return $return;
}
Is there something I am missing to get a more accurate result?
Given your comments:
$radius = 6371.0; // mean radius of Earth in km
This is taken from wikipedia, but I've seen it within a +/- 3km tolerance from other sources.
I began to question whether you were using great circle distance calculations, but this is more important for accuracy over longer distances due to the curvature of the earths surface.
Tim, you started by using a bounding box (rectangle) and then with the Haversine formula, you'll get a radius (circle), which generally is much better if you just want people within a certain distance. You don't state your purpose, but if you're looking for people who may travel a certain distance to you, you may want to consider metropolitan areas, which vary in shape. If so, look at: Canadian Metro Areas data

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?
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);
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.
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.

Categories