谷歌地图离线加载的设计与实现

2014-09-28 13:40:59    来源:中国地理信息产业协会

摘要:本文深入分析研究了Google Map瓦片组织结构,并使用QT图形界面库结合C++开发语言,实现对Google Map地图瓦片进行离线加载。

    Google Map向公众供免费网络地图服务,方便群众生活出行,同时提供方便、易掌握的API开发包,用于用户调用地图服务与自身应用相整合,实现应用程序开发[1][2]。但是应用的前提条件是计算机必须联网,对于一些上网不方便的用户来说,想结合Google Map地图服务做专题应用就变得困难了。针对上述情况,本文深入分析研究了Google Map瓦片组织结构,并使用QT图形界面库结合C++开发语言,实现对Google Map地图瓦片进行离线加载,展示地图服务,为非联网状态下使用Google Map地图服务提供一条解决方案。


1 Google Map瓦片分析
1.1 地图投影

    Google Map使用的是EPSG:900913标准的墨卡托投影,即为等角圆柱地图投影[3]。墨卡托投影的"等角"特性,保证了对象的形状的不变行;投影的"圆柱"特性,保证了南北(纬线)和东西(经线)都是平行直线,并且相互垂直。但是投影南北两极的变形比较严重,所以将两极85度以上的部分切除,形成东西和南北相等的正方形,便于之后进行四叉树计算。根据墨卡托投影的公式,将地球投影到一个边长为地球周长的正方形区域,可以反算出维度的范围是±85.05113度,经度范围为±180度。


1.2 瓦片组织
    Google Map是由分辨率大小为256*256的图片无缝拼接而成的,这些图片称为瓦片,并且从地图左上角开始编号。在初始等级时,整个地球只投影在一张256*256的图片上,往后每增加一级,瓦片会根据四叉树结构分割图片。级别为N的时候,整个地球被分为2N*2N张瓦片,瓦片索引号从(0,0)到(2N -1, 2N -1) (如图1)。


谷歌地图离线加载的设计与实现
图1


    瓦片级别确定后,整个地球切片对应的瓦片数量即确定,地球被划分的像素数量也随之确定。因此,地球的经纬度坐标和投影后的像素坐标存在对应关系。


1.3 坐标系
大地坐标和投影坐标
    Google Map的大地坐标系是WGS84,用经纬度表示整个世界范围,称为大地坐标。经过墨卡托投影后形成以米为单位的投影坐标。如果地球半径取6378137米,那么投影坐标系的最大边界值为±20037508.342789米。[4]
瓦片坐标
    坐标系的原点位于投影坐标系左上角,东西方向为横轴,南北方向为纵轴。瓦片(0,0)图片的左上角对应经纬度(-180,85.05113)。这样,大地坐标和瓦片坐标之间存在转换关系。比如Google Map在缩放级别_curTileLevel时,通过经纬度计算Google Map瓦片坐标的公式如下,
int getXByLongitude(double lon)
{
    int z = _curTileLevel;
    return (int)(((lon + 180) / 360.0) * pow(2.0,z));
}
int getYByLatitude(double lat)
{
    int z = _curTileLevel;
    double sinLat = sin(PI * lat / 180);
    double y = 0.5 - log((1.0 + sinLat) / (1.0 - sinLat))/(4.0 * PI);
    return (int)(y * pow(2.0,z));
}
像素坐标
     和瓦片坐标系定义一致,像素坐标系的原点位于投影坐标系左上角,东西方向为横轴,南北方向为纵轴。每个地图瓦片是一个正方形图片,并且图片大小固定,都是256×256像素。所以,大地坐标和像素坐标也存在对应关系,比如Google Map在缩放级别_curTileLevel上,整个地球将被一个(256*2_curTileLevel,256*2_curTileLevel)像素坐标系表示。大地坐标与像素坐标对应关系如下:
double getPixelXByLogitude(double lon)
{
return (lon + 180) * (256 << _curTileLevel) / 360;
}
double getPixelYByLatitude(double lat)
{
double siny = sin(lat * PI / 180);
double y = log((1 + siny) / (1 - siny));
return  (1.0 - y / (2 * PI))*((256/2) << _curTileLevel) ;
}
double getLogitudeByPixelX(double pixelx)
{
return (pixelx * 360) / (double)(256 << _curTileLevel) - 180;
}
double getLatitudeByPixelY(double pixely)
{
double y = (1.0 - (double)pixely/((256/2) << _curTileLevel)) * 2 * PI;
double siny = (exp(y) -1) / (exp(y) + 1);
return asin(siny) * 180 / PI;
}
窗口坐标
    QT图形库窗口坐标系以窗口左上角为坐标原点,水平方向为X轴,向右为正,竖直方向为Y轴,向下为正。窗口坐标和像素坐标是一致的。


2 Google Map离线加载
2.1 瓦片加载

    由于QT窗口坐标和Google Map像素坐标是一致的,所以可以将地图的大地坐标和窗口坐标建立对应关系。
    QT图形界面库对于图片的操作主要涉及到QGraphicsView、QGraphicsScene、QGraphicsItem三项。QGraphicsView作为窗口类,负责显示图片;QGraphicsScene类负责管理维护QGraphicsItem对象,相当于视图管理器,只能依附于QGraphicsView窗体进行图片显示。[5]
    当然,QGraphicsScene自身也具有坐标系统,抛开QT,在此处我们理解为1.3中所述的窗口坐标。Google Map的每一张瓦片都可以用一个QGraphicsItem对象进行表示,加入到QGraphicsScene场景中在QGraphicsView窗体上进行显示。当然,由于显示窗口大小有限,而每张瓦片的大小都是256x256,所以,需要初始化的QGraphicsItem对象的个数可以计算出来的。当瓦片内容需要更新的时候,只需要更新相应QGraphicsItem对象所显示的图片即可。这样做的好处是,显示需要显示的瓦片,而那些在视口范围之外瓦片则不需要显示。


谷歌地图离线加载的设计与实现
图2


    各类之间以及作用如图2所示。当需要进行移动地图操作时,需要更改QgraphicsView的显示位置(QT帮助文档上都有对应函数),每副瓦片的坐标位置也要更改。这时需要注意,瓦片位置改变了,其加载的瓦片也就改变了,可以通过坐标关系计算得到。


2.2 运行结果
    图3为对临沂市Google Map离线加载后的结果,实现了地图浏览基本操作,包括缩放、漫游、矢量影像地图的切换等。谷歌地图将影像和影像注记分别切片,所以,需要分别对瓦片数据进行加载显示。由于只有视口范围中的图片被加载显示,因此,加载速度很快,显示流畅。


谷歌地图离线加载的设计与实现
图3 运行结果


3 结束语
    本文对Google Map组织结构进行研究,得到屏幕窗口坐标系和Google Map坐标系的转化公式。提出Google Map地图离线加载的设计思路,并实现了Google Map地图的离线加载功能,为非联网机器使用Google Map地图服务提供了一套解决方案。在此基础上,通过进一步开发可以方便的实现地图定位等相关功能,辅助需要地图支持的专题应用系统的开发和应用,而如何与相关应用进行结合,是本文需要进一步研究的地方。

参考文献
[1] Google Maps运行机制以及应用研究. 巫细波,胡伟平.
[2] 基于Google Maps API的地图解析研究与实现[J].游兰,彭庆喜.湖北大学学报(自然科学版).2010(02).
[3] 高皓亮.基于GoogleMap的空间数据整合技术.中国科技论文在线.
[4] 张业舟,黄兴. GoogleMaps瓦片组织分析和应用研究. 测绘时空.
[5] Jasmin Blanchette,Mark Summerfield. C++ GUI QT 4编程(第二版). 电子工业出版社.


作者:李涛 皮青山 山东省临沂市国土资源局测绘院


声明:中国勘测联合网登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,文章内容仅供参考。