Як обчислити обмежувальне поле для заданої відстані та широти / довготи


13

Мені потрібно вміти обчислювати обмежувальний ящик або коло для заданої широти WGS84 та довготи WGS84 та відстані, але не маю уявлення з чого почати!

Відстань від старту Lat / Lon складе 10 км або менше.

Чи можна було б комусь дати мені вказівки / приклад того, як це зробити


Для кіл, які не охоплюють жоден полюс, детальна відповідь наведена на gis.stackexchange.com/questions/19221/… . Але це ще не повний сюжет, як свідчать нинішні відповіді: ви можете зробити компроміси з програмою щодо швидкості та точності програми. Також зауважте, що існує проблема "обертання" при визначенні обмежувальних коробок під час роботи в lat-lon (труднощі виникають на меридіані + -180 градусів). Для вирішення цієї проблеми , див gis.stackexchange.com/questions/17788 / ... .
whuber

Вам справді потрібна скринька, або 4 пункти поблизу даної точки будуть достатніми? Давши точку p, знайдіть відстань 4 точок d у напрямках NE, SW, SE та NW від p.
Кірк Куйкендал

@Kirk - Якщо у вас є координати 4-х точок, значить, у вас є поле ...
martinstoeckli

@martinstoeckli вірно, я просто сподівався спростити проблему, не візуалізуючи, як виглядає вікно, спроектоване на сферу. Зверніть увагу також, що проблема може бути узагальнена, щоб було зрозуміло, що сторони поля не повинні падати на однакову широту / довготу (обертається поле іншими словами).
Кірк Куйкендалл

@Kirk - Ага, якщо вам це потрібно саме так, то, звичайно, ви праві. Я вважаю, що поле корисне лише для швидкого пошуку можливих кандидатів. Щоб перевірити, чи знаходяться дві точки в межах певної відстані (кола), можна використовувати більш складну формулу Гаверсина.
martinstoeckli

Відповіді:


15

WGS-що? WGS-84? Залежно від того, яка точність вам потрібна, можливо, вам доведеться знати набагато більше інформації - я думаю, саме тому ви проголосували, хоча ніхто не переймався залишати коментар із зазначенням того, чому.

Ось два способи:

Неточне, але, ймовірно, "досить добре"

Один градус широти становить приблизно 10001,965729 / 90 кілометрів (відстань від екватора до полюса, поділене на дев'яносто градусів) або 111,113 кілометрів, використовуючи дані WGS-84. Це наближення через форму землі та через те, що відстані змінюються, коли ви наближаєтесь до полюсів (одна причина використовувати широту, а не довготу - врешті-решт відстань у один градус довготи дорівнює нулю!) Земля також не є ідеальною сфери. Обидва ці причини є причиною використання більш складного підходу, що базується на прогнозах та датах, у моїй другій відповіді.

10001.965729km = 90 degrees
1km = 90/10001.965729 degrees = 0.0089982311916 degrees
10km = 0.089982311915998 degrees

Для цього використовують десяткові градуси, а не градуси / хвилини / секунди.

Отже, ваш обмежувальний ящик буде вашою точкою плюс і мінус 0,08999 градусів. Крім того, ви можете використовувати це число як радіус, даючи вам обмежувальне коло .

Будь-яка людина, що читає це ГІС, здригнеться. Це буде здебільшого точно, хоча залежно від того, де ти знаходишся у світі. Для радіусу 10 км це повинно бути добре.

Набагато точніший, але більше код

Використовуйте бібліотеку проекцій та вкажіть свою дату тощо. Я рекомендую Proj4; він широко використовується, тому Google повертає безліч результатів для запитань про нього, і є обгортки Delphi . Якщо у вас є проблеми з його використанням, опублікуйте ще одне запитання тут на SO - це не стосується цього. На веб-сайті Proj4 є приклади, що використовують основні API, і хоча вони є на C, це має бути досить легко перекладається. Їх посилання на API - найкраще місце для початку, а потім - FAQ .

Я б використовував WGS-84 як основну дату (зображення землі), якщо ви не знаєте конкретного, який ви хочете використовувати, або який був використаний для створення ваших координат. Він зазвичай використовується і досить точний.

Якщо ваше місце походить з Карт Google (наприклад), вкажіть проекцію Mercator. Ви можете використовувати іншу проекцію або використовувати, скажімо, координати UTMзамість широти та довготи, залежно від джерела ваших даних та якщо ви хочете високої точності для невеликої місцевості. (У UTM є кілька зон, всі вони змінюють спотворення так, що всередині цієї зони він є дуже точним; якщо ви використовуєте зону для координат поза нею, спотворення сильно зросте, коли ви віддаляєтесь. Якщо ви переглядаєте всю землю, прогнозовану з однієї зона може бути невпізнанною. Але в межах зони переклади UTM будуть приблизно настільки ж хорошими, скільки ви можете отримати. Координати, як правило, вказуються в метрах, а не в градусах, тому це може бути кориснішим для вас, якщо вам потрібно 10 км 10 км легко в межах однієї зони, вам потрібно вибрати відповідну зону, виходячи з координати центру. Єдиний складний біт - це коли ви підходите до кордону: це звичайна ситуація, і це добре, просто будьтепослідовно в тому, як ви обираєте, який саме будете використовувати . Proj4 також дозволить вам перекладати прогнози, так що ви можете перейти зі свого Mercator WGS-84 lat / long до зони UTM n , наприклад, або до двох з UTM-зон.)


2
Бо де ми живемо, один з інспекторів одного разу сказав мені, що він використовує 1 ступінь приблизно в 108 км для своїх розумових розрахунків. Подумки 10 км - це приблизно 0,1 градус. Оскільки це приблизні наближення, найкраще розглянути їх з точністю до 1 значної цифри (максимум 2 або 3), а не 0,089982311915998, оскільки це передбачає рівень точності.
Стівен Куан

1
Справді не важко обчислити градуси точніше, беручи до уваги широту. Оскільки комп'ютер робить обчислення, з наближенням нічого не отримується (див. Першу функцію в моєму прикладі).
martinstoeckli

3

Припускаючи, що ви хочете зробити запит у базі даних, ви, ймовірно, хочете здійснити швидкий (неточний) пошук, а потім точно обчислити відстань до отриманих місць. Це ваш сценарій?

Наступна функція (в PHP, вибачте) буде приблизно обчислювати різниці у широті та довготі. Ці відмінності залежать від широти вашої точки пошуку. Використовуйте їх (з невеликою толерантністю) для швидкого пошуку в базі даних. Поле можна обчислити просто з широтою + -дельтаШірота і довгота + -дельтаЛіглота.

deltaLatitude[rad] = distance[m] / earthRadius[m]
deltaLongitude[rad] = distance[m] / (cos(latitude[rad]) * $earthRadius[m])

/**
 * Calculates the deltas in latitude and longitude to use, for a db search
 * around a location in the database.
 * @param float $distance Radius to use for the search [m]
 * @param float $latitude Latitude of the location, we need the angle deltas for [deg decimal]
 * @param float $deltaLatitude Calculated delta in latitude [deg]
 * @param float $deltaLongitude Calculated delta in longitude [deg]
 * @param float $earthRadius Mean earth radius in [m]
 */
public static function angleFromSphericDistance($distance, $latitude,
  &$deltaLatitude, &$deltaLongitude, $earthRadius = 6371000)
{
  $lat = deg2rad($latitude);

  $radiusOnLatitude = cos($lat) * $earthRadius;
  $deltaLatitude = $distance / $earthRadius;
  $deltaLongitude = $distance / $radiusOnLatitude;

  $deltaLatitude = rad2deg($deltaLatitude);
  $deltaLongitude = rad2deg($deltaLongitude);
}

За допомогою формули Гаверсина ви можете обчислити відстані на кулі. Використовуйте його для кожного з знайдених місць, щоб отримати "точну" відстань. Таким чином ви можете перевірити, якщо два місця знаходяться в певному радіусі (коло замість поля).

/**
 * Calculates the great-circle distance between two points, with
 * the Haversine formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
public static function haversineGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $latDelta = $latTo - $latFrom;
  $lonDelta = $lonTo - $lonFrom;

  $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
    cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
  return $angle * $earthRadius;
}

3

Щоб перевірити, чи є lat / lon в межах або поза межами обмежувального кола, потрібно обчислити відстань від опорного lat / lon до точки lat / lon, яку ви хочете перевірити. Оскільки ваша відстань становить 10 км або менше, я б спробував скористатися рівнокутним наближенням, щоб отримати відстань, а не Хаверсін через простоту. Щоб отримати відстань у км:

x = (lonRef - lon) * cos ( latRef )
y = latRef - lat
distance = EarthRadius * sqrt( x*x + y*y )

Важлива примітка: lat / lon у цій формулі знаходиться в радіанах, а не в градусах. Типове значення EarthRadius - 6371 км, що поверне відстань в одиницях км. Тепер це простий тест, якщо ваша відстань знаходиться в межах або поза колом. Якщо працює обмежувальне коло, я б пішов з цим.

Для обмежуючого прямокутника я б припустив, що ви хочете, щоб прямокутник був визначений паралельно екватору. Тоді я б обчислював кути обмежувальної коробки, використовуючи розрахунки дальності / підшипників (підшипники 45, 135, 225 і 315 град). Звідси я б припустив, що ви не навколо полюсів і використовую крапку в тесті на багатокутник.


2

Нижче наведено код T-SQL, який я використовую для створення обмежувального поля в SQL-сервері 2012. У моєму випадку я отримую десяткові значення для Lat, Long. Я використовую це для швидкого обмеження кількості рядків, перш ніж використовувати STDistanceфункцію SQL, щоб перевірити, чи результати фактично знаходяться на певній відстані. Функції географії коштують дуже дорого в SQL Server, тому, будуючи обмежувальний ящик, я можу значно скоротити кількість разів, які потрібно виконати.

DECLARE @Lat DECIMAL(20, 13) = 35.7862
   ,@Long DECIMAL(20, 13) = -80.3095
   ,@Radius DECIMAL(7, 2) = 5
   ,@Distance DECIMAL(10, 2)
   ,@Earth_Radius INT = 6371000;

SET @Distance = @Radius * 1609.344;

DECLARE @NorthLat DECIMAL(20, 13) = @Lat + DEGREES(@distance / @Earth_Radius)
   ,@SouthLat DECIMAL(20, 13) = @Lat - DEGREES(@distance / @Earth_Radius)
   ,@EastLong DECIMAL(20, 13) = @Long + DEGREES(@distance / @Earth_Radius / COS(RADIANS(@Lat)))
   ,@WestLong DECIMAL(20, 13) = @Long - DEGREES(@distance / @Earth_Radius / COS(RADIANS(@Lat)));

SELECT *
    FROM CustomerPosition AS cp
    WHERE (
            cp.Lat >= @SouthLat
            AND cp.Lat <= @NorthLat )
        AND (
              cp.Long >= @WestLong
              AND cp.Long <= @EastLong )
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.