回答:
私は最近、WGS-84データの「Haversine式」を使用してこれと同様のことを行いました。これは、「Haversinesの法則」の派生物であり、非常に満足のいく結果が得られます。
はい、WGS-84は地球が楕円体であることを前提としていますが、「Haversine式」のようなアプローチを使用した場合、平均誤差は約0.5%しか得られないと思われます。数フィートの距離について話していなくても、理論的には地球の曲率がある場合を除いて、常にある程度のエラーが発生します。より厳密なWGS-84互換のアプローチが必要な場合は、「Vincenty Formula」を確認してください。
starblueの出所を理解していますが、優れたソフトウェアエンジニアリングは多くの場合、トレードオフに関係しているため、すべては、実行する作業に必要な精度に依存します。たとえば、「マンハッタン距離式」から計算された結果と「距離式」から計算された結果は、計算コストが低いため、特定の状況ではより良い場合があります。「どの点が最も近いか」を考えてください。正確な距離測定を必要としないシナリオ。
二次元三角法に基づく「コサインの法則」ベースのアプローチの代わりに「球面三角法」を使用しているため、実装が簡単であり、「Haversine Formula」が優れているため、精度のバランスが取れています。複雑さ以上。
Chris Venessという名前の紳士は、http://www.movable-type.co.uk/scripts/latlong.htmlに、興味のあるいくつかの概念を説明し、さまざまなプログラムによる実装を示す優れたWebサイトを公開しています。これはx / y変換の質問にも答えるはずです。
これが私が見つけた答えです:
定義を完全にするために、デカルト座標系で:
変換は次のとおりです。
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
ここで、Rは地球のおおよその半径です(例:6371 km)。
三角関数がラジアンを期待している場合(おそらくそうです)、まず経度と緯度をラジアンに変換する必要があります。明らかに、度\分\秒ではなく、小数表現が必要です(たとえば、変換についてはこちらを参照してください)。
逆変換の式:
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
高さは、高さに変換されなければならないh
上記のWGS 84 ellipsoid
使用によってジオポテンシャルモデルEGM96
(ルモワーヌら、1998)。
これは、15分の弧の空間分解能でジオイド高さファイルのグリッドを補間することによって行われます。
または、あるレベルの専門家 GPS
が高度H
(msl、平均海面より上の高さ)を持ちUNDULATION
、内部テーブルから選択されたデータム出力のgeoid
との関係を持っている場合。得られるellipsoid (m)
h = H(msl) + undulation
デカルト座標によるXYZへ:
x = R * cos(lat) * cos(lon)
y = R * cos(lat) * sin(lon)
z = R *sin(lat)
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
球ではなく楕円体に基づいて座標を取得する場合は、http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEFを参照してください。変換に必要な式とWGS84定数が表示されます。
そこでの公式は、参照楕円面に対する高度も考慮に入れます(GPSデバイスから高度データを取得している場合に役立ちます)。
なぜすでに実装され、テスト済みの何かを実装するのですか?
C#には、JTSトポロジスイートの.NETポートであるNetTopologySuiteがあります。
具体的には、計算に重大な欠陥があります。地球は完全な球体ではありません。正確な測定のために、地球の半径の概算では地球をカットできない場合があります。
自作の関数を使用することが許容される場合は、GISは、信頼性が高く、テストで実証されたライブラリを使用することが非常に好ましいフィールドの良い例です。
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;
}