Отримати широту / довжину, задану поточну точку, відстань та підшипник


77

Враховуючи існуючу точку в ширину / довжину, відстань у (в КМ) та підшипник (у градусах, перетворених у радіани), я хотів би розрахувати нову ширину / довжину. Цей сайт з’являється знову і знову, але я просто не можу змусити формулу працювати для мене.

Формули, наведені за посиланням вище:

lat2 = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ))

lon2 = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2))

Наведена формула призначена для MSExcel, де

asin          = arc sin()   
d             = distance (in any unit)   
R             = Radius of the earth (in the same unit as above)  
and hence d/r = is the angular distance (in radians)  
atan2(a,b)    = arc tan(b/a)  
θ is the bearing (in radians, clockwise from north);  

Ось код, який я отримав у Python.

import math

R = 6378.1 #Radius of the Earth
brng = 1.57 #Bearing is 90 degrees converted to radians.
d = 15 #Distance in km

#lat2  52.20444 - the lat result I'm hoping for
#lon2  0.36056 - the long result I'm hoping for.

lat1 = 52.20472 * (math.pi * 180) #Current lat point converted to radians
lon1 = 0.14056 * (math.pi * 180) #Current long point converted to radians

lat2 = math.asin( math.sin(lat1)*math.cos(d/R) +
             math.cos(lat1)*math.sin(d/R)*math.cos(brng))

lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
                     math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

print(lat2)
print(lon2)

я отримав

lat2 = 0.472492248844 
lon2 = 79.4821662373

1
@GWW Я отримував відповідь, яка не мала сенсу. Причина в цьому не мала сенсу, тому що я не перетворював відповіді назад у градуси. Код змінено та включено до вихідної публікації як редагування.
— Девід М

6
Вам слід просто надіслати свою редакцію як відповідь та прийняти цю відповідь, щоб чіткіше пояснити, що ви вирішили власну проблему. В іншому випадку SO буде карати вас за те, що ви залишили питання невирішеним, що робить трохи більш імовірним, що майбутні користувачі не потурбуються відповісти на ваші запитання.
— Cerin

Ви будете отримувати кращу точність і результати, якщо будете використовувати numpy-об'єкти.
— Mike Pennington,

@Cerin - дякую за пораду.
— Девід М

чи не має це бути "lat1 = 52.20472 * (math.pi * / 180)"?
— Тушар Сет

Відповіді:


90

Потрібно для перетворення відповідей із радіанів назад у градуси. Робочий код нижче:

import math

R = 6378.1 #Radius of the Earth
brng = 1.57 #Bearing is 90 degrees converted to radians.
d = 15 #Distance in km

#lat2  52.20444 - the lat result I'm hoping for
#lon2  0.36056 - the long result I'm hoping for.

lat1 = math.radians(52.20472) #Current lat point converted to radians
lon1 = math.radians(0.14056) #Current long point converted to radians

lat2 = math.asin( math.sin(lat1)*math.cos(d/R) +
     math.cos(lat1)*math.sin(d/R)*math.cos(brng))

lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
             math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

lat2 = math.degrees(lat2)
lon2 = math.degrees(lon2)

print(lat2)
print(lon2)

той самий результат і для мене
— Боден


Я помітив, що якщо вихідна широта дорівнює 0, початкова довгота дорівнює -179, підшипник 270 градусів (радіатори 1,5 дюйми), а відстань становить 1500 км, отримана довгота дорівнює -192,4, чого немає на карті.
— JVE999,

Дякуємо, що реалізували фрагмент у C # gist.github.com/BicycleMark/3e1a2152febaa2935e4c8cfcea7e061b
— Марк Уорделл

Я перевірив вихідний код за допомогою: fcc.gov/media/radio/find-terminal-coordinate
— Майкл Беренс

33

Бібліотека geopy підтримує це:

import geopy
from geopy.distance import VincentyDistance

# given: lat1, lon1, b = bearing in degrees, d = distance in kilometers

origin = geopy.Point(lat1, lon1)
destination = VincentyDistance(kilometers=d).destination(origin, b)

lat2, lon2 = destination.latitude, destination.longitude

Знайдено через https://stackoverflow.com/a/4531227/37610


у цій бібліотеці є деякі проблеми з відстанню, які чекають свого вирішення: github.com/geopy/geopy/pull/144
— Джастін Понельт

14

Це питання відоме як безпосередня проблема при вивченні геодезії .

Це справді дуже популярне питання, яке постійно викликає плутанину. Причина полягає в тому, що більшість людей шукає просту і пряму відповідь. Але такого немає, оскільки більшість людей, які задають це питання, не надають достатньо інформації, просто тому, що вони не знають, що:

  1. Земля не є ідеальною сферою, оскільки вона сплющена / стискається полюсами
  2. З (1) земля не має постійного радіуса, R. Дивіться тут .
  3. Земля не ідеально гладка (коливання висоти) тощо.
  4. Через рух тектонічної плити, географічне положення географічної точки може змінюватися щороку на кілька міліметрів (принаймні).

Тому існує багато різних припущень, що використовуються в різних геометричних моделях, які застосовуються по-різному, залежно від вашої необхідної точності. Отже, щоб відповісти на запитання, потрібно врахувати, наскільки точно ви хотіли б отримати свій результат.

Кілька прикладів:

  • Я просто шукав приблизне місце розташування з точністю до декількох кілометрів для малих ( < 100 км) відстані в latitudesміж 0-70 deg N | S . (Земля ~ плоска модель.)
  • Я хочу відповідь, яка є доброю в будь-якій точці земної кулі, але точною лише до декількох метрів
  • Я хочу надто точне позиціонування, яке дійсне аж до атомних масштабів nanometers[нм].
  • Я хочу відповіді, які дуже швидкі та легкі для обчислення, а не обчислювальні.

Таким чином, ви можете мати багато варіантів, в якому алгоритмі використовувати. Крім того, кожна мова програмування має власну реалізацію або "пакет", помножений на кількість моделей та конкретні потреби розробників моделі. З усіх практичних цілей тут вигідно ігнорувати будь-яку іншу мову, крім javascript, оскільки вона дуже нагадує псевдокод за своєю природою. Таким чином, його можна легко перетворити на будь-яку іншу мову, з мінімальними змінами.

Тоді основними моделями є:

  • Euclidian/Flat earth model: добре для дуже коротких відстаней до ~ 10 км
  • Spherical model: добре для великих поздовжніх відстаней, але з невеликою широтною різницею. Популярна модель:
    • Haversine : точність вимірювача на вагах [км], дуже простий код.
  • Ellipsoidal models: Найточніший при будь-якій широті / довжині та відстані, але все-таки це числове наближення, яке залежить від того, яка точність вам потрібна. Деякі популярні моделі:

Список літератури:


13

Можливо, трохи пізно відповісти, але після перевірки інших відповідей виявляється, що вони працюють неправильно. Ось PHP-код, який ми використовуємо для нашої системи. Робота в усіх напрямках.

PHP-код:

lat1 = широта початкової точки в градусах

long1 = довгота початкової точки в градусах

d = відстань в КМ

кут = підшипник у градусах

function get_gps_distance($lat1,$long1,$d,$angle)
{
    # Earth Radious in KM
    $R = 6378.14;

    # Degree to Radian
    $latitude1 = $lat1 * (M_PI/180);
    $longitude1 = $long1 * (M_PI/180);
    $brng = $angle * (M_PI/180);

    $latitude2 = asin(sin($latitude1)*cos($d/$R) + cos($latitude1)*sin($d/$R)*cos($brng));
    $longitude2 = $longitude1 + atan2(sin($brng)*sin($d/$R)*cos($latitude1),cos($d/$R)-sin($latitude1)*sin($latitude2));

    # back to degrees
    $latitude2 = $latitude2 * (180/M_PI);
    $longitude2 = $longitude2 * (180/M_PI);

    # 6 decimal for Leaflet and other system compatibility
   $lat2 = round ($latitude2,6);
   $long2 = round ($longitude2,6);

   // Push in array and get back
   $tab[0] = $lat2;
   $tab[1] = $long2;
   return $tab;
 }

1
Виглядайте добре, але я думаю, що запитувач хотів би мати щось у python. Неправильно?
— user2360915

може бути краще названий get_gps_coordабо подібний. Ви не отримуєте відстань, ви надаєте це функції. Але дякую за це, це саме те, що я шукав. Багато пошукових запитів повертають обчислення відстані між шнурами (помилкові спрацьовування). Дякую!
— Dev Null

Чудово! Дякуємо за ваш внесок!
— джамікато

1
6,378.14 kmздається максимальним радіусом Землі. Середнє значення приблизно 6,371.0 km, що може дозволити більш точні розрахунки.
— JVE999

7

Я переніс відповідь Бреда на ванільну відповідь JS, без залежності від Bing maps

https://jsfiddle.net/kodisha/8a3hcjtd/

    // ----------------------------------------
    // Calculate new Lat/Lng from original points
    // on a distance and bearing (angle)
    // ----------------------------------------
    let llFromDistance = function(latitude, longitude, distance, bearing) {
      // taken from: https://stackoverflow.com/a/46410871/13549 
      // distance in KM, bearing in degrees
    
      const R = 6378.1; // Radius of the Earth
      const brng = bearing * Math.PI / 180; // Convert bearing to radian
      let lat = latitude * Math.PI / 180; // Current coords to radians
      let lon = longitude * Math.PI / 180;
    
      // Do the math magic
      lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
      lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance / R) - Math.sin(lat) * Math.sin(lat));
    
      // Coords back to degrees and return
      return [(lat * 180 / Math.PI), (lon * 180 / Math.PI)];
    
    }
    
    let pointsOnMapCircle = function(latitude, longitude, distance, numPoints) {
      const points = [];
      for (let i = 0; i <= numPoints - 1; i++) {
        const bearing = Math.round((360 / numPoints) * i);
        console.log(bearing, i);
        const newPoints = llFromDistance(latitude, longitude, distance, bearing);
        points.push(newPoints);
      }
      return points;
    }
    
    const points = pointsOnMapCircle(41.890242042122836, 12.492358982563019, 0.2, 8);
    let geoJSON = {
      "type": "FeatureCollection",
      "features": []
    };
    points.forEach((p) => {
      geoJSON.features.push({
        "type": "Feature",
        "properties": {},
        "geometry": {
          "type": "Point",
          "coordinates": [
            p[1],
            p[0]
          ]
        }
      });
    });
    
    document.getElementById('res').innerHTML = JSON.stringify(geoJSON, true, 2);

Крім того, я додав geoJSONекспорт, тому ви можете просто вставити отриманий geoJSON в: http://geojson.io/#map=17/41.89017/12.49171щоб миттєво побачити результати.

Результат: Знімок екрана geoJSON


Карта геойсона дуже корисна для мене для націлювання місця на карті
— cloudcomputes

Дякую @kodisha, ваша скрипка мені дуже допомагає!
— Дуглас Шмідт,

5

Швидкий спосіб використання геопії

from geopy import distance
#distance.distance(unit=15).destination((lat,lon),bering) 
#Exemples
distance.distance(nautical=15).destination((-24,-42),90) 
distance.distance(miles=15).destination((-24,-42),90)
distance.distance(kilometers=15).destination((-24,-42),90) 

2
Без зазначення методу, який ви використовуєте для розрахунку, відповідь в основному марна.
— not2qubit

1
@ not2qubit Незалежно від того, чи знали @ plinio-bueno-andrade-silva, чи ні, geopy.distance.distance currently uses geodesic. геопія. А якщо бути більш конкретним, то еліпсоїдальна модель, яка використовується за замовчуванням, - еліпсоїд WGS-84, "який є найбільш точним у світовому масштабі".
— hlongmore

3

lon1 та lat1 у градусах

brng = підшипник в радіанах

d = відстань у км

R = радіус Землі в км

lat2 = math.degrees((d/R) * math.cos(brng)) + lat1
long2 = math.degrees((d/(R*math.sin(math.radians(lat2)))) * math.sin(brng)) + long1

Я реалізував ваш та мій алгоритми в PHP і порівняв його. Ця версія працювала приблизно в 50% випадків. Отримані результати були однаковими, тому, здається, математично еквівалентні.

Я не тестував код python вище, тому можуть бути синтаксичні помилки.


Не працює. З півночі на південь результат правильний, але неправильний у напрямку "Схід-Захід".
— Петро

1

Також пізно, але для тих, хто може це знайти, ви отримаєте точніші результати за допомогою бібліотеки geographiclib . Перегляньте описи геодезичних завдань та приклади JavaScript для легкого вступу до того, як використовувати відповіді на питання, а також багато інших. Реалізації на різних мовах, включаючи Python. Набагато краще, ніж кодування власного, якщо ви дбаєте про точність; краще, ніж VincentyDistance у попередній рекомендації "користуватися бібліотекою". Як сказано в документації: "Наголос робиться на поверненні точних результатів з помилками, близькими до округлення (близько 5–15 нанометрів)".


1

Просто обміняйтеся значеннями у функції atan2 (y, x). Не atan2 (x, y)!


1

Я переніс Python на Javascript. Це повертає об’єкт Bing Maps Location, ви можете змінити його на будь-який, що вам подобається.

getLocationXDistanceFromLocation: function(latitude, longitude, distance, bearing) {
    // distance in KM, bearing in degrees

    var R = 6378.1,                         // Radius of the Earth
        brng = Math.radians(bearing)       // Convert bearing to radian
        lat = Math.radians(latitude),       // Current coords to radians
        lon = Math.radians(longitude);

    // Do the math magic
    lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
    lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance/R)-Math.sin(lat)*Math.sin(lat));

    // Coords back to degrees and return
    return new Microsoft.Maps.Location(Math.degrees(lat), Math.degrees(lon));

},

Будь ласка, опублікуйте функціональний код, включаючи те, що він повинен працювати. Тобто це, здається, залежить від Microsoft.Maps. Де знайти / як це встановити?
— not2qubit

Ви використовуєте Bing (Microsoft) Maps, лише якщо ваша програма використовує Bing Maps. Просто візьміть Math.degrees(lat)і Math.degrees(lon)цінності і робити з ними все , що вам потрібно для вашого застосування.
— Бред Метьюз

0

Ось версія PHP, заснована на Авіаційному формулярі Еда Вільямса. Модуль обробляється дещо інакше в PHP. Це працює для мене.

function get_new_waypoint ( $lat, $lon, $radial, $magvar, $range )
{

   // $range in nm.
   // $radial is heading to or bearing from    
   // $magvar for local area.

   $range = $range * pi() /(180*60);
   $radial = $radial - $magvar ;

   if ( $radial < 1 )
     {
        $radial = 360 + $radial - $magvar; 
     }
   $radial =  deg2rad($radial);
   $tmp_lat = deg2rad($lat);
   $tmp_lon = deg2rad($lon);
   $new_lat = asin(sin($tmp_lat)* cos($range) + cos($tmp_lat) * sin($range) * cos($radial));
   $new_lat = rad2deg($new_lat);
   $new_lon = $tmp_lon - asin(sin($radial) * sin($range)/cos($new_lat))+ pi() % 2 * pi() -  pi();
   $new_lon = rad2deg($new_lon);

   return $new_lat." ".$new_lon;

}

Не могли б ви пояснити пару змінних? $ range та $ magvar могли б використати трохи більше експозиції для читачів-початківців, таких як (я :)
— Tommie C.

Будь ласка, перегляньте мою відповідь та посилання на формулу, яку вона використовує, і на яку точність ми можемо очікувати.
— not2qubit

0

Я переніс відповідь з @David M на Java, якщо хтось цього хотів ... Я отримую трохи інший результат 52.20462299620793, 0.360433887489931

    double R = 6378.1;  //Radius of the Earth
    double brng = 1.57;  //Bearing is 90 degrees converted to radians.
    double d = 15;  //Distance in km

    double lat2 = 52.20444; // - the lat result I'm hoping for
    double lon2 = 0.36056; // - the long result I'm hoping for.

    double lat1 = Math.toRadians(52.20472); //Current lat point converted to radians
    double lon1 = Math.toRadians(0.14056); //Current long point converted to radians

    lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
            Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng));

    lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
            Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));

    lat2 = Math.toDegrees(lat2);
    lon2 = Math.toDegrees(lon2);

    System.out.println(lat2 + ", " + lon2);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.