四叉树应用于地图(点聚合)、碰撞检测等问题

简介: 四叉树应用于地图(点聚合)、碰撞检测等问题

常用场景


  • 图像处理
  • 空间数据索引
  • 2D 中的快速碰撞检测
  • 稀疏数据


算法场景一:游戏开发碰撞检测

游戏开发中经常有碰撞检测的算法,对于平面上N个图形,如果需要检测互相之间是否发生碰撞。


基本算法:进行N(N-1)次比较,时间复杂度是O(n^2)四叉树算法:时间复杂度是O(nlog n)


image.png


算法场景二:地图上标注的显示逻辑

为了保证规律性和平均分配,采用区域划分方法显示标注。简单来说就是把屏幕分割成若干个区域,每个区域最多显示一个标注,然后根据地图缩放比例动态的设置这些区域的大小以达到最佳的用户体验。


解决:非满四叉树算法如果有100条数据,我们可以嵌套循环找到合适的点放入相应的区域,循环10000次,如果有10000条数据,可能我们的操作系统就要抓狂了。这种计算方法是低效的,时间复杂度至少为O(n^2)。


image.png


实现过程


1)创建四叉树

将数据逐个放入这个树状结构,给每一个节点一个范围。创建过程较为费时,建议另开线程。


2)四叉树查找出需要的标注

首先,我们要理清一个逻辑,如果数据量很大,构建四叉树仍然需要一些时间,但是这个时间是我们允许的,相信你也不会频繁的请求大量标注数据。

关键问题就是在用户拖拽旋转结束的时候,我们需要调整我们界面的显示UI,也就是说,关键问题就是从这个四叉树里面查找出需要的标注,这部分的速度至关重要。

四叉树没有删除的方法,因为删除成本大于重建树结构成本。


主要逻辑:第一步:遍历屏幕划分的区域 第二步:比较该区域是否和四叉树元素范围有交集,无交集则舍弃,有交集继续向下查找

优化:我对查找逻辑做了小优化。在第二步的时候,如果有交集,给是否继续向下查找做了一个开关。理由是,我们查找出所有在该划分区域内的标注,如果目的只是为了算出他们的平均中心点,就显得意义不大,大可查找出几个就停止查找。

聚合分为四个级别:市、区、街道,点(不聚合)


image.png


非满四叉树优化:为每个结点添加一个“容量”的属性,在四叉树初始化时只有一个根结点,在插入数据时,如果一个结点内的数据量大于了结点“容量”,再将结点进行分裂。如此,可以保证每个结点内都存储着数据,避免了内存空间的浪费。


查询:只有找到了位置对应的结点,那么结点下的所有点都会是此位置的附近点,更小的“容量”意味着每个结点内点越少,也就意味着查询的精度会越高。


边界点问题:每个结点内的点必然是相邻的,但相邻的点越不一定在同一个结点内


image.png


特性: 字典树,又称前缀树或trie树,是一种有序树,用于保存关联数组,其中的键通常是字符串。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。

解决: 继续优化四叉树,给结点添加一个“编号”属性。


image.png


代码实现


1)四分区域标识

typedef enum
{
     UR = 0, // UR第一象限
     UL = 1, // UL为第二象限
     LL = 2, // LL为第三象限
     LR = 3  // LR为第四象限
}QuadrantEnum;


2)空间对象数据结构

typedef struct SHPMBRInfo
{
     int nID;       // 空间对象ID号
     MapRect Box;   // 空间对象MBR范围坐标
}SHPMBRInfo;


3)四叉树节点数据结构

typedef struct QuadNode
{
     MapRect Box;           // 节点所代表的矩形区域
     Int nShpCount;         // 节点所包含的所有空间对象个数
     SHPMBRInfo* pShapeObj; // 空间对象指针数组
     Int nChildCount;       // 子节点个数
     QuadNode *children[4]; // 指向节点的四个孩子
}QuadNode;


四叉树原理


四叉树是种很直接的空间索引技术。在四叉树中,每个节点表示覆盖了部分进行索引的空间的边界框,根节点覆盖了整个区域。每个节点要么是叶节点,有包含一个或多个索引点的列表,没有孩子。要么是内部节点,有四个孩子,每个孩子对应将区域沿两根轴对半分得到的四个象限中的一个,四叉树也因此得名。


image.png


四叉树的操作


1.节点分裂

  • 当满足特定条件时,为了获得更好的检索效率,四叉树的节点会发生分裂,分裂的做法是:以当前节点的矩形为中心, 划分出四个等大的矩形,作为四个子节点,每个节点深度=当前节点深度+1,根节点深度为0;并且将当前节点的元素重新插入到对应的子节点。


2.插入元素

  • 平面的元素多种多样,点,线,图形,但都能够做一个统一,第一步都是要找出图形所覆盖的节点,这里以矩形为例
  • 从根节点开始,如果当前节点无子节点,将元素插入到当前节点。如果存在子节点K,并且元素能够完全被子节K点所“包围”,就将元素插入到子节点K,对于子节点K进行上述递归操作;如果元素跨越了多个子节点,那就将元素存储在当前节点
  • 如果某一节点元素超过特定值,并且深度小于四叉树允许的最大深度,分裂当前节点。


3.检索

  • 对于给定检索区域,从根节点起,如果检索区域与当前节点有交集,返回当前节点的所有元素。
  • 如果当前节点还有子节点,递归检索与检索区域有交集的子节点。


业务定义


后台实现聚合算法,App 拿到数据后,直接显示聚合点或非聚合点。具体实现如下:

  • App 传入缩放级别,来获取标注数据,标注数据需要一个字段来标识这些点的聚合级别(市、区、街道,点)。
  • 根据标注数据向地图上添加标注。
  • 地图缩放时,重复步骤 1 和 步骤 2。这里需要注意的是,我们要将地图缩放级别按照聚合级别划分区间,同一区间内避免多次获取数据(同一聚合级别的数据没有变化,或者说变化不大,可以依赖于其他的数据刷新机制,如:再次进入 App)。


源码


主文配套:github.com/indulgeIn/Y…

API 设计合理 + 中文注释:github.com/hadesh/iOS_…


目录
相关文章
|
设计模式 测试技术 编译器
C++项目中打破循环依赖的锁链:实用方法大全(一)
C++项目中打破循环依赖的锁链:实用方法大全
1266 0
|
定位技术
Threejs实现绘制地球,地理位置标注、经纬度转换世界坐标threejs坐标
Threejs实现绘制地球,地理位置标注、经纬度转换世界坐标threejs坐标
2120 0
Threejs实现绘制地球,地理位置标注、经纬度转换世界坐标threejs坐标
|
存储 传感器 自动驾驶
几种常见的点云格式数据解析与在线预览
3D模型在线转换网站支持pcd、pts、xyz、las、laz、asc、ply等点云格式文件在线预览,同时支持将点云格式在线转换为ply、xyz等模型格式。
5808 1
|
Rust IDE NoSQL
Clion2022安装破解与激活教程,亲测可用
CLion是JetBrains公司旗下发布的一款跨平台C/C++/Rust IDE开发工具。
13293 1
|
设计模式 机器学习/深度学习 SQL
软考高级系统架构设计师通关经验分享
为什么考系统架构设计师是国家设立的计算机技术与软件专业技术资格考试(简称软考)中的一个高级科目,属于工程师高级职称系列,具有一定含金量。浙江省每年通过软考高级的人数约为1000+人,其中系统架构设计师科目的通过人数约为200+人。从学习角度来说,通过准备系统架构设计师的考试的过程,可以查漏补缺,并且了解一些系统架构设计相关的基础知识,实现一定程度上的自我提升;从目的性的角度来说,通过考试,可以在一
14649 4
软考高级系统架构设计师通关经验分享
|
算法
飞行员配对方案(Dinic求最大二分图匹配(对匈牙利算法的优化),以及二分图的建图方式)
飞行员配对方案(Dinic求最大二分图匹配(对匈牙利算法的优化),以及二分图的建图方式)
220 0
|
算法 JavaScript 前端开发
在JavaScript中实现基本的碰撞检测算法,我们通常会用到矩形碰撞检测,也就是AABB(Axis-Aligned Bounding Box)碰撞检测
【6月更文挑战第16天】JavaScript中的基本碰撞检测涉及AABB(轴对齐边界框)方法,常用于2D游戏。`Rectangle`类定义了矩形的属性,并包含一个`collidesWith`方法,通过比较边界来检测碰撞。若两矩形无重叠部分,四个条件(关于边界相对位置)均需满足。此基础算法适用于简单场景,复杂情况可能需采用更高级的检测技术或物理引擎库。
301 6
|
10月前
|
前端开发 JavaScript API
(前端3D模型开发)网页三维CAD中加载和保存STEP模型
本文介绍了如何使用`mxcad3d`库在网页上实现STEP格式三维模型的导入与导出。首先,通过官方教程搭建基本项目环境,了解核心对象如MxCAD3DObject、Mx3dDbDocument等的使用方法。接着,编写了加载和保存STEP模型的具体代码,包括HTML界面设计和TypeScript逻辑实现。最后,通过运行项目验证功能,展示了从模型加载到保存的全过程。此外,`mxcad3d`还支持多种其他格式的三维模型文件操作。
774 126
|
存储 编解码 算法
栅格数据矢量化(附有完整代码)
栅格数据矢量化(附有完整代码)
|
11月前
|
前端开发 Android开发 iOS开发
探索Android与iOS的跨平台开发策略
在移动应用开发的多元化时代,跨平台开发已成为开发者追求效率和广泛覆盖的重要手段。本文深入探讨了Android与iOS两大主流平台下的跨平台开发策略,分析了各自的优势与挑战,并通过实际案例展示了如何有效实施跨平台解决方案,以期为开发者提供有价值的参考和启示。