WebGL自学课程(16):WebGlobe实现的基本算法原理

时间:2021-12-11 04:46:20

WebGlobe(http://blog.csdn.net/sunqunsunqun/article/details/9119735)能够加载多种切片地图服务,并将其以正确的形式显示出来,本博文将阐述一下WebGlobe的基本算法原理,之所以称之为“基本”,原因是在实际开发过程中要竟可能的优化这些算法,自己在开发的过程中通过不断总结,发现根本的算法其实就是一句话:我只请求并显示那些应该被我们所看到的切片(其实就是对地图投影切片后的一个个小图片)。

1.WebMercator正反投影公式

现在Web上显示的切片地图基本都是经过WebMercator投影之后的,关于Mercator投影的介绍大家可以参见hmfly的文章mercator那些事儿,在此不详细介绍。WebMercator与Mercator还是有区别的,Mercator把地球当做一个椭球来处理,而WebMercator投影则将地球当做一个标准的球来处理,这种经过简化的处理更适合在Web端使用。WebGlobe调用的切片都是已经经过投影了的,我们最终的目的是将这些切片重新排列拼接成一个球,这一过程其实就是WebMercator反投影的过程。我们通过Mercator正反投影的公式可以推出WebMercator的正反投影公式。

WebMercator正投影公式如下:

WebGL自学课程(16):WebGlobe实现的基本算法原理

WebMercator反投影公式如下:

WebGL自学课程(16):WebGlobe实现的基本算法原理

参数说明:

dLog表示以角度为单位的经度,rLog表示以弧度为单位的经度,东经为正,西经为负取值范围是角度[-180,180],即弧度[-PI,PI];

dLat表示以角度为单位的纬度,rLat表示以弧度为单位的纬度,北纬为正,南伟为负, 取值范围是角度[-85.05112877980659,85.05112877980659],即弧度[-1.4844222297453322, 1.4844222297453322];

r表示地球半径,6378137米;

x和y分别表示投影后的坐标,都是以米为单位,范围都是[-20037508.3427892, -20037508.3427892]


2.根据当前Map的level构建可见Tile的索引序列

Map的level是从0开始的,随着level层级变大,大层级的level中的所有切片的数量是其小层级level切片数量的4倍,也就是每一层级level中所有切片的数量和level是存在数学关系的:

WebGL自学课程(16):WebGlobe实现的基本算法原理

当我们将地球缩放到某一个level的时候,此时earth上所显示的切片是这样构成的:假设当前的level是5,那么earth上肯定有第5级的切片,但是并不是所有的第5级的切片都显示在earth,我们只需要将处于视景体内的第5级的切片显示出来就可以,不在我们视野范围内的地区(比如earth的背面)我们无需使用第5级的切片将其渲染。假设我们使用了4个第5级的切片5_a.png5_b.png、5_c.png、5_d.png。有一点需要注意的是,除了第0级外,每个切片都有一个父切片,上面的4个第5级的切片肯定有父切片,有可能他们的父切片是同一个,也有可能各不相同,假设各不相同,那么它们的父切片是4_a.png、4_b.png、4_c.png、4_d.png。这四个第4级的父切片也是要显示的,同理,第三级的父切片也要显示,直到第0级。除了第0级外,第1、2、3、4、5级都可能会只显示一部分该level下的切片,这种情况随着层级level的变大而更加明显,也就是说随着level的变大,该level下所显示的切片数量占该level总数量的比例越来越小,这主要是因为总数量越来越大了。

现在需要解决的一个问题是在当前level下(比如level=5)如何找出需要显示的切片即处于视野中的切片。首先,我们知道第0级中唯一一张切片所占的地理范围是Log[-180,180] Lat[-85.05112877980659,85.05112877980659],也可以简化为[-85,85]。我们第2级中有4张图片。我们以Esri发布的全球影像切片来研究

http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/0/0/0

其获取图片的方式是service/tile/a/b/c。

每个图片都是有三个数字组成的索引(a/b/c)决定的,第零级中唯一一张图片的索引是tile/0/0/0,获取的图片如下:

WebGL自学课程(16):WebGlobe实现的基本算法原理

第1级中有4张图片,索引分别是tile/1/0/0、tile/1/0/1、tile/1/1/0以及tile/1/1/1,图片如下所示:

WebGL自学课程(16):WebGlobe实现的基本算法原理tile/1/0/0,位于tile/0/0/0左上角 WebGL自学课程(16):WebGlobe实现的基本算法原理tile/1/0/1,位于tile/0/0/0右上角
WebGL自学课程(16):WebGlobe实现的基本算法原理tile/1/1/0,位于tile/0/0/0的左下角 WebGL自学课程(16):WebGlobe实现的基本算法原理tile/1/1/1,位于tile/0/0/0的右下角

tile/a/b/c中的a表示的是层级level,一般的取值范围是[0,20];在层级a下有 个切片,这 个切片是由 行与 列共同组成的,b表示切片在该层级中的行号,c表示切片在该层级中的列号,b和c的取值范围都是[ ],a层级下左上角切片为T(a,0,0),右上角切片为T(a,0, ),左下角切片为T(a, ,0),右下角切片为T(a, , ),根据切片所在的层级以及其行列号我们就可以获取该切片。假设现在我们知道了一个切片T(a,b,c)的索引是tile/a/b/c,那么我们如何获取该切片所覆盖的以经纬度表示的地理范围呢?由于我们的切片都是经过投影之后生成的,所以我们不能直接计算该切片所覆盖的以经纬度表示的地理范围,我们首先计算该切片所覆盖的以米为单位的投影区域,我们以E prj(x1,y1,x2,y2)的形式表示该切片的投影区域,prj表示代表的区域是投影区域,x1、y1表示该切片左下角点以米为单位的投影坐标,x2,y2表示右上角点以米为单位的投影坐标。计算公式如下:size表示在a层级下每个切片的边长所代表的实际的投影长度

WebGL自学课程(16):WebGlobe实现的基本算法原理

通过上面的计算我们就得到了该切片所覆盖的投影区域E prj(x1,y1,x2,y2),此时该切片四个角点的投影坐标分别如下:

左下角:[x1,y1];      右下角:[x2,y1];      左上角:[x1,y2];      右上角:[x2,y2];

然后按照前述的WebMercator反投影公式分别将这四个投影坐标转换为以弧度表示的经纬度,然后转换为以角度表示的经纬度,可以获得该切片的最小经度log1、最大经度log2、最小纬度lat1、最大纬度lat2,其地理区域范围表示如下E(log1,lat1,log2,lat2)。

回到我们之前的问题:在当前level下(比如level=5)如何找出需要显示的切片即处于视野中的切片。

假设现有一个函数isTileVisible用于判断任意切片的经纬度范围是否处于视野内。

算法是这样的:

a.     首先建立一个可视切片的空索引,

{

tile:{

         level:0,

index:[0,0,0],

childTiles:[]

}

}

b.     从第level=1级开始,遍历该层级下所有的切片T(level,b,c),对于每个切片计算其地理范围E(log1,lat1,log2,lat2),然后调用isInViewFrustum函数判断该切片是否应该处于视野中,如果处于视野中,则构建一个tile对象{level:level,index:[level,b,c],childTiles:[]},并将其填入到其父tile的childTiles数组中;如果不处于视野中,并且如果本身之前已经作为了tile放入了其父tile的childTiles数组中,那么就首先迭代遍历清空该tile的子孙tile,然后将该tile从其父tile中移除。

c.     按照b步骤中的方法迭代处理level层级中的所有的切片。

d.     处理完某一个level之后,继续处理level+1层级,按照此方法直到处理完当前的层级。比如当前的层级是5,那么就需要从level1处理到level5。

e.     按照上述步骤就构建了一个可视切片的索引,而且该索引没有冗余的切片,里面的切片都是必须的,而且是最精简的。然后我们需要用这些切片作为纹理更新earth这就保证了在我们视景体(视野)中所看到的切片都是用的level级数比较大的,从而看到的图片也比较清晰;对于我们看不到的earth的那些部分,我们采用了level比较低的切片,但是这些切片都位于我们构建的索引中。


3.判断某个切片是否应该可见

上面我们提到了需要一个函数isTileVisible在当前视野下某切片是否应该可见,下面就说一下该函数实现的的一些基本思路。一个切片如果处于我们的视野中可见,需要满足以下两个条件:

a.切片的经纬度范围与当前视野的经纬度范围有交集;

b.切片位于面向我们视点的那个半球而非地球的背面,因为处于地球背面的切片我们是不需要显示的。

其实有好多方式可以实现isTileVisible方法,大家要力所能及的优化再优化直至计算量最小效果最好为止。


这篇博文啰啰嗦嗦说了这么多,其实就是在做一件事:我只请求并显示那些应该被我们所看到的切片。