PHP选择特定半径内的随机lon / lat

时间:2011-03-28 14:54:34

标签: php geospatial spatial

假设我有这个lon / lat:33.33333,22.22222

如何在X英里/公里范围内随机选择另一个lon / lat?

谢谢,

4 个答案:

答案 0 :(得分:10)

@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 ) );
 }

答案 1 :(得分:6)

您可以使用此帖子来指导您:

http://blog.fedecarg.com/2009/02/08/geo-proximity-search-the-haversine-equation/

所以在你的例子中,你只需要选择1到10英里之间的随机数,其中10是你的“在一定范围内”。

$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;

<强>更新

正如托马拉克在下面的评论中所说,这是在假设地球是一个球体而不是一个不均匀的大地水准面的情况下工作的。因此,您将获得近似值,而不是可能(接近)精确结果。

答案 2 :(得分:1)

选择x1,一个0到x之间的数字。 选择x2,一个介于0和x之间的数字。 您的经度是(1/2)x1 +原始经度,纬度是(1/2)x2 +原始纬度。

答案 3 :(得分:0)

以下Matlab代码示例在指定的椭圆体内均匀分析 中心点的距离。

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

此代码在方位角等距的圆内均匀采样 投影以 lat0 lon0 为中心。径向,分别。方位, 这个预测的比例是1,相应的。 R / M 。因此是区域 失真是 r / m ,这是通过接受这些点来解决的 概率 m / r

此代码还说明了 r0 大约一半的情况 地球的周长,避免双重采样几乎反对 分。