Cesium聚簇实现-kdbush原理

32 篇文章 1 订阅
12 篇文章 0 订阅


  上一篇文章通过调试发现Cesium实现点聚簇过程中一个bug,从中猜测其实现聚簇核心代码在kdbush类中,本文首先讲清楚KDBUSH原理,下一篇再展开kdbush类查看它如何实现。

问题说明

  假设二维平面中有10个点,分别为ABCDEFGHIJ,如下图所示,图中标识了其中每个点的坐标,那么对于任意点P,如何求得落入P周围一定范围内所有点?如下图所示,假设P点坐标为(-4,3):
在这里插入图片描述

  在GIS应用中经常使用的二维范围查询为圆和矩形,如果我们知道圆中心和圆半径,或者矩形中心和长宽,则可以唯一确定一个查询范围。
  传统方法是对于所有已知点进行轮询计算该点是否落入查询范围内,比如若查询范围是以P点为中心,一定半径的圆,则判断每个点与P点的距离是否小于等于半径;若查询范围是以P点为中心,长宽固定的矩形,则判断每个点是否同时满足:与P点X轴方向坐标差绝对值小于等于矩形X方向长度,与P点Y轴方向坐标差绝对值小于等于矩形Y方向长度,同时满足以上两个条件则表示在查询范围内,否则不在查询范围(此处假设查询范围矩形的长宽是直角坐标系的XY轴方向,进行旋转的暂不考虑)。

  但是若待查询点集合个数达到万或者以上,以上传统方法时间复杂度为N,查询时间线性增长,不满足快速响应的需求,比如之前使用的聚簇效果,需要每次刷新页面都重新计算聚簇集(聚簇集和查找一定范围的点的关系在后面的博客中再说明),这样会让用户明显感受到地图刷新卡顿。所以需要提高查询效率,而KDbush库解决了这个问题。

KDbush库的分块重排序算法说明

对上述点按照下图所示进行分块:
在这里插入图片描述
此处假定搜索范围矩形为上图所示橘黄色方框范围。

分块的方法:
1.将10个点按照X坐标轴从小到大排列,然后从中间一分为二,此处中间值X=-6.5,所有点x方向小于这个值的有4个点,大于这个值的有5个点,等于它的有一个点;
2.分别对刚才分出来的两个范围X<-6.5和X>-6.5进行再次分类,这次都按照Y坐标轴从小到大排列,取中间值,比如在X<-6.5的范围中,得到Y=-5分割线,Y值小于-5的有一个点,Y值大于-5的有两个点,Y等于-5的有一个点,在X>-6.5的范围中,得到Y=1分割线,Y<1的有两个点,Y>1的有两个点,Y=1的一个点;
3.重复以上步骤,直到分出来的范围中没有点为止(注意每次分块的依据坐标轴是X轴和Y轴交替变化)。

综上所述,这是一个二叉树,算法时间复杂度从N降低到LOGn;
以上例子的二叉树示意图如下所示:
在这里插入图片描述
注意:以上步骤全部是在第一次加载所有点时一次性计算完成,也就是在查询操作之前先对数据进行一个重新组织的过程,而在查询就可以直接取数据。而这个重新组织过程比较快,且不是需要实时响应的操作中,则不会让用户感受到明显的卡顿(因为用户通常在查询时要求实时响应,比如每次刷新地图都要重新聚簇,而在加载数据可以允许相对较长等待时间)。

由此得到根据搜索目标集合域进行分块重排序后的数组:JIHGFEDCBA
该新数组的特点:
10个点索引0-9,中间点索引4,对应的点为F,该点左边的四个点JIHG的X坐标值都小于该点X坐标值,右边五个点EDCBA的X坐标值都大于该点X坐标值;
0-3索引中间点索引1对应点I,该点左边1个点J的Y坐标值小于该点Y坐标,右边两个点HG的Y坐标值都大于该点Y坐标值;
5-9索引中间点索引7对应点C,该点左边两个点ED的Y坐标值都小于该点Y坐标,右边两个点BA的Y坐标值都大于该点Y坐标值;
0-1索引中间点索引0对应点J,该点没有左边点也没有右边点,到达叶子节点;
2-3索引中间点索引2对应点H,该点没有左边点,有一个右边点G,其X坐标值大于H点X坐标值,G点到达叶子节点;
5-6索引中间点索引5对应点E,该点没有左边点,有一个右边点D,其X坐标值大于E点X坐标值,D点到达叶子节点;
7-8索引中间点索引7对应点B,该点没有左边点,有一个右边点A,其X坐标值大于B点X坐标值,A点到达叶子节点。

KDbush库的查找范围点算法说明

矩形框范围查找

已知矩形框minx,maxx,miny,maxy,仍然以示意图为准:
1.找到数组中间索引值对应点的坐标,本例中为F点坐标(-6.5,0.5),查看其是否在搜索框内,即x>=minx且x<=maxx且y>=miny且y<=maxy,若在其内,则往结果集中添加该点,本例中在方框内,则往结果集中添加F点;
2.若当前是X轴二分,则判断当前中间点的X是否大于minx,若是则把当前点的左边所有点加入下一迭代搜索范围,判断当前中间点的X是否小于maxx,若是则把当前点的右边所有点加入下一迭代搜索范围;若当前是Y轴二分,则判断当前中间点的Y是否大于miny,若是则把当前点的左边所有点加入下一迭代搜索范围,判断当前中间点的Y是否小于maxy,若是则把当前点的右边所有点加入下一迭代搜索范围;
3.使用上一步骤中搜索到的多个范围挨个作为数组搜索集合范围重新进入第一步骤,重新迭代,每重新进入第一步骤迭代,搜索依据坐标轴就进行切换一次。

  比如在上例中,第一次迭代加入F点,然后将左侧和右侧范围都加入搜索范围,进入第二次迭代,左侧范围的中间值是I,右侧范围的中间值是C,C点加入结果集,I点上部分范围加入搜索集,C点上下两部分都加入搜索集,进入第三次迭代,I点上部分范围中间值是H,C点上部分中间值是B,下部分中间值E,HBE三点中没有可以加入结果集的点,H点右侧点加入搜索集,B点左侧点加入搜索集,E点左右两侧点都加入搜索集,进入第四次迭代,H点右侧点范围中间值是G,B点左侧没有点,E点左侧没有点,E点右侧中间点是D,GD两点中没有可以加入结果集的点,G点下半部分加入搜索集,D点上半部分加入搜索集,进入第五次迭代,G点下半部分没有点,D点上半部分没有点,搜索结束,退出循环。
得到搜索集:FC

圆形范围查找

  圆形范围查找整体算法与矩形框搜索完全一样,区别是判断是否加入结果集的条件,圆形是点距离中心点直线距离小于半径。
  需要说明的是:圆形范围查找在整体迭代确定搜索集合时,也是将其看作一个矩形的,该矩形的长宽相同,只是最终确定是否加入结果集时判断条件不同而已。

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值