您的位置:首页 > 数据库

利用地图SDK生成火星坐标纠偏数据库

2013-06-14 13:06 393 查看
原文地址:http://blog.csdn.net/huzgd/article/details/8933118?reload

众所周知,我们国内的电子地图如谷歌高德都是采用加密偏移过的火星坐标(GCJ-02),但手机GPS获取的却是地球坐标(WGS-84),如何从地球纠偏转到火星,这个问题是中国程序员必须要面对的。一般来说,有以下几种方式:纠偏算法、纠偏接口和纠偏数据库。有些东西其实已经是公开的秘密,因此这里我把生成纠偏数据库的过程写一下,应该也不是什么大问题吧。请注意此文的前提是假设没有纠偏算法,用SDK的纠偏接口生成纠偏库。

纠偏数据可以在网上找,但免费的并不好找,网上能找到的多数不全,或者精度较低;X宝上有人卖纠偏数据库或纠偏接口的,价格也不便宜,这么嚣张的数据一般人也不愿意共享。于是我萌生了自己调纠偏接口生成纠偏数据库的念头。

高德和百度提供了在线纠偏的接口,但毕竟是在线网络请求,坐标多的话纠偏速度较慢。全中国0.01精度的纠偏数据大概有3千万条,以一条40字节算有1.1G大小,压缩后也有一两百M。假设每秒纠10条,3千万条可能需要纠上一个月。一般地图的移动SDK(如高德)也提供了坐标纠偏功能,但多数是转而调用在线纠偏接口,速度慢。但我在使用百度地图SDK时,发现百度地图也有纠偏接口,这个接口在文档上并未公开,但确是存在的,而且它的纠偏是瞬间直接完成的,不需要连网,因此可以确定它内置了纠偏算法,可以利用它来生成纠偏数据库。

百度地图SDK有安卓和iOS版,我选择的是iOS版,原因很简单:iOS支持x86的原生指令模式运行,速度自然比安卓ARM的JAVA虚拟机快得多。代码并不复杂,基本上就是循环遍历中国的所有经纬度范围,进行转换坐标,计算偏移量。百度自己在火星坐标基础上做了个二次加密形成自己的百度坐标系(BD-09),支持从地球坐标(WGS-84)转到百度坐标(BD-09),但它也支持从火星坐标(GCJ-02)转到百度坐标(BD-09),因此我们可以通过两者相减计算出火星坐标(GCJ-02)。

于是乎我写了纠偏库生成程序,支持不同精度类型的纠偏数据生成,主体代码如下:

[cpp] view
plaincopy

- (void) doGen{  

    if(isRunning)//防止重复进入  

        return;  

    isRunning=YES;  

      

    //获取文件保存路径,生成文件名  

    NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory  

                                                                , NSUserDomainMask  

                                                                , YES);  

    NSString *fn=[NSString stringWithFormat:@"coordoffset_db_%d_%d.txt",genType,stepDis];  

    fn=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:fn];  

    NSLog(@"gen filename: %@",fn);  

      

    //用C函数打开文件,以便写大文件  

    FILE * hFile = fopen([fn UTF8String],"w+");  

    if (hFile == NULL)  

    {  

        isRunning=NO;  

        return;  

    }  

  

    //使用自定义的内存池,定时释放内存,防止大循环中内存不足  

    NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];  

      

    curCount=0;  

    int cchCount=5000;  

    CLLocationCoordinate2D coor1,coor2,coor3,coor4;  

    char * buf=(char*)malloc(101*1024); //申请101KB的内存作为写入缓存  

    int idx=0;  

    for(int ix=startLon;ix<=endLon;ix+=stepDis){  

        for(int iy=startLat;iy<=endLat;iy+=stepDis){  

            coor1.longitude=ix/1000000.0;  

            coor1.latitude=iy/1000000.0;  

            if(genType==0){//判断转换类型  

                //百度,直接转  

                NSDictionary * dist=BMKBaiduCoorForWgs84(coor1);  

                coor2=BMKCoorDictionaryDecode(dist);  

            } else {  

                //谷歌高德,先WGS转百度,再谷歌高德转百度,然后计算大概偏移,最后重新计算  

                  

                NSDictionary * dist=BMKBaiduCoorForWgs84(coor1);  

                coor2=BMKCoorDictionaryDecode(dist);  

                dist=BMKBaiduCoorForGcj(coor1);  

                coor3=BMKCoorDictionaryDecode(dist);  

                double dx1=coor3.longitude-coor1.longitude;  

                double dy1=coor3.latitude-coor1.latitude;  

                double dx2=coor2.longitude-coor1.longitude;  

                double dy2=coor2.latitude-coor1.latitude;  

                double dx3=dx2-dx1;  

                double dy3=dy2-dy1;  

                  

                //计算可能最接近原坐标纠偏结果的高德坐标,重新计算偏移  

                coor3.longitude=coor1.longitude+dx3;  

                coor3.latitude=coor1.latitude+dy3;  

                dist=BMKBaiduCoorForGcj(coor3);  

                coor4=BMKCoorDictionaryDecode(dist);  

                dx1=coor4.longitude-coor3.longitude;  

                dy1=coor4.latitude-coor3.latitude;  

                dx3=dx2-dx1;  

                dy3=dy2-dy1;  

                  

                coor2.longitude=coor1.longitude+dx3;  

                coor2.latitude=coor1.latitude+dy3;  

            }  

            //写入缓冲区  

            int len=sprintf(&buf[idx],"%0.3f,%0.3f,%0.6f,%0.6f\n",coor1.longitude,coor1.latitude,coor2.longitude-coor1.longitude,coor2.latitude-coor1.latitude);  

            idx+=len;  

            cchCount++;  

            curCount++;  

            if(cchCount>5000||idx>=100*1024||curCount>=recCount){  

                //每隔5000坐标,或缓冲使用达到100K时,或者达到最后一条记录时,写一次文件  

                fwrite(buf, idx, 1, hFile);  

                idx=0;  

                cchCount=0;  

                //释放内存池,并重新创建内存池  

                [pool release];  

                pool=[[NSAutoreleasePool alloc] init];  

                //记录当前坐标供刷新进度使用  

                curLon=ix/1000000.0;  

                curLat=iy/1000000.0;  

            }  

              

            if(!isRunning)  

                break;  

        }  

        if(!isRunning)  

            break;  

    }  

    free(buf);  

    [pool release];  

    fclose(hFile);  

    isRunning=NO;  

}  

以上代码在线程中执行,需要注意的是,为了防止大循环中内存池不释放导致内存不足,需要自己建立内存池并定时释放内存;为了支持大文件的写入,需要采用C的文件操作函数代替NSDATA;为了保证写入速度,我在其中使用了写入缓冲,每5000行写入一次。我原以为生成过程需要执行半天,还专门写了定时器刷新进度估计完成时间,结果经过这么优化后,生成速度比较快,3000万条记录大概十分钟可以生成完成,大大超出我的意料。



考虑到实际需要,我生成的是0.025精度的数据,大概500万条数据。直接在模拟器运行,运行完成后,在Finder找到生成的文件,将它拷贝出来导入数据库。



数据库我用的是ORACLE,用SQL LOADER导入,导入前先删除所有索引约束,扩大表空间限制,每隔5000条COMMIT一次,本机到服务器千兆连接,导入速度大概每秒1500条,也导了大半小时才导完。

导完后建索引。考虑到浮点数索引效率可能不高,我把经纬度转成了整数,如113.05转成113050000,23.05转成23050000,然后建立联合索引。索引建完后一测试,查询一个坐标需要6、7秒,还是太慢。于是把经度和纬度各取6位合并成一个12位唯一主键索引字段,如113.05和23.05合并成113050023050,主键索引建立好后再测试,查询SQL瞬间返回了。

至此纠偏数据库建立完成,可以直接SQL调用查询了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: