zoukankan      html  css  js  c++  java
  • 百度地图经纬度纠偏

    我们把从GPS设备接收的经纬度坐标标记到电子地图上时,会发现地图上的位置和实际位置相差甚远,除GPS设备本身加偏外,电子地图上的坐标也不是真实的,要想正确显示到地图上,还必须将收到的加偏坐标(俗称火星坐标)换算到地图对应的坐标,也就是我们常说的纠偏。

    1.使用控件

    如果和谷歌地图一样,有一个控件,直接传入GPS坐标得到地图坐标,那就省事了,通过正常途径获取控件的话,但是不是任何人都可以获取控件。因此,本方法基本放弃。

    2.百度地图接口

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

    API地址

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

    参数解释

    from=0:代表传入的是真实经纬度

    to=4:代表返回是百度纠偏后,能在百度地图上正确显示出该地址的经纬度

    x:经度          

    y:纬度

    返回数据

    {"error":0,"x":"MTEzLjI1MjIyMjUxOTg1","y":"MjMuODIwNjM5MTEyNDgy"}

    返回的数据经过Base64加密,在网上找个在线Base64解密的网站就可以了。

    3.本地数据库

    所谓的加偏,就是将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万条记录,还是比较大的,我们的车辆并不是在中国大陆任何地方都可以开的,比如车辆是不会开到居民楼顶的,我们实际用到的并不多,你可以参照我的另外一篇博客(点击打开链接)构建一个精简的数据库,这里,我们使用百度地图接口,自己创建一个完整的偏移数据库。结果如下(经纬度、偏移均使用百万分之一度表示):


    我们需要先看看上面的百度纠偏接口,需要注意的是,该接口每次返回的结果可能都不一样,也就是我们永远都是接近真实值,但永远都无法得到真实值

    有了这个接口,我们可以通过二次循环,经度从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
    View Code

    传入经纬度(百万分之一度),得到一个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;
    
                }
    View Code

    这样,我们就可以把原始坐标、偏移值、是否成功存到数据库了。

    有了这个数据库,使用相对比较简单,首先将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;
    View Code

    应该说,整个原理非常简单,真正的困难在于时间,实测了一下,一秒钟大约能解析4~5条,两千多万条记录大概需要两个多月连续工作,恐怕大多数人都是等不及的,要解决也很简单,多线程+多电脑操作,每个线程负责一段,我调用电脑资源比较方便,用了6台服务器,每台开两个程序,一个星期不到全部解析完毕。如果有需要的朋友,我上传到了CSDN下载中心了,具体链接等出来后,我在评论里给出,我自己使用的是sql server 2008 R2,考虑到很多人还是sql server 2005,故我备份为sql server2005的格式,如果你还在使用sql server 2000,那没办法了,只能找一台2005或2008的数据库还原,然后通过链接服务器插入到你的数据库了,别忘了经度、纬度一定要聚族索引,或者干脆去掉id,直接经度和纬度一起作为主键。

    经度float,纬度float,偏移经度nvarchar,偏移纬度nvarchar

    该方法可能影响了部分人的财路,在此,我表示诚挚的歉意。

    优点:本地执行,速度快。

    缺点:纠偏库需要花钱买或者在网上找,免费的可能不全。

  • 相关阅读:
    js 数据图表
    yii query builder
    mysql if
    这又是起点
    [cookie篇]从cookie-parser中间件说起
    How to find and fix Bash Shell-shock vulnerability CVE-2014-6271 in unix like system
    AngularJS打印问题
    笔记本上班时间自动静音下班自动打开
    SCP命令
    Installing Ruby 1.9.3 on Ubuntu 12.04 Precise Pengolin (without RVM)
  • 原文地址:https://www.cnblogs.com/shamo89/p/9902037.html
Copyright © 2011-2022 走看看