前端根据地理范围换算出瓦片行列号的原理核心
影像金字塔简介
影像金字塔就是,我们首先将原始影像按照用户的需求,比如用户需要显示多少种比例尺下的数据,需要显示的是原始影像中的哪个区域的数据,将原始影像按照这些需求进行划分和提取
最低层就是我们提取和划分出的比例尺最小的一级的瓦片,而最上层的则是比例尺最大的一级的瓦片。我们仔细观察可以发现这样的一个规律:比例尺越小的级别瓦片数据越少,反之则越大。而这个规律导致的结果就是:比例尺越小的级别切图的速度越快,同时,同样大小的瓦片所包含的影像范围越多。
当我们建立好了影像金子塔后,前端再请求地图时,则将只是在切好的瓦片缓存中,找到对应级别里对应的瓦片即可。然后在前端将这些请求到的瓦片拼接出来,便可以得到用户需要的级别下的可视范围内的瓦片了。
瓦片行列号的换算原理
假设,地图切图的原点是(x0,y0),地图的瓦片大小是tileSize,地图屏幕上1像素代表的实际距离是resolution。计算坐标点(x,y)所在的瓦片的行列号的公式是:
col = floorx0 - x/ tileSize*resolution
row = floory0 - y/ tileSize*resolution
简单点说就是,首先算出一个瓦片所包含的实际长度是多少LtileSize,然后再算出此时屏幕上的地理坐标点离瓦片切图的起始点间的实际距离LrealSize,然后用实际距离除以一个瓦片的实际长度,即可得此时的瓦片行列号:LrealSize/LtileSize。
换算流程
- originX,originY:地图切图时的切图原点坐标。
- tileSize:瓦片的屏幕像素大小。
- Level:地图级别。
- resolution:某地图级别下屏幕一像素代表的实际单位大小。
- canvasWidth、canvasHeight:屏幕的长宽
- geoMaxX、geoMinX:地理范围中的最大、最小X坐标。
获得请求地理范围中的中心点(centerGeoPoint)
为什么我们要首先换算这个中心点呢。原因是我们最后需要的真实地理范围,并不一定是屏幕范围所对应的那个地理范围,它极有可能是大于这个屏幕地理范围的。而事实上是,它一定是大于的,在后面我们讲解瓦片图层类的设计时,会提到一个地理范围缓冲宽度,那时候大家就更能明白为什么是要首先获取地理范围中的中心点了。
判断请求中是否包含了需要显示的地图级别,分别处理
包含了Level
如果请求中已经指定了使用的Level,则我们接下来可以直接使用此Level来进行地图实际请求范围的换算。
没有包含Level
当请求中无Level时,我们的换算将会比较复杂一些,这个换算的目的就是求出此时的地图应该以什么Level显示是最合适的,即nearestLevel。它的过程是,首先根据请求中的地理范围和屏幕大小范围,求得此时我们本需要的瓦片实际大小,即:geoMaxX-geoMinX/ canvasWidth/tileSize,也就是用实际地理长度除以此时的瓦片个数,从而得到了我们请求中本需求的瓦片实际大小
但是,目前我们不能保证我们所切的图中是一定有这个需求里的比例尺的。于是我们还需要做一个遍历,遍历我们的地图中所有的比例尺,找出一个与此需求比例尺下的瓦片实际大小最贴近的真实瓦片实际大小,而这个瓦片实际大小所对应的此时的地图比例尺,即是我们求得到的最合适的比例尺,它所代表的地图级别就是最贴近需求的地图级别,nearestLevel。
算出屏幕范围所对应的地理范围 minX、minY、maxX、maxY
首先得到Level下的一像素代表的实际大小,即resolution。然后用centerGeoPoint加上或减去半个屏幕长度(canvasBounds)乘以resolution后得到的范围便是需求中的屏幕范围在获得的Level下应该对应的实际地理范围。
以屏幕左上角X所对应的实际地理坐标为例:
minX =centerGeoPoint - resolution* canvasWidth/2;
这里顺便提一下,算出的这个屏幕范围所对应的地理范围,它的作用是非常大的,在以后的屏幕坐标转换成地理坐标,以及地理坐标转换成屏幕坐标,还有偏移补偿量的换算上是至关重要的一个参数。
瓦片起始行列号fixedTileLeftTopNumX、fixedTileLeftTopNumY
我们算出来的地理范围并不能保证真实的瓦片的起始瓦片所对应的地理坐标与地理范围的左上角地理范围重合,为此我们应该允许地理范围的一个扩张,这个扩张多少是一个很值得推敲的地方。这里我们默认为扩张至请求到的第一张瓦片左上角所对应的地理坐标。
fixedTileLeftTopNumX = Math.floorMath.absoriginX - minX/resolution*tileSize;
fixedTileLeftTopNumY = Math.floorMath.absoriginY - maxY/resolution*tileSize;
实际地理范围realMinX、realMaxY
realMinX = fixedTileLeftTopNumX * curLevelClipLength + originX;
realMaxY= originY - fixedTileLeftTopNumY * curLevelClipLength;
左上角偏移像素offSetX、offSetY
由于地理范围中的第一张瓦片,即左上角的第一张瓦片,并不一定是完全包含在屏幕地理范围内的,于是这里又涉及到另外一对参数,左上角偏移像素。
为什么要求这个参数呢,原因是,当我们把瓦片都请求回来后还要做一个换算,即换算出每一张瓦片的左上角坐标应该对应在图层(TIleCanvas)上的哪一个屏幕坐标。这个偏移像素便是为了这个换算而做的准备。
offSetX = realMinX- minX /resolution;
offSetY = maxY - realMaxY /resolution;
X、Y轴上的瓦片个数mapXClipNum、mapYClipNum
mapXClipNum = Math.ceilcanvasWidth + Math.absoffSetX/tileSize;
mapYClipNum = Math.ceilcanvasHeight + Math.absoffSetY/tileSize;
