Como faço para encontrar o tempo que é x km ao norte lat / de uma determinada latitude / longitude?

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

  •  13-09-2019
  •  | 
  •  

Pergunta

Eu tenho algum código C # que gera o Google Maps. Este códigos aparência em todos os pontos que eu preciso trama no mapa e, em seguida, trabalha para fora dos limites de um retângulo para incluir esses pontos. Em seguida, passa esta limites para a API do Google Maps para definir o nível de zoom de forma adequada para mostrar todos os pontos no mapa.

Este código está funcionando bem no entanto eu tenho um novo requisito.

Um dos pontos pode ter uma precisão associado a ele. Se este for o caso, então eu desenhar um círculo em torno do ponto com o conjunto de raio para o valor de precisão. Mais uma vez isso funciona bem no entanto os meus limites verificando agora não está fazendo o que eu quero que ele faça. Quero ter a caixa delimitadora incluem o círculo completo.

Isto requer um algoritmo para tomar um ponto x e calcular o ponto y que seria z metros ao norte de x e também metros z sul de x.

Alguém tem esse algoritmo, de preferência em C #. Eu fiz encontrar um algoritmo genérico aqui mas parecem não ter implementado este corretamente como as respostas que eu estou recebendo são 1000s de km à deriva.

Este é o exemplo 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

E este é o meu C # tradução.

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

Eu estou chamando essa função com um rolamento de 0 para calcular a nova posição norte e um valor de 180 para calcular a nova posição sul.

Can ninguém quer ver o que eu tenho feito de errado ou talvez fornecer um algoritmo que esteja funcionando?

Foi útil?

Solução

Se você tem uma determinada latitude e longitude, você pode calcular a latitude e longitude correta de uma mudança x-km na latitude assim:

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.

O mesmo pode se aplicar a longitude. Se você tiver a distância total mais a variação você pode calcular os graus totais de forma semelhante.

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.

Mais uma vez, estes cálculos devem trabalhar, mas eu estou correndo intuição pura aqui, mas a lógica não parece ser verdade.

Editar: Como foi assinalado por SKIZZ 40,075 precisa de ser ajustado para a circunferência da terra em qualquer dado latitude usando 2.pi.r.cos (LAT) ou 40074.cos (lat)

Outras dicas

Eu tenho um pedaço muito semelhante de código. Isso me resultados muito próximos quando comparado com outra implementação.

Eu acho que o problema com o seu é que você está usando "distância" como a distância linear em metros em vez de distância angular em radianos.

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

Onde

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

e LatLonAlt está em graus / metros (conversão ocorre internamente). Ajuste conforme necessário.

Eu suponho que você pode descobrir o que o valor para UnitConstants.DegreesToRadians é:)

Para pessoas preguiçosas, (como eu;)) uma solução copy-paste, a versão de Erich Mirabal com pequenas alterações:

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

Eu não tenho certeza se eu estou faltando alguma coisa aqui, mas eu acho que a pergunta poderia ser reformulada como, "Eu tenho um lat / lon ponto, e eu quero encontrar o ponto x metros ao norte e x metros ao sul da esse ponto ".

Se essa é a pergunta, então você não precisa encontrar um novo longitude (que torna as coisas mais simples), você só precisa de uma nova latitude. Um grau de latitude é de aproximadamente 60 milhas náuticas longo em qualquer lugar na Terra, e uma milha náutica é 1.852 metros. Então, para novas latitudes x metros ao norte e ao sul:

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

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

Isto não é totalmente preciso porque a Terra não é uma esfera perfeita com exatamente 60 milhas náuticas entre cada grau de latitude. No entanto, as outras respostas assumir que as linhas de latitude estão equidistantes, então eu estou supondo que você não se preocupam com isso. Se você está interessado em quanto de erro que poderia introduzir, há uma mesa agradável na Wikipedia que mostra "a distância da superfície por 1 ° mudança na latitude" para diferentes latitudes neste link:

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

Existem problemas com as duas equações na de Ed William em vez incrível local ... mas eu não analisá-los para ver o porquê.

A terceira equação que eu encontrei aqui parece dar resultados apropriados .

Aqui é o caso de teste em php ... a terceira equação estiver correta, os dois primeiros dar valores descontroladamente incorretas para a longitude.

<?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 eu recebi por e-mail do autor (Ed Williams) das duas primeiras equações:

De minha as "notas de implementação":

Nota sobre a função mod. Esta parece ser implementado de forma diferente em línguas diferentes, com diferentes convenções sobre se o sinal da resultado segue o sinal do divisor ou o dividendo. (Queremos o sinal a seguir o divisor ou ser euclidiana. fmod de C e% de Java não funcionam.) Neste documento, Mod (y, x) é o resto da divisão em y por x e sempre situa-se no intervalo de 0 <= mod

Se você tem uma função andar (int em Excel), que piso retornos (x) = "O maior número inteiro menor do que ou igual a x" v.g. chão (-2,3) = - 3 e chão (2,3) = 2

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

A seguir deve funcionar na ausência de um piso Função- independentemente do se "int" trunca ou rodadas para baixo:

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

php é como fmod em C e faz isso "errado" para os meus propósitos.

É mais exato se você primeiro reprojetar para UTM e, em seguida, verificar a distância.

Espero que isso ajude

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top