前言
B树
B树是一棵平衡树,它是把一维直线分为若干段线段,当我们查找满足某个要求的点的时候,只要去查找它所属的线段即可。这种思想其实就是先找一个大的空间,再逐步缩小所要查找的空间,最终在一个自己设定的最小不可分空间内找出满足要求的解。
R树介绍
- 二维 R 树索引不同于传统的等级(一维)B 树索引。空间数据是二维的,所以无法使用 B 树索引组织空间数据。
- 同样,使用 R 树索引不能表示非空间数据。R 树是一种用矩形表示结点的树状结构来组织数据的访问方法。
R树数据结构
- R树是B树在高维空间的扩展,是一棵平衡树。
- 每个R树的叶子结点包含了多个指向不同数据的指针,这些数据可以是存放在硬盘中的,也可以是存在内存中。
- M 为一个节点中的最大条目数。取 m <= M/2,约定此 m 为一个节点中的最小条目数。此时,R 树具有如下性质:
- 若叶节点不是根节点,则每个叶节点所包含的索引记录个数介于 m 与 M 之间;
- 对于叶节点中的索引记录(I,tuple-identifier),I 是其最小外包矩形;
- 若非叶节点不是根节点,则其中包含的子节点个数介于 m 与 M 之间;
- 对于非叶节点中的条目(I,child-pointer),I 表示能够覆盖其所有子节点的外包矩形的外包矩形。
- 根节点若非叶节点,则其至少有两个子节点。
- 所有叶子节点都在同一层。
代码实现
GeoTools实现
R树构建
|
|
查询
|
|
GeoTools实现说明
STR算法
- geotools中R树的实现使用的是STR算法,
- 对矩形的分组只考虑每个矩形的中心点,STR的基本思想是将所有的矩形分配到$\left \lceil r/n \right \rceil $(取上界)个分组中;
- 首先,对矩形按x坐标排序,然后划分成 $\sqrt{r/n}$ 个slice;
- 然后,对slice内的矩形按y坐标排序,进一步划分成 $\sqrt{r/n}$ 份;
- 最后递归的向上构建,直到根节点。
Insert
stRtree.insert(Envelope itemEnv, Object item)
时序调用如下
- 插入的一条记录为
ItemBoundable
对象,包含一个Envelope
和插入的数据 insert
的数据实际就是叶子节点,RTree build
的时候根据叶子节点向上构建。
Build
stRtree.build();
真正构建R
树createHigherLevels
递归构建R
树,递归结束的条件就是父节点只有一个,即为根节点12345678private AbstractNode createHigherLevels(List boundablesOfALevel, int level) {Assert.isTrue(!boundablesOfALevel.isEmpty());List parentBoundables = createParentBoundables(boundablesOfALevel, level + 1);if (parentBoundables.size() == 1) {return (AbstractNode) parentBoundables.get(0);}return createHigherLevels(parentBoundables, level + 1);}createParentBoundables
,根据给定的子节点信息,获取父节点的信息GeoTool
中STRtree
对象每个节点的子节点默认大小为10
个- 将子节点根据
Envelope
中心点按x
轴排序,然后划分成 $\sqrt{r/n}$ 个slice,比如r=288, n=10,即划分为6个slice,每个slice包含288/6=48个节点
1234567891011121314151617181920212223protected List createParentBoundables(List childBoundables, int newLevel) {Assert.isTrue(!childBoundables.isEmpty());int minLeafCount = (int) Math.ceil((childBoundables.size() / (double) getNodeCapacity()));ArrayList sortedChildBoundables = new ArrayList(childBoundables);Collections.sort(sortedChildBoundables, xComparator);List[] verticalSlices = verticalSlices(sortedChildBoundables,(int) Math.ceil(Math.sqrt(minLeafCount)));return createParentBoundablesFromVerticalSlices(verticalSlices, newLevel);}private List createParentBoundablesFromVerticalSlices(List[] verticalSlices, int newLevel) {Assert.isTrue(verticalSlices.length > 0);List parentBoundables = new ArrayList();for (int i = 0; i < verticalSlices.length; i++) {parentBoundables.addAll(createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));}return parentBoundables;}protected List createParentBoundablesFromVerticalSlice(List childBoundables, int newLevel) {return super.createParentBoundables(childBoundables, newLevel);}其中
super.createParentBoundables
针对每个slice,按照y坐标排序,每10个节点划分到一个group中- 其中父节点使用的是createNode(newLevel)创建,创建的为
AbstractNode
对象,
12345678910111213141516171819protected List createParentBoundables(List childBoundables, int newLevel) {Assert.isTrue(!childBoundables.isEmpty());ArrayList parentBoundables = new ArrayList();parentBoundables.add(createNode(newLevel));ArrayList sortedChildBoundables = new ArrayList(childBoundables);Collections.sort(sortedChildBoundables, getComparator());for (Iterator i = sortedChildBoundables.iterator(); i.hasNext(); ) {Boundable childBoundable = (Boundable) i.next();if (lastNode(parentBoundables).getChildBoundables().size() == getNodeCapacity()) {parentBoundables.add(createNode(newLevel));}lastNode(parentBoundables).addChildBoundable(childBoundable);}return parentBoundables;}protected AbstractNode lastNode(List nodes) {return (AbstractNode) nodes.get(nodes.size() - 1);}父节点的
Envelope
对象计算方式如下,即遍历父节点对应的子节点,找到能将子节点包围住的矩形框1234567891011121314151617181920212223242526272829303132333435363738protected Object computeBounds() {Envelope bounds = null;for (Iterator i = getChildBoundables().iterator(); i.hasNext(); ) {Boundable childBoundable = (Boundable) i.next();if (bounds == null) {bounds = new Envelope((Envelope)childBoundable.getBounds());}else {bounds.expandToInclude((Envelope)childBoundable.getBounds());}}return bounds;}public void expandToInclude(Envelope other) {if (other.isNull()) {return;}if (isNull()) {minx = other.getMinX();maxx = other.getMaxX();miny = other.getMinY();maxy = other.getMaxY();}else {if (other.minx < minx) {minx = other.minx;}if (other.maxx > maxx) {maxx = other.maxx;}if (other.miny < miny) {miny = other.miny;}if (other.maxy > maxy) {maxy = other.maxy;}}}
- 其中父节点使用的是createNode(newLevel)创建,创建的为
Query
先判断根节点是否是否包含
123456789101112protected List query(Object searchBounds) {build();ArrayList matches = new ArrayList();if (isEmpty()) {//Assert.isTrue(root.getBounds() == null);return matches;}if (getIntersectsOp().intersects(root.getBounds(), searchBounds)) {queryInternal(searchBounds, root, matches);}return matches;}然后递归查找子节点
- 如上我们说的,insert的叶子节点是ItemBoundable对象,父节点是AbstractNode对象,根据此可以判断是否到达叶子节点
12345678910111213141516171819private void queryInternal(Object searchBounds, AbstractNode node, List matches) {List childBoundables = node.getChildBoundables();for (int i = 0; i < childBoundables.size(); i++) {Boundable childBoundable = (Boundable) childBoundables.get(i);if (! getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds)) {continue;}if (childBoundable instanceof AbstractNode) {queryInternal(searchBounds, (AbstractNode) childBoundable, matches);}else if (childBoundable instanceof ItemBoundable) {matches.add(((ItemBoundable)childBoundable).getItem());}else {Assert.shouldNeverReachHere();}}}
Reference
- https://www.codedump.info/post/20200609-btree-1/
- https://desktop.arcgis.com/zh-cn/arcmap/10.3/manage-data/using-sql-with-gdbs/the-rtree-index.htm
- Guttman, A.; “R-trees: a dynamic index structure for spatial searching,” ACM, 1984, 14
- STR: a simple and efficient algorithm for R-tree packing
- 本文链接: http://lawlite.me/2022/03/27/R树索引/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议 。转载请注明出处!