Ho alcuni punti di coordinate centrati sulla terra dati come latitudine e longitudine ( WGS-84 ).
Come posso convertirli in coordinate cartesiane (x, y, z) con l'origine al centro della terra?
Ho alcuni punti di coordinate centrati sulla terra dati come latitudine e longitudine ( WGS-84 ).
Come posso convertirli in coordinate cartesiane (x, y, z) con l'origine al centro della terra?
Risposte:
Recentemente ho fatto qualcosa di simile a questo utilizzando la "Formula Haversine" sui dati WGS-84, che è un derivato della "Legge di Haversines" con risultati molto soddisfacenti.
Sì, WGS-84 presume che la Terra sia un ellissoide, ma credo che si ottenga solo un errore medio dello 0,5% circa usando un approccio come la "Formula di Haversine", che potrebbe essere una quantità di errore accettabile nel tuo caso. Avrai sempre una certa quantità di errore a meno che tu non stia parlando di una distanza di pochi piedi e anche allora teoricamente c'è una curvatura della Terra ... Se hai bisogno di un approccio più rigido compatibile con WGS-84 controlla la "Formula Vincenty".
Capisco da dove viene starblue , ma una buona ingegneria del software è spesso una questione di compromessi, quindi tutto dipende dalla precisione richiesta per ciò che stai facendo. Ad esempio, il risultato calcolato dalla "Formula della distanza di Manhattan" rispetto al risultato della "Formula della distanza" può essere migliore per determinate situazioni in quanto è meno costoso dal punto di vista del calcolo. Pensa "qual è il punto più vicino?" scenari in cui non è necessaria una misurazione precisa della distanza.
Per quanto riguarda la "Formula Haversine" è facile da implementare ed è piacevole perché utilizza la "Trigonometria Sferica" invece di un approccio basato sulla "Legge dei Coseni" che si basa sulla trigonometria bidimensionale, quindi si ottiene un buon equilibrio di accuratezza oltre la complessità.
Un gentiluomo di nome Chris Veness ha un ottimo sito web all'indirizzo http://www.mohable-type.co.uk/scripts/latlong.html che spiega alcuni dei concetti che ti interessano e mostra varie implementazioni programmatiche; questo dovrebbe rispondere anche alla tua domanda di conversione x / y.
Ecco la risposta che ho trovato:
Giusto per completare la definizione, nel sistema di coordinate cartesiane:
La conversione è:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
Dove R è il raggio approssimativo della terra (ad esempio 6371 km).
Se le tue funzioni trigonometriche si aspettano i radianti (cosa che probabilmente fanno), dovrai prima convertire la tua longitudine e latitudine in radianti. Ovviamente hai bisogno di una rappresentazione decimale, non di gradi \ minuti \ secondi (vedi ad esempio qui sulla conversione).
La formula per la conversione all'indietro:
lat = asin(z / R)
lon = atan2(y, x)
asin è ovviamente arco seno. leggi di atan2 su wikipedia . Non dimenticare di riconvertire da radianti a gradi.
Questa pagina fornisce il codice c # per questo (nota che è molto diverso dalle formule), e anche alcune spiegazioni e un bel diagramma del perché questo è corretto,
Teoria per la conversione GPS(WGS84)
in coordinate cartesiane
https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
Quanto segue è quello che sto usando:
Ho allegato un codice VB che ho scritto:
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
Si prega di notare che l' h
altitudine è superiore a WGS 84 ellipsoid
.
Di solito GPS
ci darà un'altezza H
superiore MSL
. L' MSL
altezza deve essere convertita in altezza h
sopra il WGS 84 ellipsoid
utilizzando il modello geopotenzialeEGM96
( Lemoine et al, 1998 ).
Ciò viene fatto interpolando una griglia del file di altezza del geoide con una risoluzione spaziale di 15 minuti d'arco.
Oppure se hai qualche livello professionale GPS
ha Altitudine H
( msl, altezza sopra il livello medio del mare ) e UNDULATION
, il rapporto tra il geoid
e il ellipsoid (m)
del dato scelto in uscita dalla tabella interna. Puoi prendereh = H(msl) + undulation
A XYZ per coordinate cartesiane:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
Il software proj.4 fornisce un programma a riga di comando che può eseguire la conversione, ad es
LAT=40
LON=-110
echo $LON $LAT | cs2cs +proj=latlong +datum=WGS84 +to +proj=geocent +datum=WGS84
Esso fornisce anche un'API C . In particolare, la funzione pj_geodetic_to_geocentric
eseguirà la conversione senza dover prima impostare un oggetto di proiezione.
In python3.x può essere fatto 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 ti interessa ottenere coordinate basate su un ellissoide piuttosto che su una sfera, dai un'occhiata a http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF - fornisce le formule e le costanti WGS84 necessarie per la conversione .
Le formule tengono anche conto dell'altitudine relativa alla superficie dell'ellissoide di riferimento (utile se si ottengono dati di altitudine da un dispositivo GPS).
Perché implementare qualcosa che è già stato implementato e testato?
C #, per esempio, ha NetTopologySuite che è il port .NET di JTS Topology Suite.
Nello specifico, hai un grave difetto nel tuo calcolo. La terra non è una sfera perfetta e l' approssimazione del raggio terrestre potrebbe non tagliarla per misurazioni precise.
Se in alcuni casi è accettabile utilizzare le funzioni homebrew, GIS è un buon esempio di un campo in cui è di gran lunga preferito utilizzare una libreria affidabile e collaudata.
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
Puoi farlo in questo modo su 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;
}