¿Cómo puedo encontrar la latitud / longitud que es x km al norte de una determinada latitud / longitud?

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

  •  13-09-2019
  •  | 
  •  

Pregunta

Tengo algo de código C # que genera los mapas de Google. Con este código se ve en todos los puntos que necesito para trazar en el mapa y luego se resuelve los límites de un rectángulo para incluir esos puntos. A continuación, pasa este límite a la API de Google Maps para ajustar el nivel de zoom adecuada para mostrar todos los puntos en el mapa.

Este código está trabajando bien sin embargo tengo un nuevo requisito.

Uno de los puntos pueden tener una precisión asociada a ella. Si este es el caso, entonces Dibujo un círculo alrededor del punto con el radio establecido en el valor de precisión. De nuevo, esto funciona bien sin embargo mi comprobación de límites está ahora no está haciendo lo que yo quiero que haga. Quiero tener el cuadro delimitador incluye el círculo completo.

Esto requiere un algoritmo para tomar un punto x y calcular el punto y que sería z metros al norte de x metros y también z sur de x.

¿Alguien tiene este algoritmo, preferentemente en C #. Lo que encontrar un algoritmo genérico aquí pero aparento no han implementado esta correctamente como las respuestas que estoy recibiendo son 1000 de km a la deriva.

Este es el ejemplo Genérico

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

Y este es mi C # traducción.

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

Estoy llamando a esta función con un cojinete de 0 a calcular la nueva posición del norte y un valor de 180 para calcular la nueva posición meridional.

¿Alguien puede ver bien lo que he hecho mal o quizás proporcionar un algoritmo de trabajo conocido?

¿Fue útil?

Solución

Si usted tiene una determinada latitud y longitud se puede calcular la latitud y la longitud correcta de un cambio x-km en la latitud de este 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 mismo puede aplicarse a la longitud. Si usted tiene la distancia total más el cambio se puede calcular los grados totales de una manera similar.

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.

Una vez más, estos cálculos deben trabajar, pero estoy corriendo pura intuición aquí, pero la lógica parece ser cierto.

Edit: Como se ha señalado por Skizz 40.075 necesita ser ajustada a la circunferencia de la tierra en cualquier latitud dada usando 2.pi.r.cos (LAT) o 40074.cos (lat)

Otros consejos

Tengo una pieza muy similar de código. Se me consiguió resultados muy cercanos cuando se compara con otra aplicación.

Creo que el problema es con el suyo que está utilizando la "distancia" como la distancia lineal en metros en lugar de la distancia angular en radianes.

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

Donde

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

y LatLonAlt está en grados / m (conversión se lleva a cabo internamente). Ajuste según sea necesario.

Asumo que usted puede averiguar cuál es el valor de UnitConstants.DegreesToRadians es:)

Para los perezosos, (como yo;)) una solución de copiar y pegar, la versión de Erich Mirabal con muy pequeños cambios:

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

Uso:

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

No estoy seguro de si estoy perdiendo algo, pero creo que la pregunta podría ser reformulado como: "Tengo un / lon punto de latitud, y yo quiero encontrar los puntos x metros norte y en metros al sur de ese punto ".

Si esa es la pregunta, entonces no es necesario encontrar una nueva longitud (que hace las cosas más simples), sólo se necesita un nuevo latitud. Un grado de latitud es aproximadamente 60 millas náuticas de largo en cualquier lugar de la Tierra, y una milla náutica es 1.852 metros. Por lo tanto, para las nuevas latitudes x metros de norte a sur:

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

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

Esto no es del todo exacto, porque la Tierra no es una esfera perfecta, con exactamente 60 millas náuticas entre cada grado de latitud. Sin embargo, las otras respuestas asumen que las líneas de latitud son equidistantes, así que estoy asumiendo que no se preocupa por eso. Si usted está interesado en la cantidad de errores que podrían introducir, hay un bonito cuadro de la Wikipedia que muestra "la distancia de superficie por cada 1 ° cambio de latitud" para diferentes latitudes en este enlace:

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

Hay problemas con las dos ecuaciones en el sitio en lugar de impresionante Ed William ... pero no me analizarlos para ver por qué.

Una tercera ecuación que encontré aquí parece dar resultados adecuados .

Este es el caso de la prueba en php ... la tercera ecuación es correcta, los dos primeros dan valores incorrectos violentamente para la longitud.

<?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: He recibido por correo electrónico del autor (Ed Williams) de las dos primeras ecuaciones:

  

A partir de mis notas de implementación "":

     

Nota sobre la función mod. Esto parece ser implementado de manera diferente en   diferentes idiomas, con las convenciones diferentes sobre si el signo de la   resultado se sigue el signo del divisor o el dividendo. (Queremos el signo   para seguir el divisor o ser euclidiana. HOQF de C y% de Java no funcionan.)   En este documento, Mod (y, x) es el resto en la división de y por x y siempre   se encuentra en el rango 0 <= mod      

Si usted tiene una función piso (int en Excel), que devuelve floor (x) =   "Mayor entero menor o igual a x", por ejemplo, piso (-2,3) = - 3 y   piso (2,3) = 2

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

El siguiente debe funcionar en ausencia de un piso Función- independientemente de   si "int" trunca o rondas hacia abajo:

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

php es como HOQF en C y lo hace "mal" para mis propósitos.

Es más exacto si primero se reproject UTM y luego comprobar la distancia.

Espero que esto ayude

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top