Расчет широты / долготы X миль от точки?

46

Я хочу найти точку широты и долготы с учетом азимута, расстояния и начальной широты и долготы.

Это кажется противоположностью этого вопроса ( Расстояние между широтой / длинной точкой ).

Я уже изучил формулу haversine и думаю, что это приближение мира, вероятно, достаточно близко.

Я предполагаю, что мне нужно решить формулу haversine для моего неизвестного lat / long, это правильно? Есть ли хорошие сайты, которые говорят о подобных вещах? Кажется, это было бы обычным делом, но мой поиск в Google поднял только вопросы, подобные приведенному выше.

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

Джейсон Уайтхорн
источник
Вы ищете инструмент, который делает это (например, Esri's pe.dll) или формулу?
Кирк Куйкендалл
Извините, я не уточнил ... Я ищу формулу. Я обновлю свой вопрос, чтобы быть более конкретным.
Джейсон Уайтхорн
Куча разработанных математических примеров находится здесь <a href=" movable-type.co.uk/scripts/latlong.html"> Рассчитать расстояние, пеленг и многое другое между точками широты / долготы </a>, включая решение «Пункт назначения». указать заданное расстояние и направление от начальной точки ".
Стивен Куан
1
Тесно связанные: gis.stackexchange.com/questions/2951/… .
whuber
вот страница, которая ссылается на лат / длинные расчеты [лат / длинные расчеты] ( movable-type.co.uk/scripts/latlong.html ) также на этой странице лат / длинные расчеты есть код + калькулятор
Abo gaseem

Ответы:

21

Мне было бы любопытно, как результаты этой формулы сравниваются с peri Esri's .

( цитата ).

Точка {lat, lon} - это расстояние d out по радиалу 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 на Солярисе) после получения расстояния и прямого азимута со страницы HTML для 32N, 117W до 40N, 82W. Используя 32N, 117W, d = 3255.056515890041, azi = 64.24498012065699, я получил 40N, 82W (на самом деле 82.00000000064).
Mkennedy
3
Потрясающие! Большое спасибо за ссылку на статью по авиационным формулярам Эда Уильямса, я раньше такого не видел, но до сих пор она отлично читалась. Просто примечание для тех, кто смотрит на это в будущем, входы и выходы этой формулы ВСЕ в радианах, даже на расстоянии.
Джейсон Уайтхорн
1
Какова единица расстояния в этой формуле?
Картик Муруган
1
@KarthikMurugan Эда интро говорит единицы измерения расстояния в радианах вдоль большого круга.
Кирк Кайкендалл
18

Если вы находились в самолете, то точка, находящаяся на расстоянии r метров от оси в градусах к востоку от севера, смещается на r * cos (a) в северном направлении и на r * sin (a) в восточном направлении. (Эти утверждения более или менее определяют синус и косинус.)

Хотя вы не находитесь в самолете - вы работаете на поверхности изогнутого эллипсоида, который моделирует поверхность Земли - любое расстояние менее нескольких сотен километров покрывает такую ​​небольшую часть поверхности, что для большинства практических целей это может считаться плоским. Единственное оставшееся осложнение состоит в том, что один градус долготы не покрывает такое же расстояние, как градус широты. В сферической модели Земли один градус долготы составляет всего лишь cos (широту), равный градусу широты. (В эллипсоидальной модели это по-прежнему отличное приближение, примерно до 2,5 значимых цифр.)

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

Смещение на север составляет r * cos (a) / 111111 градусов;

Смещение на восток составляет r * sin (a) / cos (широта) / 111111 градусов.

Например, на широте -0,31399 градусов и направлении = 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% в этом случае. Чтобы достичь более высокой точности, вы должны использовать формулы эллипсоидального расстояния (которые намного сложнее) или высокоточный конформный проекция с нулевой расходимостью (чтобы направление было правильным).

Whuber
источник
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. Ключевым моментом здесь является знание того, что вам нужны точки широты / долготы в радианах, и вам необходимо преобразовать их обратно в широты / долготы после применения уравнения. Также знайте, что расстояние и 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);
}
BigCheesy
источник
Я ищу решение, в котором я хочу получить 4 лат, то есть расстояние от точки, где я стою, до 50 миль к северу, 50 миль к западу, 50 миль к востоку и т. Д. В ответе выше вы можете объяснить, как я могу достичь это ?
Рахул Вьяс
1

Если вы заинтересованы в большей точности, есть Винсенти . (Ссылка на «прямую» форму, которая именно то, что вы ищете).

Существует немало существующих реализаций, если вы ищете код.

Кроме того, вопрос: Вы не предполагаете, что путешественник поддерживает один и тот же подшипник на протяжении всей поездки? Если так, то эти методы не отвечают на правильный вопрос. (Вам лучше проецировать на mercator, рисовать прямую линию, а затем не проецировать результат.)

Дан С.
источник
Очень хороший вопрос, несмотря на формулировку в моем вопросе, которая могла указывать на то, что я рассчитывал пункт назначения для путешественника, я не являюсь. Хороший вопрос, хотя. Это было главным образом для того, чтобы я мог рассчитать ограничивающую область (для небольшого заказа, скажем, 50 миль) ... Я надеюсь, что это имеет смысл
Джейсон Уайтхорн
У gis.stackexchange.com/questions/3264/… был тот же вопрос (построение ограничивающих областей из точки и расстояния)
Дэн С.
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 Earth. Титул земли имеет азимуты и расстояния, написанные следующим образом: «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
Декартовая формула для площади многоугольника, которую вы пытаетесь применить здесь, не применима к расстояниям и углам, вычисленным на изогнутой поверхности (такой как сфероид). Эта формула делает дополнительную ошибку, используя широту и долготу, как если бы они были декартовыми координатами. Единственными обстоятельствами, при которых можно было бы рассмотреть его использование, были бы именно те (для очень маленьких полигонов), где формула haversine в любом случае не нужна. В целом, кажется, что этот код работает слишком усердно, чтобы не получить выгоды.
whuber