You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
首先我们假设所有数据都是二维空间下的点,我们从图中这个R8区域说起,也就是那个shape of data object。别把那一块不规则图形看成一个数据,我们把它看作是多个数据围成的一个区域。为了实现R树结构,我们用一个最小边界矩形恰好框住这个不规则区域,这样,我们就构造出了一个区域:R8。R8的特点很明显,就是正正好好框住所有在此区域中的数据。其他实线包围住的区域,如R9,R10,R12等都是同样的道理。这样一来,我们一共得到了12个最最基本的最小矩形。这些矩形都将被存储在子结点中。
前言
亲爱的coder们,我又来了,一个喜欢图形的程序员👩💻,前几篇文章一直都在教大家怎么画地图、画折线图、画烟花🎆,难道图形就是这样嘛,当然不是,一个很简单的问题, 如果我在canvas中画了10万个点,鼠标在画布上移动,靠近哪一个点,哪一个点高亮。有同学就说遇事不决 用for循环遍历哇,我也知道可以用循环解决哇,循环解决几百个点可以,如果是几万甚至几百万个点你还循环,你想让用户等死?这时就引入今天的主角他来了就是Rbush
rbush
我们先看下定义,这个rbush到底能帮我们解决了什么问题?
看定义他是基于优化的R-tree数据结构,那么R-tree又是什么呢?
R-tree的关键思想是将附近的对象分组,并在树的下一个更高级别中用它们的最小边界矩形表示它们;R-tree 中的“R”代表矩形。由于所有对象都位于此边界矩形内,因此不与边界矩形相交的查询也不能与任何包含的对象相交。在叶级,每个矩形描述一个对象;在更高级别,聚合包括越来越多的对象。这也可以看作是对数据集的越来越粗略的近似。说着有点抽象,还是看一张图:
我来详细解释下这张图:
算法
插入
为了插入一个对象,树从根节点递归遍历。在每一步,检查当前目录节点中的所有矩形,并使用启发式方法选择候选者,例如选择需要最少放大的矩形。搜索然后下降到这个页面,直到到达叶节点。如果叶节点已满,则必须在插入之前对其进行拆分。同样,由于穷举搜索成本太高,因此采用启发式方法将节点一分为二。将新创建的节点添加到上一层,这一层可以再次溢出,并且这些溢出可以向上传播到根节点;当这个节点也溢出时,会创建一个新的根节点并且树的高度增加。
搜索
在范围搜索中,输入是一个搜索矩形(查询框)。搜索从树的根节点开始。每个内部节点包含一组矩形和指向相应子节点的指针,每个叶节点包含空间对象的矩形(指向某个空间对象的指针可以在那里)。对于节点中的每个矩形,必须确定它是否与搜索矩形重叠。如果是,则还必须搜索相应的子节点。以递归方式进行搜索,直到遍历所有重叠节点。当到达叶节点时,将针对搜索矩形测试包含的边界框(矩形),如果它们位于搜索矩形内,则将它们的对象(如果有)放入结果集中。
读着就复杂,但是社区里肯定有大佬替我们封装好了,就不用自己再去手写了,写了写估计不一定对哈哈哈。
RBUSH 用法
用法
创建一个树🌲
后面的16 是一个可选项,RBush 的一个可选参数定义了树节点中的最大条目数。 9(默认使用)是大多数应用程序的合理选择。 更高的值意味着更快的插入和更慢的搜索,反之亦然。
插入数据📚
删除数据📚
默认情况下,RBush按引用移除对象。但是,您可以传递一个自定义的
equals
函数,以便按删除值进行比较,当您只有需要删除的对象的副本时(例如,从服务器加载),这很有用:删除所有数据
搜索🔍
api 介绍完毕下面👇开始进入实战环节一个简单的小案例——canvas中画布搜索🔍的。
用图片填充画布
填充画布的的过程中,这里和大家介绍一个canvas点的api ——createPattern
第一个参数是填充画布的数据源可以是下面这:
HTMLImageElement
HTMLVideoElement
HTMLCanvasElement
CanvasRenderingContext2D
,ImageBitmap
,ImageData
,Blob
.第二个参数指定如何重复图像。允许的值有:
"repeat"
(both directions),"repeat-x"
(horizontal only),"repeat-y"
(vertical only),"no-repeat"
(neither).如果为空字符串 (
''
) 或null
(但不是undefined
),repetition将被当作"repeat"。代码如下:
这边有个小提醒的就是图片加载成功的回调里面去给画布创建模式,然后就是this 指向问题, 最后就是填充画布。
如图:
数据的生成
数据生成主要在画布的宽度 和长度的范围内随机生成10万个矩形。插入到rbush数据的格式就是有minX、maxX、minY、maxY。这个实现的思路也是非常的简单哇, minX用画布的长度Math.random minY 就是画布的高度Math.random. 然后最大再此基础上随机*20 就OK了,一个矩形就形成了。这个实现的原理就是左上和右下两个点可以形成一个矩形。代码如下:
然后循环加入10万条数据:
画布填充
这里我创建一个和当前画布一抹一样的canvas,但是里面画了n个矩形,将这个画布 当做图片填充到原先的画布中。
然后在加载数据的时候,在当前画布画了10000个矩形。这时候新建的画布有东西了,然后我们用一个drawImage api ,
这个api做了这样的一个事,就是将画布用特定资源填充,然后你可以改变位置,后面有参数可以修改,这里我就不多介绍了, 传送门
我们看下效果:
添加交互
添加交互, 就是对画布添加mouseMove 事件, 然后呢我们以鼠标的位置,形成一个搜索的数据,然后我在统计花费的时间,然后你就会发现,这个Rbush 是真的快。代码如下:
这里给大家讲解一下,现在我们画布是黑白的, 然后以鼠标搜索到数据后,然后我们画出对应的矩形,这时候呢,可以将矩形的填充模式改成 pattern 模式,这样便于我们看的更加明显。fillStyle可以填充3种类型:
分别代表的是:
OK讲解完毕, 直接gif 看在1万个矩形的搜索中Rbush的表现怎么样。
这是1万个矩形我换成10万个矩形我们在看看效果:
我们发现增加到10万个矩形,速度还是非常快的,增加到100万个矩形,canvas 已经有点画不出来了,整个页面已经卡顿了,这边涉及到canvas的性能问题,当图形的数量过多,或者数量过大的时候,fps会大幅度下降的。
总结
最后总结下:rbush 是一种空间索引搜索🔍算法,当你涉及到空间几何搜索的时候,尤其在地图场景下,因为Rbush 实现的原理是比较搜索物体的boundingBox 和已知的boundingBox 求交集, 如果不相交,那么在树的遍历过程中就已经过滤掉了。最后文章写作不易,如果有错误的话欢迎指正。如果看了对你有帮助的话,希望你能为我点个关注 和👍, 这是对我最大的支持!
学习交流
搜索公众号【前端图形】,后台回复"加群"二字, 就可以加入可视化学习交流群哦! 一起学习吧!
参考文献
深入理解空间算法
R树详细解释
维基百科-R树的介绍
Alex2wong
The text was updated successfully, but these errors were encountered: