WebGL自學課程(16):WebGlobe實現的基本演算法原理

孫群發表於2013-06-23

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正投影公式如下:


WebMercator反投影公式如下:


引數說明:

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是存在數學關係的:


當我們將地球縮放到某一個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,獲取的圖片如下:

第1級中有4張圖片,索引分別是tile/1/0/0、tile/1/0/1、tile/1/1/0以及tile/1/1/1,圖片如下所示:

tile/1/0/0,位於tile/0/0/0左上角
tile/1/0/1,位於tile/0/0/0右上角
tile/1/1/0,位於tile/0/0/0的左下角
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層級下每個切片的邊長所代表的實際的投影長度

通過上面的計算我們就得到了該切片所覆蓋的投影區域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方法,大家要力所能及的優化再優化直至計算量最小效果最好為止。


這篇博文囉囉嗦嗦說了這麼多,其實就是在做一件事:我只請求並顯示那些應該被我們所看到的切片。


相關文章