巧妙解决百度地图加偏纠偏问题

丽水市汽车运输集团股份有限公司信息中心苟安廷

 

当我们兴匆匆地把GPS设备传来的经纬度坐标标记到电子地图上时,发现地图上的位置和实际位置相差甚远,这就是天朝上国特有的地图加偏,以安全之名,行掩耳盗铃之事,给我们的开发带来了不必要的麻烦,至于加偏的原因,大家百度一下,网上一大堆,这里就不废话了。除GPS设备本身加偏外,电子地图上的坐标也不是真实的,要想正确显示到地图上,还必须将收到的加偏坐标(俗称火星坐标)换算到地图对应的坐标,也就是我们常说的纠偏,而国内用的比较多的地图主要是谷歌和百度,前者有具体的控件,比较好解决,后者就麻烦了,本人这段时间为此事折腾得不轻,但功夫不费有心人,总算比较完美地解决了,故整理一下,给还在或将要受此折腾的人参考。

和大多数人一样,碰到这个问题时,首先想到了百度,总结了百度结果,主要有以下三个方法:

1.使用控件

如果和谷歌地图一样,有一个控件,直接传入GPS坐标得到地图坐标,那就省事了,通过正常途径获取控件的话,我们这种小公司还没有那个实力,而非正常途经的控件或算法貌似还没有泄漏出来,即使泄漏了,你也不敢放心使用,万一有一天,被以泄漏“郭嘉咪咪”的名义请你去喝茶就得不偿失了,因此,本方法基本放弃。

2.百度地图接口

百度网站提供了接口,只要通过http传入GPS坐标参数就可以获得对应的地图坐标,该方法优点是方便准确,不足也很明显,受网速、百度服务器等影响,处理大量并发业务时力不从心。

3.数据库

所谓的加偏,就是将真实坐标加上一定的偏移量,而这个偏移量又不是线性的,不同地区偏移不一样,但同一地区偏移量却差不多,因此,有人就使用了个暴力破解的方法,将全国按GPS坐标分成很多小块,然后查出每个小块的偏移量,并保存到数据库里面,需要纠偏时,先根据GPS坐标取出对应区域的偏移量,反算出地图坐标。优点:本地执行,速度快,缺点:中国太大了,存放区域的记录有几千万条,不仅占用了大量存储空间,检索速度也大受影响,更要命的是,网上也很难找到一个完整的数据库,有些网站说有完整的,但要Money,提到Money就不亲热了,毕竟我们是个人或小公司嘛,钱不是问题,问题是没钱。

貌似进入了一个死胡同,但我们仔细一想,数据库方法不错,但有点浪费,因为我们面向的客户大多是某区域的,而且,车辆行驶的路线相对是固定的(尤其是客运班车),塔克拉玛干沙漠或者居民小区楼顶的坐标对于我们来说,貌似没有多少意义,理论上,我们需要的仅仅是客户车辆行驶区域的坐标就可以了,车辆能到达的区域(通常是公路)相对整个国土面积来说,太小了,因此,有这么一个轻量级的数据库是不是就完美了呢?本文正是基于这一思路,将方法2和方法3结合起来,巧妙解决了百度地图纠偏问题。

基本原理:收到GPS坐标后,首先计算出该坐标所属的区域,然后再从本地数据库查询该区域的偏移量,如果查询到,直接和该偏移相加得到地图坐标,如果没有查询到,则从百度网站接口查询,并和GPS坐标相减得到偏移,将本偏移存放到本地数据库,然后直接返回从百度接口得到的地图坐标。换句话说,我们自己根据车辆使用过的坐标,构建了一个轻量级的数据库,使用一段时间后,我们会发现,绝大部分数据都是从本地数据库获取的,数据量也就区区几十万条而已。

我们分步骤完成这一思路。

 

第一步:划分区域的算法

GPS收到的坐标(经度和纬度两个方向)是百万分之一度,整数,如果每百万分之一度作为一个区域,那么,基本上所有的GPS坐标都位于不同的区域了,这么小的区域没有什么意义。我们知道,一度等于60分,一分等于60秒,那么,一度就是3600秒了,而经纬度中,一秒对应的距离大约31米,如果我们按秒划分区域,应该是可以接受的,也就是说,用大约31米边长的正方形表示一个区域,落在该区域的坐标我们认为偏移量是一样的。因此,我们首先要把GPS坐标转换一下,用一个精确到“秒”的数字来标记所属的区域。假如某经度为120.123456度,我们将小数部分转换成“秒”:0.123456×3600=444.4416,精确到秒,就是444秒,后面的小数部分扔掉了,因此,该GPS经度位于经度为120444秒的某区域中,该区域的纬度算法一样。在数据库检索中,整数速度远远大于浮点数,我们想办法将120444秒用一个整数特征值来表示,我们知道,GPS返回的坐标为百万分之一度,因此,我们将“度”乘以一百万,再把秒加上去就可以表示了,120.123456度转换成区域经度的数字为120000444,这个数字看起来怪怪的,前面部分表示度,后面部分表示秒,其实也无所谓,反正就是一个区域的唯一标记的特征值而已。为此,我们写一个方法来计算(注意,参数都是百万分之一度)。

参考代码如下:

publicint GetAreaPostion(int GpsCoordinate)

       {

           //计算""的部分

           int nDegree = GpsCoordinate / 1000000 * 1000000;

           //计算度后面小数部分

           int nSecond = (int)(0.000001 * (GpsCoordinate - nDegree) * 3600);

           //两者重新相加

           return nDegree + nSecond;

       }

 

第二步:创建数据库表

我们需要在数据库里面创建一个表,记录每个区域的偏移量,一个区域包含精确到秒的经度和纬度两列,是唯一的,因此,为直观,我们不妨用中文的“秒经度”、“秒纬度”两列表示,并设置为主键、聚族索引,再增加“经度偏移”、“纬度偏移”、“创建时间”三列,之所以要增加“创建时间”字段,是考虑到时间久了,最好更新一下,比如,半年前的数据最好重新从百度网站取,考虑到速度问题,该字段也需要创建索引。数据库设计界面如下:

第三步:将接收到的GPS坐标转换成对应的区域

坐标包含经度、纬度两部分,因此,我们用Point表示,转换办法参考如下:

            MapCorrect map=newMapCorrect();

            Point ptArea = new Point(0, 0);

            ptArea.X = map.GetAreaPostion(Lng);

            ptArea.Y = map.GetAreaPostion(Lat);

 

第四步:创建数据库访问的方法

既然要访问数据库,必然要提供操作数据库的方法,数据库操作不是本文的重点,大家也都会操作,这里我们创建一个名称为DB的类用于操作数据库,该类主要提供两个静态方法:

public staticDataRow GetValueDataRow(string p_strSql)

该方法执行传入的SQL语句,并将数据库返回的数据集第一行返回,如果数据库没有数据返回,该方法返回null

public staticbool ExecSql(string p_strSql)

该方法执行指定的SQL语句,如果执行错误,返回false,执行成功,返回true

 

第五步:从数据库查询偏移

为了加快速度和代码更清晰,建议使用存储过程,存储过程代码如下(忽略半年前的数据)

CREATE PROCEDUREGetOffset

    @Lng INT,  --精确到秒的经度

    @Lat INT   --精确到秒的纬度

AS

BEGIN

    SET NOCOUNT ON;

    SELECT 经度偏移,纬度偏移

    FROM dbo.纠偏区域 WITH(NOLOCK)

    WHERE 秒经度=@Lng AND 秒纬度=@LatAND创建时间>DATEADD(dd,-180,GETDATE())

END

 

第六步:根据查询结果进行处理

如果查询结果中已经有偏移了,直接将偏移和GPS坐标相加,否则,从百度网站查询,并计算偏移然后保存到数据库。

public Point GetMapCoordinate(Point GpsCoordinate)

        {

            string strSql = string.Format("EXEC GetOffset @Lng ={0},@Lat={1}",

                GetAreaPostion(GpsCoordinate.X),

                GetAreaPostion(GpsCoordinate.Y));

            DataRow row = DB.GetValueDataRow(strSql);

 

            Point ptMap = new Point(0, 0);

          

            if (row == null) //从百度网站查询

            {

                ptMap = GetFromBaidu(GpsCoordinate);

            }

            else//直接返回

            {

                ptMap.X = GpsCoordinate.X + (int)row["经度偏移"];

                ptMap.Y = GpsCoordinate.Y + (int)row["纬度偏移"];

            }

            return ptMap;

        }

这里,我们需要完成从百度查询坐标的方法,百度网站返回的是JSON数据,JSON很强大,操作方法很多,但这里得到的结果却是简单的一句话:

{"error":0,"x":"MTE5LjE1NzE5NzEwMDc5","y":"MjguMzI0NzUzMTI4NDEz"}

显然,error表示错误信息,0表是无错误,后面的两个是地图坐标的经度和纬度,进行了Base64编码,格式是固定的,为了使用这么简单的一个JSON而引入其他项目显然没必要,我们直接用简单的字符串分解一下就可以了,代码较多,这里就不列出源代码了(文末会附下载地址),得到地图坐标后,一定要计算偏移,并更新到数据库中,更新数据库的存储过程参考如下:

CREATE PROCEDUREUpdateOffset

    @Lng INT,     --区域秒经度

    @Lat INT,     --区域秒纬度

    @OffLng INT,  --经度偏移

    @OffLat INT       --纬度偏移

AS

BEGIN

 

    SET NOCOUNT ON;

    UPDATE dbo.纠偏区域 SET 经度偏移=@OffLng,纬度偏移=@OffLat,创建时间=GETDATE()

    WHERE 秒经度=@Lng AND 秒纬度=@OffLat

    IF @@ROWCOUNT=0

    BEGIN

       INSERT INTO dbo.纠偏区域(秒经度,秒纬度,经度偏移,纬度偏移,创建时间)

       VALUES (@Lng,@Lat,@OffLng,@OffLat,GETDATE())

    END

 

END

使用存储过程,不仅提高了速度,减少了网络流量,还使客户端代码更加清晰简洁。以上仅仅是实现的基本原理,实际使用时,应该考虑多线程、异常处理等等实际情况。

题外话:

对于一个GPS软件,除了能在地图上正确显示车辆位置外,还应该显示当前位置对应的中文描述,如“浙江省杭州市沪杭高速”,也就是根据坐标查询对应的地理位置信息描述,即反向地理编码查询,百度地图提供了相应的接口(接口地址:http://developer.baidu.com/map/carapi-2.htm),我们可以用上面同样的办法,在本地建立区域缓存,避免每次都从网站读取,另外,谷歌地图提供的查询更详细和准确,二者均有使用限制,百度每个密钥每天限制5000次查询,而注册一个账号,可以申请最多20个密钥,账号注册是免费的,其余你懂的,后者按IP限制,每个IP每天最多查询2500次,而GPS服务器往往都是固定IP,对于业务初期,2500次杯水车薪。为解决这一问题,我们可以在缓存百度地理位置信息时,将GPS原始坐标也保存起来,然后另外开一个程序,通过从谷歌查询地理位置信息来对缓存中的位置信息描述进行更新,也就是说,下次再使用这一区域的地理信息名称时,得到的就是谷歌解析的名称了,如何突破谷歌的限制呢?你可以把软件COPY到一个拨号上网的地方(如家里),超过限制后,重新拨号,当然,你可以手工断线和重拨,也可以写一段代码实现自动断线和重拨,等过一段时间,客户车辆大部分位置信息都缓存到本地后,就可以直接运行在服务器上了。

附源代码下载地址(演示时,为方便使用,可以输入120.123456,也可以输入120123456):

 

百度地图纠偏数据库是一个基于Oracle的地理位置纠偏服务。地图纠偏是指将GPS定位数据进行偏移矫正,使其能准确地反映在真实地图上的位置。 Oracle是一种广泛使用的关系数据库管理系统,具备强大的数据存储和处理能力。百度地图纠偏数据库采用Oracle作为后台数据库,能够高效地处理大规模的位置数据。 百度地图纠偏数据库利用Oracle的空间数据存储和分析功能,对GPS定位数据进行纠正和处理。数据库中存储了包括道路网络、地理边界、道路属性和地理特征等数据,这些数据可以帮助确定GPS定位坐标的准确位置。 通过对GPS定位数据的纠偏处理,百度地图纠偏数据库能够提供准确的地理位置服务。用户只需将GPS定位数据传入数据库,系统会自动根据存储的道路网络和地理特征数据对位置进行纠偏并返回准确的位置信息。 此外,百度地图纠偏数据库还提供了丰富的地图展示和导航功能,用户可以通过地图界面查看地理位置、搜索目的地、规划路线等。数据库中的道路网络和地理边界数据为这些功能提供了支持。 总而言之,百度地图纠偏数据库是基于Oracle的地理位置纠偏服务,利用Oracle的功能提供高效准确的地理位置服务,并支持地图展示和导航功能。它对GPS定位数据进行纠偏处理,使其能够准确地反映在真实地图上的位置。
评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值