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