在特定半径内随机选择一个经纬度的PHP代码

7
假设我有这个经纬度:33.33333,22.22222 如何在X英里/千米的半径范围内随机选择另一个经纬度?
谢谢

2
最好重新表述你的原始问题(http://stackoverflow.com/questions/5460061/drawing-anonymous-circles-in-google-maps),而不是发布重复的问题。点击你的问题下面的“编辑”链接来修改你的原始问题。 - D.N.
1
@D.N. - 你确定吗?这完全是另一个问题。 - Or Weinberger
你的问题的目标仍然是相同的,这只是你原始问题的一个具体部分。通常情况下,如果你重新表述你的原始问题,别人会更好地接受你。看起来你至少得到了一些进展,但将来请记住这一点。 - D.N.
我没有你要的答案,但你可能会发现我的PHP地理计算实现对你有所帮助 https://github.com/treffynnon/Geographic-Calculations-in-PHP - Treffynnon
4个回答

20

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

9
您可以使用此帖子作为指导: 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;

更新:

如下方评论中Tomalak所说,此处的计算是基于地球为球体而非不规则椭球体。因此,您将得到近似值而非可能(接近)精确的结果。


@Tim Nordenfur - 你是指1-10兰德吗? - Or Weinberger
我想知道那些令人惊讶的69是怎么回事,我认为它们来自于一个事实,即1度约等于69英里。 - Spacedman
@Spacedman,没错...每个度数大约相当于69英里。 - Mike Lewis
@Or W:该点距离为10英里的概率等于该点距离为1英里的概率。如果你选择在原点附近和远离原点的地方各选一个1x1平方米的区域,那么随机点落在较近区域内的概率要大得多。 - Tim
我在我的系统上有这个函数已经很长时间了,希望这能帮到你@Or W。http://codepad.org/bUHx2TWg(确保提交代码以获得新的结果,而不是刷新页面)。 - Mike Lewis
显示剩余3条评论

1

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


很遗憾,这也行不通,因为x:x的偏移量将会超出半径(距离为x*sqrt(2),或者高达允许距离的1.4倍)。 - D.N.
我的改进想法是:检查生成的坐标对与原始坐标的距离(生成的坐标减去原始坐标),如果大于X,则继续生成坐标,直到距离小于或等于X。 - Amit Eyal
感谢您的帮助,我非常感激。如果半径只有约0.7倍,则可以在进行任何其他计算之前将x乘以某个数字,因此经过乘法处理后的0.7x实际上等于1x。 - Amit Eyal
你说得没错,但为什么要进行多次计算,当最有效的方法是每次都生成一个有效答案呢?这就是使用三角函数的威力所在。它允许你将参考框架从笛卡尔坐标系(x和y坐标或“网格”)转换为极坐标系(角度和弧度坐标或“圆”)。请查看http://en.wikipedia.org/wiki/Polar_coordinate_system以了解更多信息。 - D.N.
1
谢谢提供链接。我对三角函数不是很了解,所以只能使用我知道的内容。希望明年我能学习更高级的几何知识。 - Amit Eyal
显示剩余4条评论

1
以下 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

这段代码在以lat0lon0为中心的方位等距投影上均匀采样圆形。该投影的径向和方位刻度分别为1和r/m,因此面积失真为r/m,并通过以m/r的概率接受此类点来解决该问题。
此代码还考虑了r0约为地球周长一半的情况,并避免了双重采样近似对踵点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接