PHP Выберите случайный lon / lat в пределах определенного радиуса - PullRequest
5 голосов
/ 28 марта 2011

Допустим, у меня есть этот lon / lat: 33.33333,22.22222

Как я могу случайно выбрать другой lon / lat в радиусе X миль / км?

Спасибо,

Ответы [ 4 ]

10 голосов
/ 08 февраля 2014

@ Ответ 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 ) );
 }
6 голосов
/ 28 марта 2011

Вы можете использовать этот пост, чтобы помочь вам:

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;

Обновление:

Как Томалак заявил в комментариях ниже, это работает в предположении, что Земля - ​​это сфера, а не неровный геоид. Из-за этого вы получите приблизительные, а не (почти) точные результаты.

1 голос
/ 28 марта 2011

Выберите x1, число от 0 до x. Выберите x2, число от 0 до x. Ваша долгота равна (1/2) x1 + исходная долгота, а ваша широта составляет (1/2) x2 + первоначальная широта.

0 голосов
/ 11 февраля 2014

Следующие примеры кода 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, соответственно. г / м * +1011 *. Отсюда ареал искажение составляет р / м , и это учитывается при принятии таких точек с вероятностью м / р .

Этот код также учитывает ситуацию, когда r0 составляет примерно половину окружность земли и избегает двойной выборки почти антиподальной точек.

...