1. 程式人生 > >QT:GPS座標變換

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;
        }