緯度と経度( WGS-84 )として指定された地球中心の座標点があります。
原点を地球の中心にしてデカルト座標(x、y、z)に変換するにはどうすればよいですか?
最近、WGS-84データで「Haversine Formula」を使用してこれと同様のことを行いました。
はい、WGS-84は地球が楕円体であると想定していますが、「Haversine Formula」のようなアプローチを使用した場合、平均誤差は約0.5%しか得られないと考えています。数フィートの距離について話している場合を除き、常にある程度のエラーが発生します。それでも、理論的には地球の曲率があります。
私はstarblueがどこから来たのか理解していますが、優れたソフトウェアエンジニアリングは多くの場合トレードオフに関するものです。 。たとえば、「マンハッタン距離式」から計算された結果と「距離式」からの結果は、計算的に安価であるため、特定の状況でより良い場合があります。 「どの点が最も近いか」を考えてください。正確な距離測定が必要ないシナリオ。
に関しては、「Haversine Formula」は実装が簡単で、2次元三角法に基づいた「Law of Cosines」ベースのアプローチの代わりに「Spherical Trigonometry」を使用しているため、ナイスです。複雑すぎる。
Chris Venessという名前の紳士は http:/ /www.movable-type.co.uk/scripts/latlong.html これは、興味のあるいくつかの概念を説明し、さまざまなプログラムによる実装を示しています。これは、x/y変換の質問にも答えるはずです。
私が見つけた答えは次のとおりです。
定義を完成させるために、デカルト座標系で:
変換は次のとおりです。
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
Rは 地球のおおよその半径 (例:6371KM)です。
三角関数がラジアンを期待する場合(おそらくそうです)、まず経度と緯度をラジアンに変換する必要があります。もちろん、度\分\秒ではなく小数表現が必要です( e.g。here 変換についてを参照)。
逆変換の式:
lat = asin(z / R)
lon = atan2(y, x)
asinはもちろんアークサインです。 ウィキペディアでatan2について読む 。ラジアンから度に戻すことを忘れないでください。
このページ は、このためのC#コード(式とは非常に異なることに注意してください)、およびこれが正しい理由の説明とニース図を示します。
GPS(WGS84)
をデカルト座標に変換するための理論https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
以下は私が使用しているものです:
私はVBコードを添付しました
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
h
は、WGS 84 ellipsoid
よりも高度が高いことに注意してください。
通常、GPS
は、上記のH
の高さのMSL
を示します。 MSL
の高さは、geopotentialモデルWGS 84 ellipsoid
(Lemoine et al、1998)を使用して、EGM96
より上の高さh
に変換する必要があります。
これは、15アーク分の空間解像度でジオイド高さファイルのグリッドを補間することによって行われます。
または、あるレベルprofessionalGPS
に高度があるH
(msl、heigh上記の平均海面レベル)とUNDULATION
の関係geoid
および内部テーブルから選択されたdatum出力のellipsoid (m)
h = H(msl) + undulation
を取得できます
デカルト座標によるXYZへ:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
すでに実装され、テストで実証されたものを実装するのはなぜですか?
たとえば、C#には NetTopologySuite があり、これはJTS Topology Suiteの.NETポートです。
具体的には、計算に重大な欠陥があります。地球は完全な球体ではなく、地球の半径の近似は正確な測定のためにそれを切断しない場合があります。
自作の関数を使用することが許容される場合、GISは、信頼性の高い、実証済みのライブラリを使用することが好まれている分野の良い例です。
球体ではなく楕円体に基づいて座標を取得する場合は、 http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF をご覧ください。式と同様に、変換に必要なWGS84定数。
そこにある数式は、参照楕円体表面に対する高度も考慮しています(GPSデバイスから高度データを取得している場合に便利です)。
Python3.xでは、以下を使用して実行できます。
# 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
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でこれを行うことができます。
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;
}