当我们需要打车或者骑共享单车回家时,打开手机APP,会看到如下的界面:
共享单车中给定一定距离的车辆,来源:焉知汽车
在应用程序界面中,用户可以便捷地查看以自己为中心点、在特定半径范围内的可用出租车或共享单车。为了在地图上精确地展示这些交通工具,一个直接的方法是从数据库检索相关信息:即计算并查询所有与用户距离在5公里以内的车辆,再将这些筛选后的数据传输回客户端进行展示。
然而,这种方法在实际操作中并不高效,尤其是当数据量庞大时。原因在于,它需要对数据库中的每一条记录进行距离计算,这无疑会消耗大量的时间和计算资源。
而日常生活中,面对诸如计算滴滴出行或共享单车的行程距离、寻找最近的超市、菜市场、公共厕所等实际场景,我们应该选择哪种空间索引技术呢?
本文将为您介绍四种常用的空间索引方法,并探讨它们各自的适用场合。
一、四叉树
四叉树(Quadtree)是一种空间索引树,四叉树的每一个节点都代表着一块矩形区域。我们知道在平面直角坐标系中,平面可以被分为第一二三四象限,四叉树的每一个节点也类似,可以分裂为四个子节点,子节点在满足条件的情况下可以继续分裂,这样构成了一个四元的树状结构,就是四叉树。
四叉树索引的基本思想是将空间递归划分为不同层次的树结构。它将已知范围的空间等分成四个相等的子空间,如此递归下去,直至树的层次达到一定深度或者满足某种要求后停止分割。
四叉树结构图
经过这样分割之后,每进行一次判断,整个空间中3/4不符合需求的几何对象就会被直接排除,从而大大提高了计算机的运行效率。
#应用场景
- 地形渲染
在游戏开发中,地形渲染扮演着重要的角色,不仅提供了视觉上的环境背景,还直接影响着游戏的沉浸感和交互性。
渲染地形时,如果采用固定地形精度等级,则会随着地形面积的增大,渲染的三角形数量会随着面积的增大而增长。现代游戏引擎更多的采用基于四叉树的地形表达,可把地形不在视野内的网格去掉,有效降低程序的时间复杂度。
地形的四叉树结构,来源:GAMES104
2018年GDC(游戏开发者大会)上,游戏制作商Ubisoft(育碧)介绍了旗下热门3A游戏《FARCRY5(孤岛惊魂5)》的地形渲染流程。游戏内地形网格的划分正是采取了四叉树空间结构,按照每2千米的贴图细分为一个四叉树层级。下图中第四张图能看到,红色的是玩家所处的地块位置,随着玩家的走动颜色会一直变化,地块则会根据玩家距离,进而改变细分的精度。
随玩家位置改变而改变的四叉细分规则,来源:《孤岛惊魂5》技术分享
- 二维地图渲染
互联网地图采用墨卡托投影,把地图搞成了一个正方形,长宽是等值的,这正是为了瓦片分级切割方案服务的,而这个切割方案则是基于四叉树索引。
在这颗四叉树中,每一个节点都是一张地图瓦片,都有唯一的标识,通过标识可以快速找到它的父节点,子节点,相邻的节点等等,我们也可以通过标识快速计算出瓦片的空间坐标。
当做移动或者缩放地图这种操作的时候,查询的区域发生了变化,根据新的区域范围以及当前地图等级计算出所需要的四叉树节点。
这个查询相对于其他的空间查询要简单得多,因为地图等级是不变的,也就是说该查询是锁定了四叉树的level,并不需要进行递归,效率也要高很多。
二、R树
R树(R-tree)是一种平衡树数据结构,用于存储空间数据,如多维对象的边界框。这意味着所有叶节点都在同一层级上,从而保证了查询效率。因此广泛应用于地理信息系统(GIS)、数据库索引、以及其他需要高效执行空间查询的领域。R树通过将对象组织到相互重叠的最小边界矩形(Minimum Bounding Rectangles, MBRs)中,来优化范围查询、最近邻查询等操作。
#应用场景
- 最近邻查询
R树可以用来存储地图上的空间信息,例如餐馆地址,或者地图上用来构造街道,建筑,湖泊边缘和海岸线的多边形。然后可以用它来回答“查找距离我2千米以内的博物馆”,“检索距离我2千米以内的所有路段”(然后显示在导航系统中)或者“查找(直线距离)最近的加油站”这类问题。
在这里直接借鉴Franz Yang大佬,简单实现基于geopandasR树空间索引查找最近到路线的场景:
通过调用geopandas内置的R树空间索引算法,可以根据点位匹配最近到路线:
利用geopandas实现查找最近道路线,来源:Franz Yang
可以看到匹配基本成功。处理约30000个点仅需约90秒,也就是一个点需要3ms,相比之下,采用原始的遍历筛选方法处理一个点需要30000ms。因此,通过 R 树对路网进行空间索引的方法将地图匹配的速度提升了多个数量级。
三、Goole S2
Google’s S2 library is a real treasure, not only due to its capabilities for spatial indexing but also because it is a library that was released more than 4 years ago and it didn’t get the attention it deserved.
上面这段话来自2015年一位谷歌工程师的博文。他由衷的感叹 S2 算法发布4年没有得到它应有的赞赏。不过现在 S2 已经被各大公司使用了。
在介绍这个重量级算法之前,先解释一些这个算法的名字由来。S2其实是来自几何数学中的一个数学符号 S²,它表示的是单位球。S2 这个库其实是被设计用来解决球面上各种几何问题的。
在 S2 中,每个剖分出的空间范围被称为单元,每个单元是由四条大地线构成的四边形。S2 的最顶层通过将立方体的六个面投影到单位球体上获得,而其他层则通过递归地将每个单元细分为四个子单元生成。
S2的每个单元都由 64 位 S2单元编码唯一标识。S2 单元以特殊方式编号,以便在用于空间索引时最大化引用局部性(与其他单元编号方法相比)。具体而言,S2 单元沿着空间填充曲线(一种分形)按顺序排列。S2 使用的特定曲线称为 S2 空间填充曲线,由六条希尔伯特曲线组成,这些曲线相互连接,形成覆盖整个球体的单个连续循环。以下是经过 5 级细分后的 S2 曲线的图示:
S2 空间填充曲线,来源:未铭流
黄色曲线是近似的 S2 空间填充曲线,它是一个具有连续重复结构的分形图形,其可以无限细分,甚至细分到可以经过球表面上的任意一点。绿线显示 S2 单元在 0、1、2、3、4 和 5 级的边界,以不同宽度的线条绘制。每一级上的单元编号沿着该曲线递增,这就使得S2单元编码具有以下属性:如果两个单元的 S2编码靠得很近,那么这些单元在空间上也靠得很近。当单元用于索引时,此属性可显著改善引用局部性。
#应用场景
- 派单系统
S2可以根据形状圈出范围。如果你想画一个以伦敦为中心,半径为1公里的圆,S2可以告诉你需要哪些单元格来完全覆盖这个形状。Uber正是采用了该算法实现了车辆派单系统,系统会根据乘客的GPS数据,使用地图圆形区域包含的所有单元ID,找出覆盖的车辆。然后根据乘客的需求过滤无效车辆,算出能最快接到乘客的车子进行派单。
S2索引快速定位,来源:Learning to Rank for Spatiotemporal Search
- 全球瓦片加载
通常,传统的 GIS 会把地球椭球体投影到某个平面或可展成平面的曲面(圆锥、圆柱)上。例如,墨卡托投影就是投影到圆柱体上。墨卡托有一个众所周知的问题,那就是离极点越近,形变越大。
S2 这种投射方式就改善了墨卡托的问题,并且还有其它的优点,它把地表面分割成没有奇怪端点、形变较小、瓦片相对来说几乎相等的瓦片。
基于上述特征,使用 S2 定义的这套瓦片层次结构的 3D Tiles Next 扩展就特别合适跨越全球范围的瓦片数据集。
以下例子为 3DTILES_bounding_volume_S2 表达 S2 的 6 个根单元网格,可以很明显看出它能均匀覆盖全球:
3DTILES_bounding_volume_S2 表达 S2 的 6 个根单元网格,来源:岭南灯火
四、BSP树
BSP树(Binary Space Partioning Tree,二叉空间分区树)产生于计算机图形学需要快速绘制由多边形组成的三维场景。绘制此类场景的一种简单方法是画家算法,该算法依据与观察者的距离,按照从后到前的顺序生成多边形。这种方法有两个缺点:按从后到前的顺序对多边形进行排序所需的时间较长,以及可能存在多边形重叠问题。在使用画家算法时,构建BSP树通过提供一种根据给定视点对多边形进行排序的快速方法,并通过细分重叠多边形可以避免这两个问题。
二叉空间分割树(BSP tree)的生成过程,来源:WIKI
#应用场景
- 游戏场景管理
游戏场景管理包含了关卡、几何图形、天气数据、材质分配、AI 寻路信息、光照贴图和其他数据结构。BSP树为游戏场景提供了一种良好的层次关系,便于进行筛选(Culling)和隐藏面消除(Hidden Surface Removal)。这对于大型游戏场景尤为重要,可以显著提高渲染性能。BSP广泛应用于游戏室内场景的处理中,它使用一个分离面(splitting plane)对每一层一分为二,从而实现对空间的划分,其中用于分割的平面可以出现在任何方位。
Blood Gulch 的 BSP 在Sapien中的结构可视化效果,来源:c20
BSP可以说是八叉树的一般化。前人在这方面已经做了很多有效的工作,与八叉树剖分相比,BSP树具有内存耗费小、剖分方式灵活、产生的无效区域较小的优点;且对大部分场景来说,BSP树较八叉树来说更为平衡。回溯至 1993 年,游戏开发领域的先驱 id Software 发行了一款具有里程碑意义的第一人称射击游戏 ——《毁灭战士 DOOM》。值得一提的是,BSP 技术在该游戏中首次得到应用,并极大地推动了《毁灭战士》游戏引擎的运行速度提升。在此之前,BSP 技术尚未在电子游戏领域崭露头角,而《毁灭战士 DOOM》的成功无疑为该技术在游戏行业的广泛应用开辟了道路,也让更多人认识到其在优化游戏性能方面的巨大潜力和价值。
1993年《毁灭战士》游戏首次采用BSP树作为场景管理的空间结构索引,来源:《毁灭战士》游戏截图
通过对这四种空间索引技术的深入探索,我们不难发现,每一种索引都在其特定的应用场景中发挥着不可替代的作用。
作为数字孪生应用开发工具,Mapmost SDK for WebGL中最常见的则是四叉树索引,基于四叉树的地形数据不仅可以提供真实的地貌信息,还可以支持三维空间分析和模拟,如地形开挖分析等。
地形开挖分析效果,来源:Mapmost SDK for WebGL
此外,二维矢量、栅格图层也是按照四叉树索引的规则去切分瓦片,前端按照规则,计算好瓦片行列号后就可以发送瓦片服务获取瓦片信息,最后完成渲染。
Mapmost中基于四叉树索引的栅格图层加载,来源:Mapmost SDK for WebGL
从地形渲染到地图查询,从最近邻搜索到全球瓦片加载,这些空间索引技术不仅优化了数据检索效率,更是推动了相关领域的发展和创新。希望本文能为您在理解和应用空间索引技术方面提供一些有益的参考和启示。
关于Mapmost SDK for WebGL对于空间索引的更多应用,点击此处前往Mapmost官网体验!