Come faccio a trovare la latitudine / longitudine cioè x km a nord di un dato lat / lungo?

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

  •  13-09-2019
  •  | 
  •  

Domanda

Ho qualche codice C # che genera le mappe di Google. Questi codici esamina tutti i punti di cui ho bisogno per tracciare sulla mappa e poi elabora i limiti di un rettangolo di includere tali punti. Si passa poi questi limiti per l'API di Google Maps per impostare il livello di zoom in modo appropriato per mostrare tutti i punti sulla mappa.

Questo codice sta lavorando bene però ho un nuovo requisito.

Uno dei punti possono avere una precisione ad esso associati. Se questo è il caso allora mi disegnare un cerchio intorno al punto con il raggio impostato il valore di precisione. Ancora una volta questo funziona bene ma il mio controllo dei limiti è ora non sta facendo quello che voglio fare. Voglio avere il rettangolo di selezione include il cerchio completo.

Ciò richiede un algoritmo per prendere un punto x e calcolare il punto y che sarebbe z metri a nord di x ed anche z metri a sud di x.

Qualcuno ha questo algoritmo, preferibilmente in C #. Ho trovato un algoritmo generico qui ma mi sembra di non aver implementato questo correttamente come le risposte che che ottiene sono 1000s di km alla deriva.

Questo è l'esempio generico

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

E questo è il mio C # traduzione.

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

io chiamo questa funzione con un cuscinetto da 0 a calcolare la nuova posizione a nord e un valore di 180 per calcolare la nuova posizione meridionale.

chiunque sia possibile vedere ciò che ho fatto male o forse fornire un algoritmo funzionante?

È stato utile?

Soluzione

Se si dispone di una certa latitudine e longitudine si può calcolare la latitudine e la longitudine corretta di un cambiamento x-km in latitudine in questo modo:

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.

Lo stesso si applica alla longitudine. Se avete la distanza totale, più il cambiamento è possibile calcolare i gradi totali in un modo simile.

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.

Ancora una volta, questi calcoli dovrebbero funzionare, ma sto correndo fuori pura intuizione qui, ma la logica sembra tenere vero.

Edit: Come indicato da Skizz 40,075 deve essere adeguato alla circonferenza della terra in qualsiasi latitudine usando 2.pi.r.cos (LAT) o 40074.cos (LAT)

Altri suggerimenti

Ho un pezzo molto simile di codice. E mi ha fatto risultati molto vicini rispetto a un'altra implementazione.

Credo che il problema con il vostro è che si sta utilizzando "distanza", come la distanza lineare in metri anziché distanza angolare in radianti.

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

Dove

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

e LatLonAlt è in gradi / m (conversione avviene internamente). Regolare, se necessario.

presumo si può capire quale sia il valore per UnitConstants.DegreesToRadians è:)

Per i più pigri, (come me;)) una soluzione di copia-incolla, la versione di Erich Mirabal, con leggerissime modifiche:

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

Utilizzo:

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

Non sono sicuro se mi manca qualcosa qui, ma penso che la questione potrebbe essere riformulato come, "Ho un lat / lon punto, e voglio trovare il punto x metri a nord e X metri a sud della quel punto ".

Se questo è il problema, allora non c'è bisogno di trovare un nuovo longitudine (che rende le cose più semplici), hai solo bisogno di una nuova latitudine. Un grado di latitudine è circa 60 miglia nautiche a lungo ovunque sulla Terra, e un miglio nautico è 1.852 metri. Così, per le nuove latitudini x metri a nord ea sud:

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

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

Questo non è del tutto preciso, perché la Terra non è una sfera perfetta con esattamente 60 miglia nautiche tra ogni grado di latitudine. Tuttavia, le altre risposte per scontato che le linee di latitudine sono equidistanti, quindi sto supponendo che non si cura di questo. Se siete interessati in quanto errore che potrebbe introdurre, c'è un bel tavolo su Wikipedia che mostra "distanza di superficie per 1 ° cambio di latitudine" per le diverse latitudini a questo link:

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

Non ci sono problemi con le due equazioni in loco piuttosto impressionante di Ed William ... ma non ho analizzarli per capire perché.

Una terza equazione che ho trovato qui sembra dare risultati corretti .

Ecco il caso di test in php ... terza equazione è corretta, i primi due valori invia selvaggiamente non corretti per longitudine.

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

Nota ho ricevuto per e-mail da parte dell'autore (Ed Williams) delle prime due equazioni:

  

Dalle mie "Note di implementazione":

     

Nota sulla funzione mod. Questo sembra essere attuate in modo diverso in   lingue diverse, con convenzioni diverse sulla questione se il segno della   risultato segue il segno del divisore e dividendo. (Vogliamo che il segno   a seguire il divisore o essere euclidea. fmod di C e% di Java non funzionano.)   In questo documento, Mod (y, x) è il resto sul dividere y da x e sempre   si trova nell'intervallo 0 <= mod      

Se si dispone di una funzione di piano (int in Excel), che restituisce floor (x) =   "Maggior numero intero minore o uguale a x" es piano (-2.3) = - 3 e   piano (2.3) = 2

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

Il seguente dovrebbe funzionare in assenza di un pavimento FUNCTION- indipendentemente   se "Int" tronca o giri verso il basso:

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

php è come fmod in C e lo fa "sbagliato" per i miei scopi.

E 'più preciso se prima riproiettare a UTM e quindi controllare la distanza.

Spero che questo aiuti

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top