假设我有这个经纬度:
谢谢
33.33333,22.22222
如何在X英里/千米的半径范围内随机选择另一个经纬度?谢谢
33.33333,22.22222
如何在X英里/千米的半径范围内随机选择另一个经纬度?@MikeLewis的回答是一种更简单的方法,但它只给出了纬度和经度的范围,从该范围中随机选择可能会得到超出给定半径的点。
以下方法有点复杂,但应该会给您带来“更好”的结果。(可能并不需要,但我想试试 :) )。
与@MikeLewis的回答一样,这里的假设是地球是一个球体。我们不仅在公式中使用这一点,还利用旋转对称性。
首先,我们采取显而易见的方法,选择一个随机距离$distance
(小于$radius
英里),并尝试找到一个距离$distance
英里的随机点。这些点在球面上形成一个圆,您可以很快地说服自己,该圆的简单参数化很难。相反,我们考虑一个特殊情况:北极。
与北极相距固定距离的点在具有固定纬度的球面上形成一个圆(90-($ distance /(pi * 3959)* 180
)。这为我们提供了一个非常容易的方法,以在此圆上选择一个随机点:它将具有已知的纬度和随机的经度。
然后,我们只需旋转球体,使我们的北极位于最初给定的点。此旋转后我们所得到的随机点的位置即为我们所需的点。
注意:此处使用的笛卡尔<-->球面坐标变换与文献中通常所用的不同。我之所以这样做的唯一动机是使 z 轴(0,0,1)
指向北方,y 轴(0,1,0)
指向您和经度纬度都为 0 的点。因此,如果您希望想象地球,您正在看几内亚湾。
/**
* Given a $centre (latitude, longitude) co-ordinates and a
* distance $radius (miles), returns a random point (latitude,longtitude)
* which is within $radius miles of $centre.
*
* @param array $centre Numeric array of floats. First element is
* latitude, second is longitude.
* @param float $radius The radius (in miles).
* @return array Numeric array of floats (lat/lng). First
* element is latitude, second is longitude.
*/
function generate_random_point( $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 ) );
}
$longitude = (float) 33.33333;
$latitude = (float) 22.22222;
$radius = rand(1,10); // in miles
$lng_min = $longitude - $radius / abs(cos(deg2rad($latitude)) * 69);
$lng_max = $longitude + $radius / abs(cos(deg2rad($latitude)) * 69);
$lat_min = $latitude - ($radius / 69);
$lat_max = $latitude + ($radius / 69);
echo 'lng (min/max): ' . $lng_min . '/' . $lng_max . PHP_EOL;
echo 'lat (min/max): ' . $lat_min . '/' . $lat_max;
更新:
如下方评论中Tomalak所说,此处的计算是基于地球为球体而非不规则椭球体。因此,您将得到近似值而非可能(接近)精确的结果。
选择x1,一个介于0和x之间的数字。 选择x2,一个介于0和x之间的数字。 您的经度为(1/2)x1 + 原始经度,您的纬度为(1/2)x2 + 原始纬度。
function [lat, lon] = geosample(lat0, lon0, r0, n)
% [lat, lon] = geosample(lat0, lon0, r0, n)
%
% Return n points on the WGS84 ellipsoid within a distance r0 of
% (lat0,lon0) and uniformly distributed on the surface. The returned
% lat and lon are n x 1 vectors.
%
% Requires Matlab package
% http://www.mathworks.com/matlabcentral/fileexchange/39108
todo = true(n,1); lat = zeros(n,1); lon = lat;
while any(todo)
n1 = sum(todo);
r = r0 * max(rand(n1,2), [], 2); % r = r0*sqrt(U) using cheap sqrt
azi = 180 * (2 * rand(n1,1) - 1); % sample azi uniformly
[lat(todo), lon(todo), ~, ~, m, ~, ~, sig] = ...
geodreckon(lat0, lon0, r, azi);
% Only count points with sig <= 180 (otherwise it's not a shortest
% path). Also because of the curvature of the ellipsoid, large r
% are sampled too frequently, by a factor r/m. This following
% accounts for this...
todo(todo) = ~(sig <= 180 & r .* rand(n1,1) <= m);
end
end