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


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.