PHP 在一定半径内随机选择经度/纬度

发布于 2024-10-28 01:19:23 字数 99 浏览 5 评论 0原文

假设我有这个经度/纬度: 33.33333,22.22222

我如何在 X 英里/公里半径内随机选择另一个经度/纬度?

谢谢,

Let's say I have this lon/lat: 33.33333,22.22222

How can I randomly select another lon/lat within an X miles/km radius?

Thanks,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

〆凄凉。 2024-11-04 01:19:23

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

@MikeLewis answer is by far a simpler approach, but it only gives you a range of latitude and longitude, and drawing randomly from that might give you points outside the given radius.

The following is a bit more complicated, but should give you 'better' results. (The chances are that isn't necessary, but I wanted to have a go :) ).

As with @MikeLewis' answer the assumption here is that Earth is a sphere. We use this not only in the formulas, but also when we exploit rotational symmetry.

The theory

First we take the obvious approach of picking a random distance $distance (less then $radius miles) and try to find a random point $distance miles away. Such points form a circle on the sphere, and you can quickly convince yourself a straightforward parametrisation of that circle is hard. We instead consider a special case: the north pole.

Points which are a set distance away from the north pole form a circle on the sphere of fixed latitude ( 90-($distance/(pi*3959)*180). This gives us a very easy way of picking a random point on this circle: it will have known latitude and random longitude.

Then we simply rotate the sphere so that our north pole sits at the point we were initially given. The position of our random point after this rotation gives us the desired point.

The code

Note: The Cartesian <--> Spherical co-ordinate transformations used here are different to what is usual in literature. My only motivation for this was to have the z-axis (0,0,1) was pointing North, and the y-axis (0,1,0) pointing towards you and towards the point with latitude and longitude equal to 0. So if you wish to imagine the earth you are looking at the Gulf of Guinea.

/**
 * 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 ) );
 }
一个人的旅程 2024-11-04 01:19:23

您可以使用这篇文章来帮助指导您:

http://blog.fedecarg.com/2009/02/08/geo-proximity-search-the-havesine-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;

更新:

正如托马拉克在下面的评论中所述,这是在地球是一个球体而不是不均匀的大地水准面的假设下进行的。因此,您将得到近似值,而不是潜在(接近)精确的结果。

You could use this post to help guide you along:

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

So with your example, you would just pick a random number between 1 and 10 miles, where 10 is your "within a certain radius".

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

Update:

As Tomalak stated in the comments below, this is working under the assumption that the earth is a sphere rather than a uneven geoid. Because of this, you will get approximations rather than potentially (near)exact results.

一杆小烟枪 2024-11-04 01:19:23

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

Pick x1, a number between 0 and x.
Pick x2, a number between 0 and x.
Your longitude is (1/2)x1 + original longitude and your latitude is (1/2)x2 + original latitude.

纸短情长 2024-11-04 01:19:23

以下 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 约为一半的情况
地球的周长并避免近对映体的双重采样
点。

The following Matlab code samples points uniformly on the ellipsoid within a specified
distance of a center point.

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

This code samples uniformly within a circle on the azimuthal equidistant
projection centered at lat0, lon0. The radial, resp. azimuthal,
scale for this projection is 1, resp. r/m. Hence the areal
distortion is r/m and this is accounted for by accepting such points
with a probability m/r.

This code also accounts for the situation where r0 is about half the
circumference of the earth and avoids double sampling nearly antipodal
points.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文