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 haltitude está acima de WGS 84 ellipsoid.
Normalmente GPSnos dará altura Hacima MSL. A MSLaltura deve ser convertida para a altura hacima do WGS 84 ellipsoidusando 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 geoide 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_geocentricfará 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;
}