在OpenLayers学习——Layer体系(一)中,绘制出整个OpenLayers的UML架构图。
这里重点讨论OpenLayers的网络瓦片地图的绘制过程,这里以XYZ图层作为样例来调试学习。
在讨论之前,首先明确一些基本概念:
分辨率(resolution):瓦片上每个像素所代表的地图距离(单位是地图单位)。
最大范围(maxExtent):地图或者图层的最大的范围。
瓦片切图原点的位置(tileOriginCorner):"tl”(左上角)、"tr”、"bl”、"br”。
行编号顺序(rowSign):不需要开发者直接调用,如果设置的tileOriginCorner为“tl”,则该值为1,代表为从上到下为行号,从左到右为列号,默认值为-1。
比率(ratio):地图分辨率与对应等级瓦片分辨率的比例。
调试代码,查看Layer绘图机制,如下图:
上图是简化的绘制出地图地图的整个过程。
(1)任何地图视图发生变化的时候都会执行map的moveTo方法,该方法中首先判断baseLayer是否存在,如果存在,则执行baseLayer的moveTo方法。
(2)在baseLayer的moveTo方法中首先会计算需要绘制的瓦片的行列号(即图中的initGridTiles),目的是为了计算该瓦片在服务器中的存放地址(url)。
计算瓦片的行列号,需要5个参数:待求瓦片的范围tileBound,地图切图的原点tileOrigin,地图的分辨率resolution,瓦片的大小tileSize,地图的切图方向。
这里假设地图切图的方向是从左到右,从上到下,则瓦片行列号的计算方法如下:
colNum = Math.Ceil((tileBound.getCenter().lon - tileOrigin.lon)/(tileSize.width*resolution));
rowNum =Math.Ceil((tileOrigin.lat - tileBound.getCenter().lat )/(tileSize.height*resolution));
(3)在initGridTiles()方法中,除了计算每一张瓦片的行列号之外还计算该瓦片在地图页面上的绘图位置(控件坐标系),目的是为了地图显示在前台页面上。
计算绘图位置的方法是,首先取到该图层在地图上左上角的地理坐标:
var layerContainerDivLeft = this.map.layerContainerOriginPx.x; var layerContainerDivTop = this.map.layerContainerOriginPx.y;
然后以图层容器左上角显示的瓦片的左上角地理坐标,并调用getViewPortPxFromLonLat()来计算该点的屏幕坐标。如下图所示:
图中黑色的边框矩形代表绘图的屏幕,即map对应的div,红色边框的矩形代表瓦片数据,getViewPortPxFromLonLat()方法计算的是某一个地图坐标的值对应在map的div参考坐标系下的屏幕坐标值。即要计算每一个瓦片绘制在什么位置上,通过该步骤计算得到,此处只要统一计算容器的左上角瓦片的坐标参考(图中蓝色的点),其他的瓦片绘图位置可以根据该位置推算得到。
在initGridTiles得到一个tileData数组,包含了屏幕上需要显示的瓦片所需要的所有数据,瓦片的行列号,瓦片的地理范围坐标,瓦片的屏幕坐标。
(4)完成初始化tileData数组之后,可以开始绘图,绘图是调用的renderTile()方法,该方法中可以调用到Layer的getUrl()方法获取到瓦片数据所在的路径,然后初始化地图容器中的该瓦片所对应的div,并在该div中添加img数据,将img的SRC指向瓦片的url,完成地图的渲染。
结论:
瓦片地图数据的绘图方式都是一样,初始化一个需要绘制瓦片大小一样的DIV容器,容器中存放一个Img,Img的Src设置为tile的Url。
瓦片地图数据最关键的是计算瓦片所在的服务器路径,即重写好getURL方法。
计算瓦片的方法都是一样,根据切图原点、瓦片大小、瓦片编号方式就可以计算出某一个瓦片的行列号,关键是理解所调用TMS服务的切图协议。