1. 程式人生 > >中國地圖座標(GCJ-02)偏移演算法破解小史

中國地圖座標(GCJ-02)偏移演算法破解小史

2006年,Google開始與AutoNavi合作使用後者所提供的中國地圖。這應該是外企首次接觸到這個問題。

從2009年開始,中國地圖的座標偏移開始為外界所知。Garmin的使用者發現在美國購買的GPS到了中國幾乎無法使用,而在中國購買的Garmin產品則沒有問題。Google Maps API的使用者發現興趣點無法被準確標註在中國地圖上。更有意思的是,有使用者反覆就此報告bug給Google,卻從未得到任何迴應。類似的,Garmin也聲稱自己沒有解決方案,建議客戶在需要的情況下在中國境內購買GPS裝置。

於此同時,各路豪傑開始嘗試破解這種偏移演算法。其中有兩條路徑值得注意:

2010年1月,網友wuyongzheng發現:

I accidentally found the Chinese version of Google Mapditu.google.com to be able to correlate satellite image with map, and it gives the amount of deviation for any location in China. This URL queries the deviation of 34.29273N,108.94695E (Xi’an):http://ditu.google.com/maps/vp?spn=0.001,0.001&t=h&z=18&vp=$34.29273,108.94695

(seems it’ doesn’t work now)

在此之前的嘗試都是零星的,針對個別城市的。wuongzheng的這個建議算是在全面系統地解決這個問題上邁出了第一步。

2013年5月,Maxime Guilbot根據這個建議得到4-5米精度的逼近:

2013年10月,wuyongzheng自己進行了迴歸,得到如下結果:

Maxime Guibot和wuyongzheng的迴歸結果基本代表了在黑暗中摸索的最佳結果,因此得到了廣泛的注意和應用。

在另一條路徑上,2010年4月,emq project增加了一個檔案,Converter.java:

這段程式碼可以以很高的精度把WGS-84座標轉換到GCJ-02座標。

2013年2月,這段程式碼被網友coolypf注意到,整理後用到了他自己的專案中:

其中的關鍵程式碼值得貼在這裡:

        const double pi = 3.14159265358979324;

        //
        // Krasovsky 1940
        //
        // a = 6378245.0, 1/f = 298.3
        // b = a * (1 - f)
        // ee = (a^2 - b^2) / a^2;
        const double a = 6378245.0;
        const double ee = 0.00669342162296594323;

        //
        // World Geodetic System ==> Mars Geodetic System
        public static void transform(double wgLat, double wgLon, out double mgLat, out double mgLon)
        {
            if (outOfChina(wgLat, wgLon))
            {
                mgLat = wgLat;
                mgLon = wgLon;
                return;
            }
            double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
            double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
            double radLat = wgLat / 180.0 * pi;
            double magic = Math.Sin(radLat);
            magic = 1 - ee * magic * magic;
            double sqrtMagic = Math.Sqrt(magic);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * pi);
            mgLat = wgLat + dLat;
            mgLon = wgLon + dLon;
        }

2013年3月,coolypf在自己的部落格中介紹了這一段程式碼:

2014年9月,wuyongzheng注意到了coolypf的專案。至此,兩條路徑合流,座標偏移問題基本得到了完美解決。

從上面的程式碼可以看出,相對於WGS-84,GCJ-02一方面採用了不同的參考橢球體(SK-42, Krasovsky。應該屬於前蘇聯影響的遺留),另一方面引入了高頻非線性偏移。

https://www.zhihu.com/question/19806163