Wie finde ich die lat/lange, das ist x km nördlich von einem bestimmten lat/long?

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

  •  13-09-2019
  •  | 
  •  

Frage

Ich habe einige C# - code, der generiert google maps.Diese codes sieht überhaupt die Punkte, die ich noch Komplott auf die Karte und dann klappt der Grenzen eines Rechtecks um auch diese Punkte.Es geht dann diese Grenzen für die Google Maps-API, um die zoom-Stufe angemessen ist, um alle Punkte auf der Karte.

Dieser code ist in Ordnung, aber ich habe eine neue Anforderung.

Einer der Punkte, die kann haben eine Präzision zugeordnet.Wenn dies der Fall ist, dann ziehe ich einen Kreis um den Punkt mit dem radius einstellen, um den Wert für die Genauigkeit.Wieder dies funktioniert gut, aber mein bounds checking ist jetzt nicht tun, was ich will, es zu tun.Ich möchte die bounding-box umfasst den kompletten Kreis.

Dies erfordert einen Algorithmus, um zu nehmen einen Punkt x, und berechnen Sie den Punkt y das wäre z m nördlich von x und auch z m südlich von x.

Hat jemand diesen Algorithmus, vorzugsweise in C#.Ich habe einen generischen Algorithmus hier aber ich anscheinend noch nicht implementiert, dies korrekt als die Antworten, die ich erhalte, sind 1000 km adrift.

Dies ist das Allgemeine Beispiel

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

Und dies ist mein C# - übersetzung.

  // 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;
    }

Ich rufe diese Funktion mit einer Tragfähigkeit von 0 zu berechnen, den neuen nördlichen position und einen Wert von 180, - berechnen Sie die neue position Richtung Süden.

Kann jemand entweder sehen, was ich falsch gemacht habe oder vielleicht eine bekannte arbeitet Algorithmus?

War es hilfreich?

Lösung

Wenn Sie haben eine bestimmte Breite und Länge können Sie berechnen die richtige Breite und Länge eines x-km ändern in der Breite etwa so:

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.

Das gleiche kann gelten für die Länge.Wenn Sie die Gesamt-Distanz plus dem ändern, Sie kann berechnen die insgesamt Grad in einer ähnlichen Weise.

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.

Auch diese Berechnungen funktionieren sollte, aber ich bin aus reiner intuition hier, aber die Logik scheint zu halten gilt.

Edit:Wie bereits von Skizz 40,075 muss eingestellt werden, um den Umfang der Erde zu einem gegebenen Breitengrad mit 2.pi.r.cos(lat) oder 40074.cos(lat)

Andere Tipps

Ich habe ein sehr ähnliches Stück code.Es hat mich sehr enge Ergebnisse, wenn im Vergleich zu anderen Implementierung.

Ich denke, dass das problem mit Ihnen ist, dass Sie mit "Distanz" als linearer Abstand in Metern statt der Winkel in Bogenmaß.

/// <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);
}

Wo

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

und LatLonAlt in Grad/Meter (Umwandlung erfolgt intern).Justieren Sie, wie gebraucht.

Ich nehme an, Sie herausfinden können, was der Wert für UnitConstants.DegreesToRadians ist :)

Für faule Leute (wie mich ;) ) ein copy-paste-Lösung, Erich Mirabal version mit sehr geringfügigen änderungen:

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;
}

Verwendung:

[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}");
    }
}

Ich bin mir nicht sicher, ob ich hier etwas fehlt, aber ich denke, die Frage könnte umformuliert werden als: "ich habe ein lat/lon-Punkt, und ich möchte den Punkt x Meter nördlich und x Meter südlich von diesem Punkt."

Wenn das die Frage dann brauchen Sie nicht, um ein neues Länge (das macht die Sache einfacher), Sie müssen nur eine neue Breite.Ein Breitengrad ist etwa 60 nautische Meilen lang überall auf der Erde, und eine nautische Meile ist, zeigten 1.852 Meter.Also, für die neuen x Meter breiten Nord-und Süd:

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

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

Dies ist nicht ganz präzise, da die Erde keine perfekte Kugel mit genau 60 Seemeilen zwischen jeden Grad der Breite.Aber auch die anderen Antworten, die davon ausgehen, dass die Linien der Breitengrade gleich weit entfernt, also bin ich vorausgesetzt, Sie haben keine sorgen über, dass.Wenn Sie daran interessiert sind, wie viel Fehler führen könnten, gibt es eine schöne Tabelle auf Wikipedia zeigt, dass "Oberflächen-Distanz pro 1° change in latitude" für andere breiten auf diesen link:

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

Gibt es Probleme mit den beiden Gleichungen auf Ed William ist ziemlich ehrfürchtig Website...aber ich wusste nicht, analysieren Sie, um zu sehen, warum.

Eine Dritte Gleichung, die Ich fand hier zu geben scheint richtige Ergebnisse.

Hier ist der Testfall in php...die Dritte Gleichung ist richtig, die ersten beiden geben Wüst falsche Werte für die Länge.

<?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";
?>

Hinweis erhielt ich per E-Mail vom Autor (Ed Williams) von den beiden ersten Gleichungen:

Aus meiner "implementation notes":

Hinweis auf die mod-Funktion.Dies scheint anders implementiert in verschiedene Sprachen, mit unterschiedlichen Konventionen darüber, ob das Zeichen von der Ergebnis folgt das Zeichen der divisor oder der dividend.(Wollen wir das Schild Folgen der divisor oder der euklidischen.C fmod und Java % nicht Arbeit.) In diesem Dokument, Mod(y,x) ist der Rest bei Division von y nach x und immer liegt im Bereich von 0 <= mod < x.Zum Beispiel:mod(2.3,2.)=0,3 und mod(-2.3,2.)=1.7

Wenn Sie eine Funktion floor (int in Excel), zurückgibt, floor(x)= "größte ganze Zahl kleiner oder gleich x" z.B.Boden(-2.3)=-3 und Boden - (2.3) =2

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

Das folgende sollte Arbeit in die Abwesenheit der floor-Funktion - unabhängig davon, ob "int" schneidet oder Runden, nach unten:

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

php ist wie fmod C-und tut es "falsch" für meine Zwecke.

Es ist genauer, wenn Sie zuerst reproject es zu UTM und dann überprüfen Sie die Distanz.

Hoffe, das hilft

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top