Как найти lat / long, который находится на x км к северу от заданного lat / long?

У меня есть код C #, который генерирует карты Google. Эти коды просматривают все Точки, которые мне нужны для построения на карте, а затем выстраивают границы прямоугольника, чтобы включить эти точки. Затем он передает эту оценку API Карт Google, чтобы установить уровень масштабирования соответствующим образом, чтобы отобразить все точки на карте.

Этот код работает нормально, но у меня есть новое требование.

Одна из точек может иметь связанную с ней точность. Если это так, тогда я рисую круг вокруг точки с радиусом, заданным для значения точности. Опять же, это прекрасно работает, однако моя проверка границ теперь не делает то, что я хочу, чтобы она делала. Я хочу, чтобы ограничивающая рамка включала полный круг.

Для этого требуется, чтобы алгоритм взял точку x и вычислил точку y, которая была бы z метров к северу от x, а также z метров к югу от x.

Кто-нибудь имеет этот алгоритм, предпочтительно на C #. Я нашел общий алгоритм здесь, но я, кажется, не реализовал это правильно, так как ответы, которые я получаю, являются 1000-м км по течению.

Это общий пример

Lat/lon given radial and distance A point {lat,lon} is a distance d out on the tc radial from point 1 if: 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 

И это мой перевод на C #.

  // Extend a Point North/South by the specified distance public static Point ExtendPoint(Point _pt, int _distance, int _bearing ) { Decimal lat = 0.0; Decimal lng = 0.0; lat = Math.Asin(Math.Sin(_pt.Lat) * Math.Cos(_distance) + Math.Cos(_pt.Lat) * Math.Sin(_distance) * Math.Cos(_bearing)); if (Math.Cos(lat) == 0) { lng = _pt.Lng; // endpoint a pole } else { lng = ( (_pt.Lng - Math.Asin(Math.Sin(_bearing) * Math.Sin(_distance) / Math.Cos(lat)) + Math.PI) % (2 * Math.PI)) - Math.PI; } ret = new Point(lat,lng); return ret; } 

Я называю эту функцию несущей с 0 для вычисления новой северной позиции и значением 180 для вычисления нового южного положения.

Может ли кто-нибудь увидеть, что я сделал неправильно, или, возможно, предоставить известный алгоритм работы?

    Если у вас заданная широта и долгота, вы можете рассчитать правильную широту и долготу изменения x-km в широте следующим образом:

     new-lat = ((old-km-north + x-km-change)/40,075) * 360) ^ is the ratio of the ^ times the ratio of the circle of the earth the change by 360 to get the total ratio covers. covered in degrees. 

    То же самое можно применить и к долготе. Если у вас есть общее расстояние плюс изменение, вы можете рассчитать общие степени аналогичным образом.

     new-long = ((old-km-east + x-km-change)/40,075) * 360) ^ is the ratio of the ^ times the ratio of the circle of the earth the change by 360 to get the total ratio covers. covered in degrees. 

    Опять же, эти вычисления должны работать, но я убегаю от чистой интуиции здесь, но логика, похоже, сохраняется.

    Изменить: Как указано Skizz 40,075, необходимо отрегулировать по окружности земли на любой заданной широте с использованием 2.pi.r.cos (lat) или 40074.cos (lat)

    У меня очень похожий код. Это принесло мне очень близкие результаты по сравнению с другой реализацией.

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

     ///  /// Calculates the end-point from a given source at a given range (meters) and bearing (degrees). /// This methods uses simple geometry equations to calculate the end-point. ///  /// Point of origin /// Range in meters /// Bearing in degrees /// End-point from the source given the desired range and bearing. public static LatLonAlt CalculateDerivedPosition(LatLonAlt source, double range, double bearing) { double latA = source.Latitude * UnitConstants.DegreesToRadians; double lonA = source.Longitude * UnitConstants.DegreesToRadians; double angularDistance = range / GeospatialConstants.EarthRadius; double trueCourse = bearing * UnitConstants.DegreesToRadians; double lat = Math.Asin( Math.Sin(latA) * Math.Cos(angularDistance) + Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse)); double dlon = Math.Atan2( Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA), Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat)); double lon = ((lonA + dlon + Math.PI) % UnitConstants.TwoPi) - Math.PI; return new LatLonAlt( lat * UnitConstants.RadiansToDegrees, lon * UnitConstants.RadiansToDegrees, source.Altitude); } 

    где

     public const double EarthRadius = 6378137.0; // WGS-84 ellipsoid parameters 

    и LatLonAlt находится в gradleусах / метрах (преобразование происходит внутри). При необходимости отрегулируйте.

    Я предполагаю, что вы можете понять, что значение для UnitConstants.DegreesToRadians :

    Для ленивых людей (как и я;)) решение для копирования пасты, версия Эриха Мирабала с очень незначительными изменениями:

     using System.Device.Location; // add reference to System.Device.dll public static class GeoUtils { ///  /// Calculates the end-point from a given source at a given range (meters) and bearing (degrees). /// This methods uses simple geometry equations to calculate the end-point. ///  /// Point of origin /// Range in meters /// Bearing in degrees /// End-point from the source given the desired range and bearing. public static GeoCoordinate CalculateDerivedPosition(this GeoCoordinate source, double range, double bearing) { var latA = source.Latitude * DegreesToRadians; var lonA = source.Longitude * DegreesToRadians; var angularDistance = range / EarthRadius; var trueCourse = bearing * DegreesToRadians; var lat = Math.Asin( Math.Sin(latA) * Math.Cos(angularDistance) + Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse)); var dlon = Math.Atan2( Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA), Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat)); var lon = ((lonA + dlon + Math.PI) % (Math.PI*2)) - Math.PI; return new GeoCoordinate( lat * RadiansToDegrees, lon * RadiansToDegrees, source.Altitude); } private const double DegreesToRadians = Math.PI/180.0; private const double RadiansToDegrees = 180.0/ Math.PI; private const double EarthRadius = 6378137.0; } 

    Применение:

     [TestClass] public class CalculateDerivedPositionUnitTest { [TestMethod] public void OneDegreeSquareAtEquator() { var center = new GeoCoordinate(0, 0); var radius = 111320; var southBound = center.CalculateDerivedPosition(radius, -180); var westBound = center.CalculateDerivedPosition(radius, -90); var eastBound = center.CalculateDerivedPosition(radius, 90); var northBound = center.CalculateDerivedPosition(radius, 0); Console.Write($"leftBottom: {southBound.Latitude} , {westBound.Longitude} rightTop: {northBound.Latitude} , {eastBound.Longitude}"); } } 

    Я не уверен, что здесь что-то не хватает, но я думаю, что этот вопрос можно перефразировать так: «У меня есть точка lat / lon, и я хочу найти точку x метров на север и x метров к югу от этой точки. ”

    Если это вопрос, тогда вам не нужно искать новую долготу (что упрощает работу), вам просто нужна новая широта. Степень широты составляет примерно 60 морских миль в длину на Земле, а морская миля – 1852 метра. Итак, для новых широт х метров север и юг:

     north_lat = lat + x / (1852 * 60) north_lat = min(north_lat, 90) south_lat = lat - x / (1852 * 60) south_lat = max(south_lat, -90) 

    Это не совсем точно, потому что Земля не является идеальной сферой с ровно 60 морских миль между каждой степенью широты. Однако другие ответы предполагают, что линии широты являются эквидистантными, поэтому я предполагаю, что вас это не волнует. Если вам интересно, сколько ошибок может возникнуть, в Википедии есть хорошая таблица, которая показывает «Расстояние по поверхности на 1 gradleус по широте» для разных широт по этой ссылке:

    http://en.wikipedia.org/wiki/Latitude#Degree_length

    Есть проблемы с двумя уравнениями на довольно удивительном сайте Эд Уильяма … но я не анализировал их, чтобы понять, почему.

    Третье уравнение, которое я нашел здесь, похоже, дает правильные результаты.

    Вот тестовый пример в php … третье уравнение верно, первые два дают дико неверные значения долготы.

     \n"; // doesn't work $lat2 = asin(sin($lat1) * cos($distance) + cos($lat1) * sin($distance) * cos($bearing) ); $dlon = atan2(sin($bearing) * sin($distance) * cos($lat1), cos($distance) - sin($lat1) * sin($lat2)); $lon2 = (($lon1 - $dlon + M_PI) % (2 * M_PI)) - M_PI; // normalise to -180...+180 echo "lon2: " . rad2deg($lon2) . " lat2: " . rad2deg($lat2) . "
    \n"; // same results as above $lat3 = asin( (sin($lat1) * cos($distance)) + (cos($lat1) * sin($distance) * cos($bearing))); $lon3 = (($lon1 - (asin(sin($bearing) * sin($distance) / cos($lat3))) + M_PI) % (2 * M_PI)) - M_PI; echo "lon3: " . rad2deg($lon3) . " lat3: " . rad2deg($lat3) . "
    \n"; // gives correct answer... go figure $lat4 = asin(sin($lat1) * cos($linDistance/6371) + cos($lat1) * sin($linDistance/6371) * cos($bearing) ); $lon4 = $lon1 + atan2( (sin($bearing) * sin($linDistance/6371) * cos($lat1) ), (cos($linDistance/6371) - sin($lat1) * sin($lat2))); echo "lon4: " . rad2deg($lon4) . " lat4: " . rad2deg($lat4) . "
    \n"; ?>

    Примечание. Я получил по электронной почте автора (Эд Уильямса) из первых двух уравнений:

    Из моих «заметок об осуществлении»:

    Обратите внимание на функцию мод. По-видимому, это реализуется по-разному на разных языках, с различными соглашениями о том, следует ли знак результата следовать знаку делителя или дивиденда. (Мы хотим, чтобы знак следовал за делителем или был евклидовым, а fmod C и Java% не работают.) В этом документе Mod (y, x) является остатком при делении y на x и всегда лежит в диапазоне 0 <= mod

    Если у вас есть функция floor (int в Excel), которая возвращает пол (x) = «наибольшее целое число, меньшее или равное x», например, floor (-2.3) = – 3 и floor (2.3) = 2

     mod(y,x) = y - x*floor(y/x) 

    Нижеследующее должно работать при отсутствии функции пола – независимо от того, уходит ли «int» или округляется вниз:

     mod=y - x * int(y/x) if ( mod < 0) mod = mod + x 

    php подобен fmod в C и делает это «неправильно» для моих целей.

    Это более точно, если вы сначала перепрограммируете его в UTM, а затем проверяете расстояние.

    Надеюсь это поможет

    Для чего это стоит, у меня есть пример в PHP, который может делать то, что запрашивает OP. В моем примере он рисует окно вокруг стартовой координаты lat / long, но код может быть легко использован для получения одной точки, X-километра или мили.

    http://www.richardpeacock.com/blog/2011/11/draw-box-around-coordinate-google-maps-based-miles-or-kilometers

    Давайте будем гением компьютера.