巧妙解決百度地圖加偏糾偏問題


搞GPS相關開發的,無一例外都會碰到坐標糾偏問題,即接收到的GPS坐標,如果直接顯示到電子地圖上,和實際位置有較大差距,必須在GPS坐標的基礎上加上一個偏移量,才能正確顯示,由於偏移是非線性的,而算法又是保密的,故很難獲得算法,網上通用的做法就是使用偏移數據庫,原理是某一個區域的偏移是差不多的,那么,按0.01度或0.001度等方式,把中國地圖分割成很多區域,GPS坐標落在哪個區域,就取出這個區域偏移值,和GPS坐標相加就可以得到地圖坐標。中國很大,數據庫也很龐大,網上很少有完整的,即使有,也需要付錢,百度地圖提供了糾偏接口,傳入GPS經緯度,返回地圖經緯度,但通過網頁接口訪問,顯然無法滿足大規模並發處理,故使用本地偏移庫相對靠譜一些。中國大陸(含海南島)的經緯度大概范圍在經度73.5~135度和緯度18~53.6之間,如果按1度一條記錄,那么記錄數為(53.6-18)*(135-73.5)=2189.4條,顯然,1度的跨度太大了,如果0.1度,則需要2189.4*100(10的平方)約22萬條記錄,如果是0.01度,則需要2189.4萬條,已經很龐大了,再精確到0.001,那么,就是21.894億條了,根據實測,這么高精度沒必要,精確到0.01度足矣,也就是說,中國大陸1/100度的偏移庫有2189.4萬條記錄,還是比較大的,我們的車輛並不是在中國大陸任何地方都可以開的,比如車輛是不會開到居民樓頂的,我們實際用到的並不多,你可以參照我的另外一篇博客(點擊打開鏈接)構建一個精簡的數據庫,這里,我們使用百度地圖接口,自己創建一個完整的偏移數據庫。結果如下(經緯度、偏移均使用百萬分之一度表示):

 

我們先看看百度糾偏接口:

http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x=120.434966&y=28.160923

x代表經度,y代表緯度,網站返回如下信息:

{"error":0,"x":"MTIwLjQ0NTg3NzczOTc1","y":"MjguMTYzNjM2MzM0NjU5"}
 

這是一個JSON的數據結構,error=0表示解析成功,x代表糾偏后的經度,y代表糾偏后的緯度,經緯度采用了base64加密,還原后就可以正常取得了,注意,該接口每次返回的結果可能都不一樣,也就是我們永遠都是接近真實值,但永遠都無法得到真實值。

有了這個接口,我們可以通過二次循環,經度從73.5到135循環,每次增加0.01度,緯度亦然,這樣就可以自動生成自己的偏移庫了。

我們知道,整數的運輸速度遠遠快於浮點數,因此,我們參照部標GPS規定,用百萬分之一度來描述經緯度,也就是把經緯度都乘以一百萬保存到數據庫里面。顯然,數據庫表至少應該有經度(int)、緯度(int),經度偏移(smallint)、緯度偏移(smallint)幾列,由於網絡的不穩定性,並不能保證一次性每條都解析成功,因此,還需要增加一列,當解析失敗時,做個標記,以便下次重新解析。這里給出我用C#寫的調用百度接口進行解析的代碼供參考,參數均是百萬分之一度:

 

#region 查詢百度糾偏坐標

        ///<summary>

        ///計算百度地圖坐標

        ///</summary>

        ///<paramname="Lng">原始經度</param>

        ///<paramname="Lat">原始緯度</param>

        ///<returns>百度地圖糾偏后的坐標</returns>

        public Point GetBaiduPosOff(intLng, int Lat)

        {

            Pointpos = new Point(0,0);

                     //還原到度供接口使用

            doublelng = 0.000001 * Lng, lat = 0.000001 * Lat;

            try

            {

                stringurl = string.Format("http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x={0}&y={1}",

                    lng, lat);

                HttpWebRequestrequest = (HttpWebRequest)WebRequest.Create(url);

                request.Timeout = 5000;

                HttpWebResponseresponse = (HttpWebResponse)request.GetResponse();

                Streamstream = response.GetResponseStream();

                byte[]bytes = new byte[1024];

                intn = stream.Read(bytes, 0, 1024);

                response.Close();

                if(n < 2)

                {

                    ShowLog(string.Format("返回長度不正確經度:{0},緯3度:{1}", lng, lat));

                }

                else

                {

                    strings = System.Text.Encoding.UTF8.GetString(bytes).Substring(1,n - 2).Replace("\"","");

                    foreach(string teamins.Split(','))

                    {

                        string[] infos = team.Split(':');

                        if (infos.Length < 2)

                        {

                            ShowLog(string.Format("格式不正確,經度:{0},緯度:{1}", lng, lat));

                            break;

                        }

                        string strValue = infos[1];

                        switch (infos[0])

                        {

                            case "error":

                                if (strValue !="0")

                                    ShowLog(string.Format("返回了錯誤號,經度:{0},緯度:{1},錯誤號:{2}", lng, lat,strValue));

                                break;

                            case "x":

                                {

                                    byte[] outputb =Convert.FromBase64String(strValue);

                                    strValue = Encoding.Default.GetString(outputb);

                                    pos.X = (int)(double.Parse(strValue)* 1000000);

                                }

                                break;

                            case"y":

                                {

                                    byte[] outputb =Convert.FromBase64String(strValue);

                                    strValue = Encoding.Default.GetString(outputb);

                                    pos.Y = (int)(double.Parse(strValue) * 1000000);

                                }

                                break;

                        }

                    }

 

                }

            }

            catch(Exception ee)

            {

                ShowLog(string.Format("錯誤,經度:{0},緯3度:{1},錯誤信息:{2}", lng, lat,ee.Message));

            }

 

            returnpos;

        }

        #endregion

 

傳入經緯度(百萬分之一度),得到一個Point數據結構,x代表糾偏后的經度(百萬分之一度),y代表緯度,如果x或y為0,表示解析失敗,需要記錄下來下次重新解析。用糾偏后的坐標減去原始坐標,就得到了偏移值,存到數據庫就可以了。

例如:

            intnLng = 85000000, nLat = 28000000,nLngOffset=0,nLatOffset=0;

            Pointpt = GetBaiduPosOff(nLng, nLat);

            boolbSuccess = false;

            if(pt.X > 0 && pt.Y > 0)

            {

                nLngOffset = pt.X - nLng;

                nLatOffset = pt.Y - nLat;

                bSuccess = true;

            }

這樣,我們就可以把原始坐標、偏移值、是否成功存到數據庫了。

有了這個數據庫,使用相對比較簡單,首先將GPS坐標(百萬分之一度)轉換到0.01度的經度(GPS坐標/10000*10000),並從數據庫取得偏移值,然后將原始GPS和坐標偏移相加即可,參考代碼如下:

            int nLng = 85123456,nLat = 28123456;

//從數據庫讀取偏移

            Pointpt = GetDBOff(nLng / 10000 * 10000, nLat / 10000 * 10000);

            //加上偏移

            nLng += pt.X;

            nLat += nLat;

應該說,整個原理非常簡單,真正的困難在於時間,實測了一下,一秒鍾大約能解析4~5條,兩千多萬條記錄大概需要兩個多月連續工作,恐怕大多數人都是等不及的,要解決也很簡單,多線程+多電腦操作,每個線程負責一段,我調用電腦資源比較方便,用了6台服務器,每台開兩個程序,一個星期不到全部解析完畢。如果有需要的朋友,我上傳到了CSDN下載中心了,具體鏈接等出來后,我在評論里給出,我自己使用的是sql server 2008 R2,考慮到很多人還是sql server 2005,故我備份為sql server2005的格式,如果你還在使用sql server 2000,那沒辦法了,只能找一台2005或2008的數據庫還原,然后通過鏈接服務器插入到你的數據庫了,別忘了經度、緯度一定要聚族索引,或者干脆去掉id,直接經度和緯度一起作為主鍵。
該方法可能影響了部分人的財路,在此,我表示誠摯的歉意。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM