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

来源:互联网 发布:淘宝如何批量设置运费 编辑:程序博客网 时间:2024/04/28 12:15

原文地址: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
  1. - (void) doGen{  
  2.     if(isRunning)//防止重复进入  
  3.         return;  
  4.     isRunning=YES;  
  5.       
  6.     //获取文件保存路径,生成文件名  
  7.     NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory  
  8.                                                                 , NSUserDomainMask  
  9.                                                                 , YES);  
  10.     NSString *fn=[NSString stringWithFormat:@"coordoffset_db_%d_%d.txt",genType,stepDis];  
  11.     fn=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:fn];  
  12.     NSLog(@"gen filename: %@",fn);  
  13.       
  14.     //用C函数打开文件,以便写大文件  
  15.     FILE * hFile = fopen([fn UTF8String],"w+");  
  16.     if (hFile == NULL)  
  17.     {  
  18.         isRunning=NO;  
  19.         return;  
  20.     }  
  21.   
  22.     //使用自定义的内存池,定时释放内存,防止大循环中内存不足  
  23.     NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];  
  24.       
  25.     curCount=0;  
  26.     int cchCount=5000;  
  27.     CLLocationCoordinate2D coor1,coor2,coor3,coor4;  
  28.     char * buf=(char*)malloc(101*1024); //申请101KB的内存作为写入缓存  
  29.     int idx=0;  
  30.     for(int ix=startLon;ix<=endLon;ix+=stepDis){  
  31.         for(int iy=startLat;iy<=endLat;iy+=stepDis){  
  32.             coor1.longitude=ix/1000000.0;  
  33.             coor1.latitude=iy/1000000.0;  
  34.             if(genType==0){//判断转换类型  
  35.                 //百度,直接转  
  36.                 NSDictionary * dist=BMKBaiduCoorForWgs84(coor1);  
  37.                 coor2=BMKCoorDictionaryDecode(dist);  
  38.             } else {  
  39.                 //谷歌高德,先WGS转百度,再谷歌高德转百度,然后计算大概偏移,最后重新计算  
  40.                   
  41.                 NSDictionary * dist=BMKBaiduCoorForWgs84(coor1);  
  42.                 coor2=BMKCoorDictionaryDecode(dist);  
  43.                 dist=BMKBaiduCoorForGcj(coor1);  
  44.                 coor3=BMKCoorDictionaryDecode(dist);  
  45.                 double dx1=coor3.longitude-coor1.longitude;  
  46.                 double dy1=coor3.latitude-coor1.latitude;  
  47.                 double dx2=coor2.longitude-coor1.longitude;  
  48.                 double dy2=coor2.latitude-coor1.latitude;  
  49.                 double dx3=dx2-dx1;  
  50.                 double dy3=dy2-dy1;  
  51.                   
  52.                 //计算可能最接近原坐标纠偏结果的高德坐标,重新计算偏移  
  53.                 coor3.longitude=coor1.longitude+dx3;  
  54.                 coor3.latitude=coor1.latitude+dy3;  
  55.                 dist=BMKBaiduCoorForGcj(coor3);  
  56.                 coor4=BMKCoorDictionaryDecode(dist);  
  57.                 dx1=coor4.longitude-coor3.longitude;  
  58.                 dy1=coor4.latitude-coor3.latitude;  
  59.                 dx3=dx2-dx1;  
  60.                 dy3=dy2-dy1;  
  61.                   
  62.                 coor2.longitude=coor1.longitude+dx3;  
  63.                 coor2.latitude=coor1.latitude+dy3;  
  64.             }  
  65.             //写入缓冲区  
  66.             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);  
  67.             idx+=len;  
  68.             cchCount++;  
  69.             curCount++;  
  70.             if(cchCount>5000||idx>=100*1024||curCount>=recCount){  
  71.                 //每隔5000坐标,或缓冲使用达到100K时,或者达到最后一条记录时,写一次文件  
  72.                 fwrite(buf, idx, 1, hFile);  
  73.                 idx=0;  
  74.                 cchCount=0;  
  75.                 //释放内存池,并重新创建内存池  
  76.                 [pool release];  
  77.                 pool=[[NSAutoreleasePool alloc] init];  
  78.                 //记录当前坐标供刷新进度使用  
  79.                 curLon=ix/1000000.0;  
  80.                 curLat=iy/1000000.0;  
  81.             }  
  82.               
  83.             if(!isRunning)  
  84.                 break;  
  85.         }  
  86.         if(!isRunning)  
  87.             break;  
  88.     }  
  89.     free(buf);  
  90.     [pool release];  
  91.     fclose(hFile);  
  92.     isRunning=NO;  
  93. }  


以上代码在线程中执行,需要注意的是,为了防止大循环中内存池不释放导致内存不足,需要自己建立内存池并定时释放内存;为了支持大文件的写入,需要采用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调用查询了。


原创粉丝点击