QT:GPS座標變換
座標系簡介
WGS-84:是國際標準,GPS座標(Google Earth使用、或者GPS模組)
GCJ-02:中國座標偏移標準,Google
Map、高德、騰訊使用 BD-09:百度座標偏移標準,Baidu Map使用
(1)視覺化 GPS 路徑
剛拿到一堆GPS軌跡資料,想看看它長什麼樣?於是先想辦法把它們可視化出來。有很多地圖的API可以用,如果不是想搞演示,只是為了快速隨便看一眼的話,推薦用百度的線上示例API http://developer.baidu.com/map/jsdemo.htm#c1_3 裡面有比較詳細的例子,很豐富的操作,修改程式碼也比較簡單。 當然也可以把原始碼copy下來在本地操作。
(2) 座標格式
一般從GPS得到的資料是經緯度。經緯度有多種表示方法。
1.) ddd.ddddd, 度 . 度的十進位制小數部分(5位)例如:31.12035º
2.) ddd.mm.mmm,度 . 分 . 分的十進位制小數部分(3位)例如 31º10.335'
3.) ddd.mm.ss, 度 . 分 . 秒 例如 31º12'42"
在應用程式中基本都用格式1),所以首先要確定你的原始資料格式是否是格式1),如果不是,需要轉換,方法比較簡單。 一個從2)轉換成1)的程式如下:
public Point(double longitude, double latitude ) { int degree = (int)(longitude); this.longitude = (longitude - degree) * 100 / 60 + degree; degree = (int)(latitude); this.latitude = (latitude - degree) *100 / 60 + degree; }
(3)座標糾偏
剛拿到GPS軌跡資料的時候,直接print到地圖上,發現路線比較奇怪,沒有和道路完全吻合,很像偏移了一些,甚至會穿越沒有道路的地方。 起初以為是GPS精度的問題。後來經人提醒,GPS座標是有好幾套標準的。 有興趣的同學可以閱讀更多資料例如 http://yanue.net/post-121.html , 這裡簡單的說明什麼是座標偏移:
天朝為了保衛國家安全,對真實座標系統進行人為的加偏處理,把真實的座標加密成虛假的座標,稱為火星座標。這種偏移是非線性的,偏移方向也不確定。 除了火星座標系統,不同的地圖資料商也可能使用自己的座標系統,例如百度。 國內各地圖API座標系統比較詳情請見http://www.cnblogs.com/Tangf/archive/2012/03/15/2398397.html 。 所以天朝所有準許上市的地圖類產品都不是真實座標系統。要想在某個地圖API上正確的顯示GPS軌跡,必須要先把資料轉換成它的座標系統。
這裡還是以百度地圖為例。假如我有原始(真實的)GPS資料,要在百度地圖上正確顯示,就需要先變成百度座標系。 座標轉換的API說明在 http://lbsyun.baidu.com/index.php?title=webapi/guide/changeposition 可以看到,這個API支援從很多種不同的座標系統轉換到百度座標系(但是不支援轉換到百度座標系以外的模式,吐槽一下)。 自己寫個程式批量地轉換,速度有點慢,一次只能發10個點左右。 線上演示在http://developer.baidu.com/map/jsdemo.htm#c1_3
把其中的座標改成你自己的資料,再找到這行程式碼:
convertor.translate(pointArr, 3, 5, translateCallback)
其中的3,5表示從火星座標轉換到百度座標; 如果改成1 , 5 則是從原始座標轉換到百度座標,點選執行。
在我自己的資料上的效果如下:
很明顯經過處理之後,路徑才是正確的。
另外,高德地圖採用的是火星座標,也可以玩玩 http://lbs.amap.com/api/javascript-api/example/marker/marker-content/
原始座標和火星座標是可以相互轉換的,需要一個小型輔助資料庫,這裡就不貼程式碼了,如有需要請留言。
這裡附上百度座標和火星座標的c#轉換程式碼:
public static Point Baidu2Mars(Point a)
{
Point b = new Point();
double x = a.longitude - 0.0065;
double y= a.latitude - 0.006;
double z = Math.Sqrt(x * x + y * y) - 0.00002 * Math.Sin(y * Math.PI / 180);
double theta = Math.Atan2(y, x) - 0.000003 * Math.Cos(x * Math.PI / 180);
b.longitude = z * Math.Cos(theta);
b.latitude = z * Math.Sin(theta);
return b;
}
public static Point Mars2Baidu(Point a){
Point b = new Point();
double x = a.longitude;
double y= a.latitude;
double z = Math.Sqrt(x * x + y * y) + 0.00002 * Math.Sin(y * Math.PI / 180);
double theta = Math.Atan2(y, x) + 0.000003 * Math.Cos(x * Math.PI / 180);
b.longitude = z * Math.Cos(theta) + 0.0065;
b.latitude = z * Math.Sin(theta) + 0.006;
return b;
}
最後順便貼一個求兩個GPS點間距離的程式碼:
public static double Degree2Radian(double degree)
{
double radian = degree * Math.PI / 180;
return radian;
}
//http://www.movable-type.co.uk/scripts/latlong.html
public static double Distance(Point a, Point b)
{
double R = 6371000;
double phi1 = Degree2Radian(a.latitude);
double phi2 = Degree2Radian(b.latitude);
double deltaPhi = Degree2Radian(a.latitude - b.latitude);
double deltaLam = Degree2Radian(a.longitude - b.longitude);
double aa = Math.Sin(deltaPhi / 2) * Math.Sin(deltaPhi / 2) + Math.Cos(phi1) * Math.Cos(phi2) * Math.Sin(deltaLam / 2) * Math.Sin(deltaLam / 2);
double c = 2 * Math.Atan2(Math.Sqrt(aa), Math.Sqrt(1 - aa));
double d = R * c;
return d;
}