ฉันมีจุดพิกัดกลางโลกที่กำหนดให้เป็นละติจูดและลองจิจูด ( WGS-84 )
ฉันจะแปลงเป็นพิกัดคาร์ทีเซียน (x, y, z) โดยมีจุดเริ่มต้นอยู่ที่ใจกลางโลกได้อย่างไร
ฉันมีจุดพิกัดกลางโลกที่กำหนดให้เป็นละติจูดและลองจิจูด ( WGS-84 )
ฉันจะแปลงเป็นพิกัดคาร์ทีเซียน (x, y, z) โดยมีจุดเริ่มต้นอยู่ที่ใจกลางโลกได้อย่างไร
คำตอบ:
เมื่อเร็ว ๆ นี้ฉันได้ทำสิ่งที่คล้ายกับสิ่งนี้โดยใช้ "Haversine Formula" บนข้อมูล WGS-84 ซึ่งเป็นอนุพันธ์ของ "Law of Haversines" ซึ่งได้ผลลัพธ์ที่น่าพอใจมาก
ใช่ WGS-84 ถือว่าโลกเป็นทรงรี แต่ฉันเชื่อว่าคุณจะได้รับข้อผิดพลาดประมาณ 0.5% โดยเฉลี่ยโดยใช้วิธีการเช่น "Haversine Formula" ซึ่งอาจเป็นจำนวนข้อผิดพลาดที่ยอมรับได้ในกรณีของคุณ คุณจะมีข้อผิดพลาดอยู่เสมอเว้นแต่คุณจะพูดถึงระยะทางไม่กี่ฟุตและถึงแม้จะมีความโค้งของโลกในทางทฤษฎี ... หากคุณต้องการวิธีที่เข้ากันได้กับ WGS-84 ที่เข้มงวดมากขึ้นให้ชำระเงิน "Vincenty Formula"
ฉันเข้าใจว่าstarblueมาจากไหน แต่วิศวกรรมซอฟต์แวร์ที่ดีมักจะเกี่ยวกับการแลกเปลี่ยนดังนั้นทุกอย่างขึ้นอยู่กับความแม่นยำที่คุณต้องการสำหรับสิ่งที่คุณกำลังทำ ตัวอย่างเช่นผลลัพธ์ที่คำนวณจาก "สูตรระยะทางแมนฮัตตัน" เทียบกับผลลัพธ์จาก "สูตรระยะทาง" อาจดีกว่าสำหรับบางสถานการณ์เนื่องจากมีค่าใช้จ่ายในการคำนวณน้อยกว่า คิดว่า "จุดไหนใกล้ที่สุด" สถานการณ์ที่คุณไม่ต้องการการวัดระยะทางที่แม่นยำ
เกี่ยวกับ "Haversine Formula" เป็นเรื่องง่ายที่จะนำไปใช้และเป็นสิ่งที่ดีเพราะใช้ "Spherical Trigonometry" แทนที่จะใช้วิธีการตาม "Law of Cosines" ซึ่งเป็นไปตามตรีโกณมิติสองมิติดังนั้นคุณจึงมีความสมดุลที่ดี มากกว่าความซับซ้อน
สุภาพบุรุษที่ใช้ชื่อว่า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 คือรัศมีโดยประมาณของโลก (เช่น 6371 กม.)
หากฟังก์ชันตรีโกณมิติของคุณคาดว่าจะเป็นเรเดียน (ซึ่งน่าจะทำได้) คุณจะต้องแปลงลองจิจูดและละติจูดเป็นเรเดียนก่อน เห็นได้ชัดว่าคุณต้องมีการแทนค่าทศนิยมไม่ใช่องศา \ นาที \ วินาที (ดูเช่นที่นี่เกี่ยวกับการแปลง)
สูตรสำหรับการแปลงกลับ:
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
โดยใช้geopotentialรุ่นEGM96
( Lemoine et al, 1998 )
ทำได้โดยการแก้ไขตารางของไฟล์ geoid height ด้วยความละเอียดเชิงพื้นที่ 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 # สำหรับหนึ่งมีNetTopologySuiteซึ่งเป็นพอร์ต. NET ของ JTS Topology Suite
คุณมีข้อบกพร่องอย่างรุนแรงในการคำนวณของคุณ โลกไม่ใช่ทรงกลมที่สมบูรณ์แบบและการประมาณรัศมีของโลกอาจไม่ได้ตัดมันเพื่อการวัดที่แม่นยำ
หากในบางกรณีสามารถใช้ฟังก์ชัน homebrew ได้ 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;
}