Обчислення широти / довготи X миль від точки?


46

Я хочу знайти точку широти та довготи з урахуванням підшипника, відстані та початкової широти та довготи.

Це здається протилежним до цього питання ( Відстань між лат / довгими точками ).

Я вже заглянув у формулу Гаверсина і думаю, що це наближення світу, ймовірно, досить близько.

Я припускаю, що мені потрібно вирішити формулу Гаверсина для мого невідомого лат / довго, чи правильно це? Чи є хороші веб-сайти, які розповідають про подібні речі? Здається, це було б звичайно, але мій googling лише виникли питання, подібні до вище.

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


Ви шукаєте інструмент, який робить це (як pe.dll Esri) або формулу?
Кірк Куйкендалл

Вибачте, що я не був конкретним ... Шукаю формулу. Я оновлю своє запитання, щоб бути більш конкретним.
Джейсон Уайтхорн

Купа відпрацьованих зразків математики тут <a href=" movable-type.co.uk/scripts/latlong.html"> Обчисліть відстань, підшипники та інше між точками широти / довготи </a>, що включає рішення "Місце призначення точка заданої відстані та опора від точки початку ".
Стівен Куан

1
Тісно пов'язані з : gis.stackexchange.com/questions/2951 / ... .
whuber

ось сторінка, що посилається на лат / довгі обчислення [лат. / довгі обчислення] ( movable-type.co.uk/scripts/latlong.html ) також ця сторінка Lat / long розрахунки є код + калькулятор
Abo gaseem

Відповіді:


21

Мені буде цікаво, як результати цієї формули порівнюють з pe.dll Esri .

( цитування ).

Точка {lat, lon} - відстань d на радіалі tc від точки 1, якщо:

 lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
 IF (cos(lat)=0)
    lon=lon1      // endpoint a pole
 ELSE
    lon=mod(lon1-asin(sin(tc)*sin(d)/cos(lat))+pi,2*pi)-pi
 ENDIF

Цей алгоритм обмежений відстанями, такими, що dlon <pi / 2, тобто такими, які проходять довше однієї чверті окружності Землі по довготі. Повністю загальний, але складніший алгоритм необхідний, якщо допускаються великі відстані:

 lat =asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
 dlon=atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(lat))
 lon=mod( lon1-dlon +pi,2*pi )-pi

Ось HTML-сторінка для тестування .


Дякую за швидку відповідь. Дозвольте мені перетравити частину цієї інформації, і я з вами повернусь. На поверхні, однак, це виглядає плямою.
Джейсон Уайтхорн

1
Я спробував прямий випадок, використовуючи pe.dll (фактично libpe.so на solaris) після отримання відстані та азимуту вперед із сторінки html для 32N, 117W до 40N, 82W. Використовуючи 32N, 117W, d = 3255.056515890041, azi = 64.24498012065699, я отримав 40N, 82W (фактично 82.00000000064).
mkennedy

3
Дивовижно! Дякую велике за посилання на статтю про авіаційну формулу Еда Вільямса, я цього раніше не бачив, але він до цього часу виявився чудовим читанням. Лише примітка для тих, хто дивиться на це в майбутньому, входи та виходи цієї формули ВСІ в радіанах, навіть на відстані.
Джейсон Уайтхорн

1
Яка одиниця відстані у цій формулі?
Картик Муруган

1
@KarthikMurugan Еда інтро говорить одиниці виміру відстані в радіанах уздовж великого кола.
Кірк Куйкендалл

18

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

Хоча ви не в площині - ви працюєте над поверхнею зігнутого еліпсоїда, який моделює поверхню Землі - будь-яка відстань менше кількох сотень кілометрів покриває таку невелику частину поверхні, що для більшості практичних цілей вона може вважати плоским. Єдине, що залишилося ускладнення - це те, що один градус довготи не покриває ту саму відстань, що і ступінь широти. У сферичній моделі Землі один градус довготи - це лише cos (широта), доки ступінь широти. (У еліпсоїдальній моделі це все-таки відмінне наближення, добре приблизно до 2,5 значущих цифр.)

Нарешті, один градус широти приблизно 10 ^ 7/90 = 111 1111 метрів. Зараз у нас є вся інформація, необхідна для перетворення лічильників у градуси:

Зсув на північ - r * cos (a) / 111111 градусів;

Зміщення на схід - r * sin (a) / cos (широта) / 111111 градусів.

Наприклад, на широті -0,31399 градусів і опорі a = 30 градусів на схід від півночі, ми можемо обчислити

cos(a) = cos(30 degrees) = cos(pi/6 radians) = Sqrt(3)/2 = 0.866025.
sin(a) = sin(30 degrees) = sin(pi/6 radians) = 1/2 = 0.5.
cos(latitude) = cos(-0.31399 degrees) = cos(-0.00548016 radian) = 0.999984984.
r = 100 meters.
east displacement = 100 * 0.5 / 0.999984984 / 111111 = 0.000450007 degree.
north displacement = 100 * 0.866025 / 111111 = 0.000779423 degree.

Звідси, починаючи з (-78.4437, -0.31399), нове місце розташування знаходиться в (-78.4437 + 0.00045, -0.31399 + 0.0007794) = (-78.4432, -0.313211).

Більш точна відповідь у сучасній системі відліку ITRF00 - (-78.4433, -0.313207): це на 0,43 метра від приблизної відповіді, що вказує на помилки наближення на 0,43% у цьому випадку. Для досягнення більшої точності потрібно використовувати або еліпсоїдальні формули відстані (які набагато складніші), або високоточну конформальну проекцію з нульовою розбіжністю (щоб підшипник був правильним).


2
+1 для правильного розуміння математичного контексту (тобто його локальної площини)
Френк Конрі

4

Якщо вам потрібно рішення JavaScript, врахуйте це functionsта цю загадку :

var gis = {
  /**
  * All coordinates expected EPSG:4326
  * @param {Array} start Expected [lon, lat]
  * @param {Array} end Expected [lon, lat]
  * @return {number} Distance - meter.
  */
  calculateDistance: function(start, end) {
    var lat1 = parseFloat(start[1]),
        lon1 = parseFloat(start[0]),
        lat2 = parseFloat(end[1]),
        lon2 = parseFloat(end[0]);

    return gis.sphericalCosinus(lat1, lon1, lat2, lon2);
  },

  /**
  * All coordinates expected EPSG:4326
  * @param {number} lat1 Start Latitude
  * @param {number} lon1 Start Longitude
  * @param {number} lat2 End Latitude
  * @param {number} lon2 End Longitude
  * @return {number} Distance - meters.
  */
  sphericalCosinus: function(lat1, lon1, lat2, lon2) {
    var radius = 6371e3; // meters
    var dLon = gis.toRad(lon2 - lon1),
        lat1 = gis.toRad(lat1),
        lat2 = gis.toRad(lat2),
        distance = Math.acos(Math.sin(lat1) * Math.sin(lat2) +
            Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLon)) * radius;

    return distance;
  },

  /**
  * @param {Array} coord Expected [lon, lat] EPSG:4326
  * @param {number} bearing Bearing in degrees
  * @param {number} distance Distance in meters
  * @return {Array} Lon-lat coordinate.
  */
  createCoord: function(coord, bearing, distance) {
    /** http://www.movable-type.co.uk/scripts/latlong.html
    * φ is latitude, λ is longitude, 
    * θ is the bearing (clockwise from north), 
    * δ is the angular distance d/R; 
    * d being the distance travelled, R the earth’s radius*
    **/
    var 
      radius = 6371e3, // meters
      δ = Number(distance) / radius, // angular distance in radians
      θ = gis.toRad(Number(bearing));
      φ1 = gis.toRad(coord[1]),
      λ1 = gis.toRad(coord[0]);

    var φ2 = Math.asin(Math.sin(φ1)*Math.cos(δ) + 
      Math.cos(φ1)*Math.sin(δ)*Math.cos(θ));

    var λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(δ)*Math.cos(φ1),
      Math.cos(δ)-Math.sin(φ1)*Math.sin(φ2));
    // normalise to -180..+180°
    λ2 = (λ2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; 

    return [gis.toDeg(λ2), gis.toDeg(φ2)];
  },
  /**
   * All coordinates expected EPSG:4326
   * @param {Array} start Expected [lon, lat]
   * @param {Array} end Expected [lon, lat]
   * @return {number} Bearing in degrees.
   */
  getBearing: function(start, end){
    var
      startLat = gis.toRad(start[1]),
      startLong = gis.toRad(start[0]),
      endLat = gis.toRad(end[1]),
      endLong = gis.toRad(end[0]),
      dLong = endLong - startLong;

    var dPhi = Math.log(Math.tan(endLat/2.0 + Math.PI/4.0) / 
      Math.tan(startLat/2.0 + Math.PI/4.0));

    if (Math.abs(dLong) > Math.PI) {
      dLong = (dLong > 0.0) ? -(2.0 * Math.PI - dLong) : (2.0 * Math.PI + dLong);
    }

    return (gis.toDeg(Math.atan2(dLong, dPhi)) + 360.0) % 360.0;
  },
  toDeg: function(n) { return n * 180 / Math.PI; },
  toRad: function(n) { return n * Math.PI / 180; }
};

Тож якщо ви хочете обчислити нову координату, це може бути так:

var start = [15, 38.70250];
var end = [21.54967, 38.70250];
var total_distance = gis.calculateDistance(start, end); // meters
var percent = 10;
// this can be also meters
var distance = (percent / 100) * total_distance;
var bearing = gis.getBearing(start, end);
var new_coord = gis.createCoord(icon_coord, bearing, distance);

2

Це я працював в ObjectiveC. Ключовим тут є знання того, що вам потрібні точки lat / lng в радіанах, і вам потрібно перетворити їх назад у lat / lng після застосування рівняння. Також знайте, що відстань і tc знаходяться в радіанах.

Ось початкове рівняння:

 lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
 IF (cos(lat)=0)
    lon=lon1      // endpoint a pole
 ELSE
    lon=mod(lon1-asin(sin(tc)*sin(d)/cos(lat))+pi,2*pi)-pi
 ENDIF

Тут вона реалізована в ObjC, де радіан - це радіан, вимірюваний проти годинникової стрілки від N (наприклад, PI / 2 W, PI - S, 2 PI / 3 - E), а відстань - у кілометрах.

+ (CLLocationCoordinate2D)displaceLatLng:(CLLocationCoordinate2D)coordinate2D withRadian:(double)radian
                            withDistance:(CGFloat)distance {
  double lat1Radians = coordinate2D.latitude * (M_PI / 180);
  double lon1Radians = coordinate2D.longitude * (M_PI / 180);
  double distanceRadians = distance / 6371;
  double lat = asin(sin(lat1Radians)*cos(distanceRadians)+cos(lat1Radians)*sin(distanceRadians)
      *cos(radian));
  double lon;
  if (cos(lat) == 0) {
    lon = lon1Radians;
  } else {
    lon = fmodf((float) (lon1Radians - asin(sin(radian) * sin(distanceRadians) / cos(lat)) + M_PI),
        (float) (2 * M_PI)) - M_PI;
  }
  double newLat = lat * (180 / M_PI);
  double newLon = lon * (180 / M_PI);
  return CLLocationCoordinate2DMake(newLat, newLon);
}

Я шукаю рішення, де я хочу отримати 4 лати, довгі від точки, де я стою на 50 милях на північ, 50 миль на захід, 50 миль на схід і так далі ... У наведеній вище відповіді ви можете пояснити, як я можу досягти це?
Рахул Вяс

1

Якщо вас цікавить краща точність, є Вінсент . (Посилання - на "пряму" форму. Це саме те, що ви шукаєте).

Існує досить багато існуючих реалізацій, якщо ви користуєтеся кодом.

Крім того, питання: Ви не вважаєте, що мандрівник підтримує однаковий підхід для всієї поїздки, чи не так? Якщо так, то ці методи не відповідають правильному питанню. (Вам краще буде проектувати в Меркатор, малюючи пряму, а потім не проектувати результат.)


Дуже хороше запитання, незважаючи на формулювання в моєму запитанні, яке, можливо, вказувало на те, що я розраховував місце призначення для подорожнього, я не є. Хороший момент, хоча. Це було головним чином для того, щоб я міг обчислити обмежувальну площу (за невеликим замовленням, скажімо, 50 миль) ... Я сподіваюся, що це має сенс.
Джейсон Уайтхорн

gis.stackexchange.com/questions/3264/… було те саме питання (побудова обмежувальних областей з точки та відстані)
Dan S.

0

Ось рішення Python:

    def displace(self, theta, distance):
    """
    Displace a LatLng theta degrees counterclockwise and some
    meters in that direction.
    Notes:
        http://www.movable-type.co.uk/scripts/latlong.html
        0 DEGREES IS THE VERTICAL Y AXIS! IMPORTANT!
    Args:
        theta:    A number in degrees.
        distance: A number in meters.
    Returns:
        A new LatLng.
    """
    theta = np.float32(theta)

    delta = np.divide(np.float32(distance), np.float32(E_RADIUS))

    def to_radians(theta):
        return np.divide(np.dot(theta, np.pi), np.float32(180.0))

    def to_degrees(theta):
        return np.divide(np.dot(theta, np.float32(180.0)), np.pi)

    theta = to_radians(theta)
    lat1 = to_radians(self.lat)
    lng1 = to_radians(self.lng)

    lat2 = np.arcsin( np.sin(lat1) * np.cos(delta) +
                      np.cos(lat1) * np.sin(delta) * np.cos(theta) )

    lng2 = lng1 + np.arctan2( np.sin(theta) * np.sin(delta) * np.cos(lat1),
                              np.cos(delta) - np.sin(lat1) * np.sin(lat2))

    lng2 = (lng2 + 3 * np.pi) % (2 * np.pi) - np.pi

    return LatLng(to_degrees(lat2), to_degrees(lng2))

-2

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

Я використовую це для визначення площі суші, яка є багатокутником, і побудувати цей полігон в Google Землі. Назва заголовка має підшипники та відстані, записані таким чином: "NorthOrSouth x градусів y хвилин EastOrWest, z метрів до точки n;".

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

Нижче наведено javascript:

var mapCenter = new google.maps.LatLng(referencePointLatitude, referencePointLongitude); //the ref point lat and lon must be given, usually a land mark (BLLM)
var latDiv = latDiv(mapCenter); //distance per one degree latitude in this location
var lngDiv = lngDiv(mapCenter); //distance per one degree longitude in this location
var LatLng2 = NextCoord(PrevCoord,NorthOrSouth,x,y,EastOrWest,z); //next coordinate given the bearing and distance from previous coordinate
var Lat2 = LatLng2.lat(); //next coord latitude in degrees
var Lng2 = LatLng2.lng(); //next coord longitude in degrees
var polygon=[p1,p2,...,pn-1,pn,p1]; //p1,p2,etc. are the coordinates of points of the polygon, i.e. the land Title. Be sure to close the polygon to the point of beginning p1
var area = Area(polygon); //area of the polygon in sq.m.
function NextCoord(PrevCoord,NorthOrSouth,x,y,EastOrWest,z) {
  var angle = ( x + ( y / 60 ) ) * Math.PI / 180;
  var a = 1;
  var b = 1;
  if (NorthOrSouth == 'South') { a = -1; }
  if (EastOrWest == 'West') { b = -1; }
  var nextLat = PrevCoord.lat() +  ( a * z * Math.cos(angle) / latDiv );
  var nextLng = PrevCoord.lng() +  ( b * z * Math.sin(angle) / lngDiv );
  var nextCoord = new google.maps.LatLng(nextLat, nextLng);
  return nextCoord;
}
function latDiv(mapCenter) {
  var p1 = new google.maps.LatLng(mapCenter.lat()-0.5, mapCenter.lng());
  var p2 = new google.maps.LatLng(mapCenter.lat()+0.5, mapCenter.lng());
  return dist(p1,p2);
}
function lngDiv(mapCenter) {
  var p3 = new google.maps.LatLng(mapCenter.lat(), mapCenter.lng()-0.5);
  var p4 = new google.maps.LatLng(mapCenter.lat(), mapCenter.lng()+0.5);
  return dist(p3,p4);
}
function dist(pt1, pt2) {
    var dLat  = ( pt2.lat() - pt1.lat() ) * Math.PI / 180;
    var dLng = ( pt2.lng() - pt1.lng() ) * Math.PI / 180;
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +                 
            Math.cos(rad(pt1.lat())) * Math.cos(rad(pt2.lat())) *
            Math.sin(dLng/2) * Math.sin(dLng/2);
    var R = 6372800; //earth's radius
    var distance = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    return distance;
}
function Area(polygon) {
  var xPts=[];
  for (i=1; i&lt;polygon.length; i++) {
    xPts[i-1] = ( polygon[i].lat() - polygon[0].lat() ) * latDiv;
  }
  var yPts=[];
  for (i=1; i&lt;polygon.length; i++) {
    yPts[i-1] = ( polygon[i].lng() - polygon[0].lng() ) * lngDiv;
  }
  var area = 0;
  j = polygon.length-2;
  for (i=0; i&lt;polygon.length-1; i++) {
    area = area +  ( xPts[j] + xPts[i] ) * ( yPts[j] - yPts[i] );
    j = i;
  }
  area = Math.abs(area/2);
  return area;
}

1
Декартова формула для області багатокутника, яку ви намагаєтеся застосувати тут, не застосовується для відстаней та кутів, обчислених на вигнутій поверхні (наприклад, сфероїда). Ця формула робить додаткову помилку, використовуючи широту та довготу, як ніби вони були декартовими координатами. Єдиними обставинами, за яких можна було б вважати його використання, були б саме ті (для дуже малих полігонів), де формула гаверсину в будь-якому разі не потрібна. В цілому, схоже, цей код працює надто важко, щоб не отримати прибутку.
whuber
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.