주어진 위도/경도에서 북쪽으로 xkm 떨어진 위도/경도를 어떻게 찾나요?

StackOverflow https://stackoverflow.com/questions/1125144

  •  13-09-2019
  •  | 
  •  

문제

Google 지도를 생성하는 C# 코드가 있습니다.이 코드는 지도에 플롯하는 데 필요한 모든 포인트를 살펴본 다음 해당 포인트를 포함하기 위해 직사각형의 경계를 계산합니다.그런 다음 이 경계를 Google Maps API에 전달하여 지도의 모든 지점을 표시하도록 확대/축소 수준을 적절하게 설정합니다.

이 코드는 잘 작동하지만 새로운 요구 사항이 있습니다.

점 중 하나에 연관된 정밀도가 있을 수 있습니다.이 경우에는 정밀도 값으로 설정된 반경을 사용하여 점 주위에 원을 그립니다.다시 말하지만 이것은 잘 작동하지만 내 경계 검사가 이제 내가 원하는 작업을 수행하지 않습니다.경계 상자에 완전한 원이 포함되도록 하고 싶습니다.

이를 위해서는 점 x를 가져와 x에서 북쪽으로 z미터, x에서 남쪽으로 z미터에 있는 점 y를 계산하는 알고리즘이 필요합니다.

C#에서 이 알고리즘을 사용하는 사람이 있습니까?일반적인 알고리즘을 찾았습니다 여기 하지만 내가 얻은 답변은 표류 1000km에 달하기 때문에 이것을 올바르게 구현하지 않은 것 같습니다.

이것은 일반적인 예입니다

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)를 사용하여 주어진 위도에서 지구의 둘레에 조정해야합니다.

다른 팁

나는 매우 비슷한 코드가 있습니다. 다른 구현과 비교할 때 결과가 매우 가까워졌습니다.

나는 당신의 문제는 당신이 라디안의 각도 거리 대신 미터의 선형 거리로 "거리"를 선형 거리로 사용하고 있다는 것입니다.

/// <summary>
/// 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.
/// </summary>
/// <param name="source">Point of origin</param>
/// <param name="range">Range in meters</param>
/// <param name="bearing">Bearing in degrees</param>
/// <returns>End-point from the source given the desired range and bearing.</returns>
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는도/미터입니다 (전환은 내부적으로 이루어집니다). 필요에 따라 조정하십시오.

나는 당신이 무엇의 가치를 알아낼 수 있다고 생각합니다 UnitConstants.DegreesToRadians 이다 :)

게으른 사람들의 경우 (나와 같은) 사본-페이스트 솔루션, Erich Mirabal의 버전은 매우 사소한 변경 사항입니다.

using System.Device.Location; // add reference to System.Device.dll
public static class GeoUtils
{
    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="source">Point of origin</param>
    /// <param name="range">Range in meters</param>
    /// <param name="bearing">Bearing in degrees</param>
    /// <returns>End-point from the source given the desired range and bearing.</returns>
    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}");
    }
}

여기서 뭔가 빠졌는지 잘 모르겠지만, 질문은 "위도/경도 지점이 있고 그 지점에서 북쪽으로 x미터, 남쪽으로 x미터 지점을 찾고 싶습니다."라고 바꿔 말할 수 있을 것 같습니다. "

이것이 문제라면 새로운 경도를 찾을 필요가 없고(이렇게 하면 일이 더 간단해집니다) 새로운 위도만 있으면 됩니다.위도는 지구상 어디에서나 대략 60해리이며, 1해리는 1,852미터입니다.따라서 새로운 위도 x 미터 북쪽과 남쪽의 경우:

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° 변화당 표면 거리"를 보여주는 Wikipedia의 멋진 표를 참조하세요.

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

두 방정식에 문제가 있습니다 에드 윌리엄의 멋진 사이트... 그러나 나는 이유를 알기 위해 그들을 분석하지 않았습니다.

세 번째 방정식 나는 여기서 찾았다 적절한 결과를 제공하는 것 같습니다.

PHP의 테스트 사례는 다음과 같습니다. 세 번째 방정식은 정확하고 처음 두 개는 경도에 대해 매우 잘못된 값을 제공합니다.

<?php
            $lon1 = -108.553412; $lat1 = 35.467155; $linDistance = .5; $bearing = 170;
            $lon1 = deg2rad($lon1); $lat1 = deg2rad($lat1);
            $distance = $linDistance/6371;  // convert dist to angular distance in radians
            $bearing = deg2rad($bearing);

            echo "lon1: " . rad2deg($lon1) . " lat1: " . rad2deg($lat1) . "<br>\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) . "<br>\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) . "<br>\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) . "<br>\n";
?>

참고 나는 처음 두 방정식의 저자 (Ed Williams)의 이메일로 다시 보았습니다.

내 "구현 노트"에서 :

MOD 기능에 주목하십시오. 이는 결과의 부호가 디바이저의 부호를 따르는지 여부에 대한 관습이 다른 언어로 다르게 구현되는 것으로 보입니다. (우리는 부호가 divisor 또는 euclidean이되기를 원합니다. C의 fmod와 Java의 %는 작동하지 않습니다.)이 문서에서는 mod (y, x)가 y를 x로 나누는 나머지는 항상 0 <= 범위에 있습니다. 모드 <x. 예를 들어 : mod (2.3,2.) = 0.3 및 mod (-2.3,2.) = 1.7

바닥 함수 (int in Excel)가있는 경우 바닥 (x) = "가장 큰 정수 또는 x"보다 작거나 동일하게 반환됩니다.

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

다음은 바닥 기능이 없을 때 작동해야합니다.

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

PHP는 C의 FMOD와 같으며 내 목적을 위해 "잘못"을합니다.

먼저 다시 재판매하면 더 정확합니다. UTM 그런 다음 거리를 확인하십시오.

도움이 되었기를 바랍니다

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top