Eu tenho alguns pontos de coordenadas centrados na Terra dados como latitude e longitude ( WGS-84 ).
Como posso convertê-los em coordenadas cartesianas (x, y, z) com a origem no centro da Terra?
Eu tenho alguns pontos de coordenadas centrados na Terra dados como latitude e longitude ( WGS-84 ).
Como posso convertê-los em coordenadas cartesianas (x, y, z) com a origem no centro da Terra?
Respostas:
Recentemente, fiz algo semelhante a isso usando a "Fórmula Haversine" nos dados WGS-84, que é um derivado da "Lei de Haversine" com resultados muito satisfatórios.
Sim, o WGS-84 assume que a Terra é um elipsóide, mas acredito que você obtenha apenas cerca de 0,5% de erro médio usando uma abordagem como a "Fórmula Haversine", que pode ser uma quantidade aceitável de erro no seu caso. Você sempre terá algum erro, a menos que esteja falando sobre uma distância de alguns metros e mesmo assim, teoricamente, há uma curvatura da Terra ... Se você precisar de uma abordagem compatível com WGS-84 mais rígida, verifique a "Fórmula de Vincenty".
Eu entendo de onde vem o starblue , mas uma boa engenharia de software geralmente trata de compensações, então tudo depende da precisão que você precisa para o que está fazendo. Por exemplo, o resultado calculado da "Fórmula da distância de Manhattan" em comparação com o resultado da "Fórmula da distância" pode ser melhor para certas situações, pois é computacionalmente mais barato. Pense "qual é o ponto mais próximo?" cenários onde você não precisa de uma medição de distância precisa.
Em relação à "Fórmula Haversine", é fácil de implementar e é agradável porque usa "Trigonometria Esférica" em vez de uma abordagem baseada na "Lei dos Cossenos" que é baseada na trigonometria bidimensional, portanto, você obtém um bom equilíbrio de precisão sobre a complexidade.
Um cavalheiro chamado Chris Veness tem um ótimo site em http://www.movable-type.co.uk/scripts/latlong.html que explica alguns dos conceitos nos quais você está interessado e demonstra várias implementações programáticas; isso deve responder também à sua pergunta de conversão x / y.
Aqui está a resposta que encontrei:
Só para completar a definição, no sistema de coordenadas cartesianas:
A conversão é:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
Onde R está o raio aproximado da terra (por exemplo, 6371 km).
Se suas funções trigonométricas esperam radianos (o que provavelmente acontece), você precisará primeiro converter sua longitude e latitude em radianos. Você obviamente precisa de uma representação decimal, não graus \ minutos \ segundos (veja por exemplo, aqui sobre conversão).
A fórmula para conversão reversa:
lat = asin(z / R)
lon = atan2(y, x)
asin é, naturalmente, arco seno. leia sobre atan2 na wikipedia . Não se esqueça de converter de volta de radianos para graus.
Esta página fornece o código c # para isso (observe que é muito diferente das fórmulas), e também algumas explicações e um belo diagrama de por que isso está correto,
Teoria para converter GPS(WGS84)
em coordenadas cartesianas
https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
O seguinte é o que estou usando:
Anexei um código VB que escrevi:
Imports System.Math
'Input GPSLatitude is WGS84 Latitude,h is altitude above the WGS 84 ellipsoid
Public Function GetSphericalLatitude(ByVal GPSLatitude As Double, ByVal h As Double) As Double
Dim A As Double = 6378137 'semi-major axis
Dim f As Double = 1 / 298.257223563 '1/f Reciprocal of flattening
Dim e2 As Double = f * (2 - f)
Dim Rc As Double = A / (Sqrt(1 - e2 * (Sin(GPSLatitude * PI / 180) ^ 2)))
Dim p As Double = (Rc + h) * Cos(GPSLatitude * PI / 180)
Dim z As Double = (Rc * (1 - e2) + h) * Sin(GPSLatitude * PI / 180)
Dim r As Double = Sqrt(p ^ 2 + z ^ 2)
Dim SphericalLatitude As Double = Asin(z / r) * 180 / PI
Return SphericalLatitude
End Function
Observe que a h
altitude está acima de WGS 84 ellipsoid
.
Normalmente GPS
nos dará altura H
acima MSL
. A MSL
altura deve ser convertida para a altura h
acima do WGS 84 ellipsoid
usando o modelo geopotencialEGM96
( Lemoine et al, 1998 ).
Isso é feito interpolando uma grade do arquivo de altura do geóide com uma resolução espacial de 15 minutos de arco.
Ou se você tiver algum nível profissional GPS
tem Altitude H
( msl, altura acima do nível médio do mar ) e UNDULATION
, a relação entre o geoid
e o ellipsoid (m)
da saída do datum escolhido da tabela interna. você pode terh = H(msl) + undulation
Para XYZ por coordenadas cartesianas:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
O software proj.4 fornece um programa de linha de comando que pode fazer a conversão, por exemplo
LAT=40
LON=-110
echo $LON $LAT | cs2cs +proj=latlong +datum=WGS84 +to +proj=geocent +datum=WGS84
Ele também fornece um API C . Em particular, a função pj_geodetic_to_geocentric
fará a conversão sem ter que configurar um objeto de projeção primeiro.
Em python3.x, isso pode ser feito usando:
# Converting lat/long to cartesian
import numpy as np
def get_cartesian(lat=None,lon=None):
lat, lon = np.deg2rad(lat), np.deg2rad(lon)
R = 6371 # radius of the earth
x = R * np.cos(lat) * np.cos(lon)
y = R * np.cos(lat) * np.sin(lon)
z = R *np.sin(lat)
return x,y,z
Se você se preocupa em obter coordenadas com base em um elipsóide em vez de uma esfera, dê uma olhada em http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF - ele fornece as fórmulas, bem como as constantes WGS84 de que você precisa para a conversão .
As fórmulas ali também levam em consideração a altitude relativa à superfície do elipsóide de referência (útil se você estiver obtendo dados de altitude de um dispositivo GPS).
Por que implementar algo que já foi implementado e testado?
C #, por exemplo, tem o NetTopologySuite, que é a porta .NET do JTS Topology Suite.
Especificamente, você tem uma falha grave em seu cálculo. A Terra não é uma esfera perfeita, e a aproximação do raio da Terra pode não cortá-la para medições precisas.
Se em alguns casos for aceitável usar funções homebrew, o GIS é um bom exemplo de um campo no qual é preferível usar uma biblioteca confiável e comprovada por teste.
Coordinate[] coordinates = new Coordinate[3];
coordinates[0] = new Coordinate(102, 26);
coordinates[1] = new Coordinate(103, 25.12);
coordinates[2] = new Coordinate(104, 16.11);
CoordinateSequence coordinateSequence = new CoordinateArraySequence(coordinates);
Geometry geo = new LineString(coordinateSequence, geometryFactory);
CoordinateReferenceSystem wgs84 = DefaultGeographicCRS.WGS84;
CoordinateReferenceSystem cartesinaCrs = DefaultGeocentricCRS.CARTESIAN;
MathTransform mathTransform = CRS.findMathTransform(wgs84, cartesinaCrs, true);
Geometry geo1 = JTS.transform(geo, mathTransform);
java.lang.IllegalArgumentException: dimension must be <= 3
Você pode fazer isso em Java.
public List<Double> convertGpsToECEF(double lat, double longi, float alt) {
double a=6378.1;
double b=6356.8;
double N;
double e= 1-(Math.pow(b, 2)/Math.pow(a, 2));
N= a/(Math.sqrt(1.0-(e*Math.pow(Math.sin(Math.toRadians(lat)), 2))));
double cosLatRad=Math.cos(Math.toRadians(lat));
double cosLongiRad=Math.cos(Math.toRadians(longi));
double sinLatRad=Math.sin(Math.toRadians(lat));
double sinLongiRad=Math.sin(Math.toRadians(longi));
double x =(N+0.001*alt)*cosLatRad*cosLongiRad;
double y =(N+0.001*alt)*cosLatRad*sinLongiRad;
double z =((Math.pow(b, 2)/Math.pow(a, 2))*N+0.001*alt)*sinLatRad;
List<Double> ecef= new ArrayList<>();
ecef.add(x);
ecef.add(y);
ecef.add(z);
return ecef;
}