Cesium聚簇實現 kdbush類實現

2021-09-26 20:16:37 字數 3901 閱讀 2076

開發者除了開發了該庫,還開發了另外幾個空間搜尋庫,包括rbush,rbush-knn,geokdbush,相關的原理說明請參考:

若感興趣的同學可自行檢視。

建構函式

function kdbush(points, getx, gety, nodesize, arraytype)

引數說明

points:所有要參加搜尋的點集合

getx,gety:獲取每個點的x和y座標函式引用

nodesize:點個數

arraytype:點座標資料型別

公共函式

function range(minx, miny, maxx, maxy)

獲取矩形框範圍內所有點

引數:用矩形的最大最小xy值唯一確定乙個矩形

function within(qx, qy, r)

獲取圓形範圍內所有點

qx,qy:圓心xy座標

r:半徑

function kdbush(points, getx, gety, nodesize, arraytype) 

sort(this.ids, this.coords, this.nodesize, 0, this.ids.length - 1, 0);

}

首先將points點座標全部放到coords陣列中,用ids陣列記錄索引,然後用sort函式實現排序。

sort函式是乙個遞迴函式,功能是將點座標按照之前提到的kdbush方法重新排序,方便快速查詢。

function sort(ids, coords, nodesize, left, right, depth)
閱讀這些經典演算法的源**,始終有一種如沐春風的感覺,函式優美地分離,程式清晰簡潔,能看懂一眼就明白,看不懂地也一眼讓你看出來,避免帶你入坑太久,虐的你體無完膚再放你出來,好在如今一切看起來都是那麼地自然清新。

這裡沒什麼好解釋,**自解釋:找到中間索引點,呼叫乙個select函式進行一番重排序操作,然後遞迴呼叫sort函式分別處理中間索引左側和右側序列,並且有乙個引數depth標識它遞迴到哪一層,所以主要邏輯在函式select中。

function select(ids, coords, k, left, right, inc) 

var t = coords[2 * k + inc];

var i = left;

var j = right;

swapitem(ids, coords, left, k);

if (coords[2 * right + inc] > t) swapitem(ids, coords, left, right);

while (i < j)

if (coords[2 * left + inc] === t) swapitem(ids, coords, left, j);

else

if (j <= k) left = j + 1;

if (k <= j) right = j - 1;}}

以上**很好地展現了優秀**的風格,比如在一開始進入迴圈的部分,有一段當right-left大於600的**段,裡面有很多數學公式,很明顯這段**本人看不懂,感謝作者在這裡將其很好地與主程式隔離,讓讀者即使不去理會這一段也不影響整個程式的理解。

接下來的程式就是通過不斷交換陣列元素達到生成符合要求的陣列的目標,其中swapitem是交換函式,該函式交換了指定索引的點座標和點索引,而每次呼叫select得到的新數列符合要求:在left和right中間索引點的x或者y座標滿足:其左邊的所有點x或者y左邊都小於等於該點對應座標,其右邊所有點的x或者y左邊都大於等於該點對應座標,至於用x還是用y來作為比較依據,那取決於當前的層級,也就是最後乙個引數inc,層級為偶數為x,層級為奇數為y,層級從0開始。

整個演算法總體思想:

將數列的中間點取出,放到數列的指定位置,使其滿足在這個位置左邊的所有值都小於等於該點值,右邊的所有值都大於等於該點值,然後根據新位置和中間點關係來縮小查詢範圍,直到找到的新位置正好是中間位置為止。

如下圖所示舉例說明:

矩形框查詢函式為range:

function range(ids, coords, minx, miny, maxx, maxy, nodesize) 

continue;

}var m = math.floor((left + right) / 2);

x = coords[2 * m];

y = coords[2 * m + 1];

if (x >= minx && x <= maxx && y >= miny && y <= maxy) result.push(ids[m]);

var nextaxis = (axis + 1) % 2;

if (axis === 0 ? minx <= x : miny <= y)

if (axis === 0 ? maxx >= x : maxy >= y)

}return result;

}

**相當簡潔,其中判斷條件即為:

if (x >= minx && x <= maxx && y >= miny && y <= maxy) result.push(ids[m]);

當前點的x座標和y座標值都分別在minx、maxx和miny、maxy之間。

前半部分是普通依次對比的演算法,不用太在意,在cesium中呼叫nodesize值為64,也就是乙個點最多又64個子節點,在這64個點中搜尋就採用依次搜尋:

if (right - left <= nodesize) 

continue;

}

而後半部分是類似二分法查詢,由於資料都在前面的排序演算法中構成了樹,所以這裡只需要對比中間點,判斷中間點對應的x或者y(具體是x還是y要看當前迴圈到哪一層,單數層是x,雙數層是y)是大於minx或者miny還是小於maxx或者maxy,如果是前者,則將中間點的左側點都加入下一輪比較,如果是後者,則將中間點的右側點都加入下一輪比較,如果兩者都滿足,說明當前中間點本身就在查詢範圍內,這時候並不能確定其左側點或者右側點都不在查詢範圍,所以將本輪中間點的左右兩側點都加入下一輪比較。

圓形範圍查詢和矩形範圍查詢基本一致,方法為within:

function within(ids, coords, qx, qy, r, nodesize) 

continue;

}var m = math.floor((left + right) / 2);

var x = coords[2 * m];

var y = coords[2 * m + 1];

if (sqdist(x, y, qx, qy) <= r2) result.push(ids[m]);

var nextaxis = (axis + 1) % 2;

if (axis === 0 ? qx - r <= x : qy - r <= y)

if (axis === 0 ? qx + r >= x : qy + r >= y)

}return result;

}

只是判斷條件不同:

if (sqdist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) result.push(ids[i]);

這裡sqdist為勾股定理距離:

function sqdist(ax, ay, bx, by)

聚簇與非聚簇索引

我們平時建立的索引唯一鍵索引,復合索引,字首索引都是非聚簇索引,有的也叫輔助索引 secondary index 其資料結構是b 樹。在mysql中,聚簇索引沒有語句可以生成,在 innodb中,資料是按照主鍵的順序來進行儲存的。葉子節點就是存放每條記錄的。由於表所有資料只能按照乙個b 樹進行排序,...

聚簇索引和非聚簇索引

一 聚簇索引 clustered indexes 的使用 聚簇索引是一種對磁碟上實際資料重新組織以按指定的乙個或多個列的值排序。由於聚簇索引的索引頁面指標指向資料頁面,所以使用聚簇索引查詢資料幾 乎總是比使用非聚簇索引快。每張表只能建乙個聚簇索引,並且建聚簇索引需要至少相當該錶120 的附加空間,以...

聚簇索引和非聚簇索引

一 聚簇索引 clustered indexes 的使用 聚簇索引是一種對磁碟上實際資料重新組織以按指定的乙個或多個列的值排序。由於聚簇索引的索引頁面指標指向資料頁面,所以使用聚簇索引查詢資料幾乎總是比使用非聚簇索引快。每張表只能建乙個聚簇索引,並且建聚簇索引需要至少相當該錶120 的附加空間,以存...