丽水市汽车运输集团股份有限公司信息中心 苟安廷
搞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 team ins.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;