R-Tree原理及朴素实现代码

R树是用于空间访问方法的树数据结构,即用于索引多维信息,例如地理坐标、矩形或多边形。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 

1、空间数据

如果一个对象至少具有一个捕获其在 2D 或 3D 空间中的位置的属性,则该对象被表征为空间对象。 此外,空间物体可能在空间中具有几何范围。 例如,我们可以说建筑物是一个空间对象,因为它在 2D 或 3D 地图中具有位置和几何范围。

在多维空间中不存在保持空间邻近性的全序。 因此,空间中的对象无法以理论上为空间查询提供最佳性能范围的方式物理集群到磁盘页面。

假设我们尝试使用某些一维排序键对一组二维点进行排序。 无论我们使用哪个键,按皮亚诺曲线或希尔伯特排序的列排序,我们都不能保证任何在空间上接近的对象对在总顺序上也将接近。

R 树正在绘制我们的世界,这是一个现实世界的应用程序。 它们存储空间数据对象,例如商店位置和创建地图的所有其他形状。 例如,从城市街区或您最喜欢的餐厅创建多边形的地理坐标。

为什么这个有用?

嘿,Siri! 找到离我最近的麦当劳

是的,我们需要根据对象的实际位置进行查询。

2、R树中的“R”代表矩形

数据结构的关键思想是将附近的对象分组并用矩形表示它们。 最小外接矩形或简称 MBR。 这是递归发生的:

由于所有对象都位于此边界矩形内,因此不与边界矩形相交的查询也无法与任何包含的对象相交。 在叶级别,每个矩形描述一个空间对象。 所有其他级别都只是节点指针。

该树的一个约束是每个节点(根节点除外)应至少为 40%,以便正确利用磁盘空间。 下面我们可以看到 R 树的示例。

为了保持 R 树的利用率,我们必须做四件事:

  • 目录节点条目的 MBR 所覆盖的区域应最小化。
  • 同一级别目录项的 MBR 之间的重叠应最小化。
  • 目录节点条目的 MBR 的边距应最小化。
  • R 树节点中的平均条目数应最大化。

3、批量加载

批量加载(bulk loading)是一种通常以“大块”的方式将数据加载到数据库中的方法。 我们将使用一种称为排序平铺递归 (STR:Sort Tile Recursive) 的方法。

每个 MBR 包含以下由制表符 (‘\t’) 分隔的内容: object-id、 x-low、 x-high、 y-low、 y-high。 下面我们可以看到一个计算矩形列表的 MBR 的函数。 每个矩形都存储在一个字符串中:

def find_mbr(slc):# convert the string that contains the rectangles info into a list of listsa = [item.split() for item in slc]# itemgetter(0) has the idx_low = min(a, key=operator.itemgetter(1))[1]x_max = max(a, key=operator.itemgetter(2))[2]y_low = min(a, key=operator.itemgetter(3))[3]y_max = max(a, key=operator.itemgetter(4))[4]return [x_low, x_max, y_low, y_max]

STR 技术首先根据矩形的 x-low 对 n 个矩形进行排序。 经过排序,我们知道树中会有 L = ⌈n/C⌉ 的叶子节点。 属性C指的是每个节点的容量。 具体来说它可以存储多少个矩形:

def construct(rectangle_file, node_array, counter_left_of):global tree_levelglobal node_counter# sort according to the second column (x-low).data = sorted(rectangle_file, key=lambda line: line.split()[1])number_of_data_rectangles = len(data)# assume each rectangle needs 36 bytes and each node can hold up to 1200 bytes.node_can_hold = math.floor(1200 / 36)number_of_nodes = math.ceil(number_of_data_rectangles / node_can_hold)number_of_slices = math.ceil(math.sqrt(number_of_nodes))

然后,(已排序的)矩形被分为 ⌈SquareRoot( L)⌉ 组(垂直条纹),并且使用矩形的 y-low 作为键对每个组进行排序。 这种排序的输出被打包到叶节点中,并且树是通过递归调用以自下而上的方式构建的。

if number_of_slices > 1:# create the slicesslices = list(divide_chunks(data, node_can_hold*number_of_slices))# sort every slice according to the fourth column (y-low)sorted_slices = [sorted(item, key=lambda line: line.split()[3]) for item in slices]# merges the slices into a single listconcat_list = [j for i in sorted_slices for j in i]# divide them again in chunks that a node can holdslices = list(divide_chunks(concat_list, node_can_hold))# find the MBR of each slicembr = [find_mbr(item) for item in slices]# this counter is passed to the upper recursion level so the# next level nodes will know where to point.counter_left_of = node_counter# append the nodes in the node arrayfor slc in slices:node_array.append(Node(node_counter, len(slc), slc, tree_level))node_counter += 1node_ids = [i for i in range(counter_left_of, node_counter)]# create the chunks that are needed for the upper level, mbrs of every slice and where# they will pointdata_for_the_upper = [str(a) + '\t' + str(b[0]) + '\t' + str(b[1]) + '\t' + str(b[2]) + '\t' + str(b[3]) fora, b in zip(node_ids, mbr)]tree_level += 1number_of_nodes_per_level.append(number_of_nodes)average_MBR_area_per_level.append(get_area(data)/len(data))# recursively construct from bottom to topconstruct(data_for_the_upper, node_array, counter_left_of)

当切片数为 1 时,递归结束。这意味着我们到达了根级别:

# recursion is over, we reached top node
else:# sort  according to the fourth column (y-low)data = sorted(data, key=lambda line: line.split()[4])node_array.append(Node(node_counter, len(data), data, tree_level))node_counter += 1

4、让我们查询树

遵循与查询范围相交的条目指针,以深度优先的方式遍历树。 查询算法采用三个参数; 查询范围q,检索对象应满足q的谓词θ,以及R树节点n。

如果我们位于叶节点上,我们会搜索该节点的每个 MBR 以检查它们是否满足我们的查询。 如果是的话,我们将矩形添加到答案集中。

如果我们不在叶节点上,我们将搜索该节点的每个 MBR 以检查它们是否满足我们的查询。 如果确实如此,我们将递归调用该函数。 我们使用相同的参数。 唯一改变的参数是节点。 我们现在正在传递当前节点所指向的节点。

简单来说,我们要检查是否与查询的 MBR 存在交集。

我们希望能够执行 3 种查询类型。 交集,对于至少有一个公共点的 MBR,内部,对于位于查询内部的 MBR,以及包含,对于包含查询的 MBR 的 MBR。

def query(rectangles_answer_set, rectangle_of_the_query, node_array, node_from_recursion, first_call, nodes_accessed,q_type):# hacky way to count the accesses with a mutable type passing with reference, globals would not work herenodes_accessed.append('1')if first_call:# the first call gets the root nodenode_to_check = node_array[-1]else:node_to_check = node_from_recursionif node_to_check.tree_level == 1:for mbr in node_to_check.list_of_attrs:mbr_splitted = mbr.split()if query_type(rectangle_of_the_query, mbr_splitted, q_type):rectangles_answer_set.append(mbr)else:for mbr in node_to_check.list_of_attrs:mbr_splitted = mbr.split()points_to = mbr_splitted[0]if query_type(rectangle_of_the_query, mbr_splitted, 'intersection'):query(rectangles_answer_set, rectangle_of_the_query, node_array, node_array[int(points_to)], False,nodes_accessed, q_type)

每个非叶节点必须仅检查交集。 我们可以通过下面的例子看到,如果我们严格过滤非叶节点上的所有 MBR,我们会错过很多结果。

考虑内部查询和非叶节点处的步骤。 黑色矩形是查询的 MBR。 红色是被过滤的中间节点,绿色是实际对象。 如果在递归中我们有严格的规则要求 MBR 完全位于内部进行检查,则红色将不会通过过滤器,因此我们将失去绿色的 MBR,因为它是符合标准的实际对象:

接受对象的标准取决于其几何形状,如下所示:

# At least one  point intersecting.
if q_type == 'intersection':x_axis = (query_x_high >= mbr_x_low) or (query_x_high <= mbr_x_high)y_axis = (query_y_low <= mbr_y_high) or (query_y_high <= mbr_y_low)return x_axis and y_axis# The MBR of the rectangle we examine must be inside of the MBR of the query.
elif q_type == 'inside':x_axis = (query_x_high >= mbr_x_high) and (query_x_low <= mbr_x_low)y_axis = (query_y_low <= mbr_y_low) and (query_y_high >= mbr_y_high)return x_axis and y_axis# Exactly the opposite from the previous. We can change the positions in the comparisons.
elif q_type == 'containment':x_axis = (mbr_x_high >= query_x_high) and (mbr_x_low <= query_x_low)y_axis = (mbr_y_low <= query_y_low) and (mbr_y_high >= query_y_high)return x_axis and y_axis

5、结束语

空间数据库系统中处理空间选择的方式取决于所查询的关系 R 是否已索引。 如果 R 没有索引,我们线性扫描它并将每个对象与查询范围进行比较。 正如所讨论的,DBMS 应用了两步处理技术,该技术在对象的精确几何形状之前针对查询测试对象的 MBR。 如果关系被索引(例如,通过 R 树),那么我们可以使用它来快速查找符合过滤步骤的对象。


原文链接:R-Tree原理及朴素实现 - BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/305850.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用ROCm的HIP API向量加法程序

一、向量加法程序 Radeon Open Compute (ROCm) 是一个开源平台&#xff0c;用于加速高性能计算 (HPC) 和机器学习应用程序。它支持包括GPUs在内的多种硬件&#xff0c;并提供HIP (Heterogeneous-compute Interface for Portability) 作为CUDA代码的便捷转换工具。为了提供一个…

蓝桥杯算法题:栈(Stack)

这道题考的是递推动态规划&#xff0c;可能不是很难&#xff0c;不过这是自己第一次靠自己想出状态转移方程&#xff0c;所以纪念一下&#xff1a; 要做这些题目&#xff0c;首先要把题目中会出现什么状态给找出来&#xff0c;然后想想他们的状态可以通过什么操作转移&#xf…

关闭笔记本自带的键盘

目录 一、问题 二、方法 【方法一】 【方法二】 一、问题 笔记本自带的键盘上的个别按键又坏了&#xff0c;可能是因为使用电脑时&#xff0c;最先坏的几个按键那里温度比较高&#xff0c;久而久之就烧坏了吧。距离上次更换新键盘才差不多一年&#xff0c;所以不打算再买新…

基于arcgis /envi PCA(主成分分析)实现过程

基于arcgis /envi PCA(主成分分析)实现过程 1 提取研究范围 2对研究范围进行重采样 &#xff08;根据数据情况进行选做&#xff0c;如数据较大建议进行该步骤操作&#xff09; 3 对研究范围内数据进行归一化处理 4 将空值替换为0 5 对同期不同要素数据进行波段合成 对波段…

python pivot_table功能详解与应用 -- 实现Excel的透视表功能

1. 背景描述 透视表是一种能对多维数据进行分析统计的工具&#xff0c;具有筛选处理、分类汇总&#xff0c;优化显示等强大的功能&#xff0c;是Excel中最好用的数据分析工具之一。 在自动化办公中&#xff0c;使用python的pivot_table()&#xff0c;搭配合适的聚合函数&#x…

【linux篇】ubuntu安装教程

有道是工欲善其事必先利其器&#xff0c;在学习linux前&#xff0c;先得搭建好环境才能事半功倍。 1.VMware虚拟机安装 打开浏览器&#xff0c;可直接在搜索栏中输入VMware。

【C#】 删除首/尾部字符

代码 static void Main(string[] args){string str "123abc";string strdelete "abc";string str1 str.Trim(1);string strc str1.Trim(c);string str11 str1.TrimStart(1);string strcc str1.TrimEnd(c);string strabc str.Trim(strdelete.ToCharA…

每天学点儿Python(5) -- 序列索引和切片

Python中&#xff0c;序列是指一块可存放多个值的连续内存空间&#xff0c;这些值按一定顺序排列&#xff0c;可通过每个值所在位置的编号&#xff08;称为索引&#xff09;访问它们。它类似于C/C中的数组或字符串&#xff0c;但又比数组或字符串强大很多 序列类型包括字符串、…

ES6 全详解 let 、 const 、解构赋值、剩余运算符、函数默认参数、扩展运算符、箭头函数、新增方法,promise、Set、class等等

目录 ES6概念ECMAScript6简介ECMAScript 和 JavaScript 的关系ES6 与 ECMAScript 2015 的关系 1、let 、 const 、var 区别2、变量解构赋值1、数组解构赋值2、对象解构赋值3、字符串的解构赋值 3、展开剩余运算符1、**展开运算符(...)**2、**剩余运算符(...)** 4、函数的拓展函…

日出6万单!美区“开塞露”卖疯了,保健赛道正式起飞!

质疑养生&#xff0c;理解养生&#xff0c;加入养生&#xff01; 从保温杯里泡枸杞&#xff0c;到桌上摆满保健品&#xff0c;"养生"已经从一种模糊的概念转变为了生活中的刚需。在加班、熬夜、脱发这些"亚健康"标签的围绕下&#xff0c;年轻人开始重视自…

D-LinkNAS 远程命令执行漏洞(CVE-2024-3273)RCE漏

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 D-LinkNAS是由D-Link公司制造的网络附加存储设备。…

git 账号 personal access token

新入职&#xff0c;gitlab登录使用公司验证方式 clone项目时&#xff0c;要求填写用户名&#xff0c;access token或密码 用户名&#xff1a;gitlab的名字 access token&#xff1a;从这里获取

函数、指针和数组的相互运用(C语言)

1、函数指针数组 含义&#xff1a;数组的每个元素都是函数指针类型.eg&#xff1a; &#xff08;此代码链接&#xff1a;http://t.csdnimg.cn/ClJmb.也可以在我发布博客中找到&#xff09; 2、指向函数指针数组的指针 1、引入 3、回调函数 1、含义&#xff1a;就是一个通过…

Pixel 手机上连接提示受阻,无法上网-解决方法

命令行中输入 adb shell settings delete global captive_portal_https_urladb shell settings delete global captive_portal_http_url输入服务器信息 adb shell settings put global captive_portal_http_url http://connect.rom.miui.com/generate_204adb shell settings …

package.java文件的作用

你查看springboot的源码&#xff0c;有很多类都有这个文件&#xff0c;在idea不能创建&#xff0c;因为不支持这种命名&#xff0c;只能用记事本创建后复制都项目中。 主要应用是给类添加正常&#xff0c;或者把公用的注解都放到这里&#xff0c;常量不合适&#xff0c;作用范…

0.6V30A的降压开关稳压器IC解决方案上哪找?NCP3230MNTXG了解一下

降压开关稳压器IC是一种常见的电源管理器件&#xff0c;具有以下几个优势&#xff1a; 高效性&#xff1a;降压开关稳压器IC采用了开关调节的方式&#xff0c;能够实现高效的电能转换。相比传统的线性稳压器&#xff0c;它的能效更高&#xff0c;能够减少功耗和热量产生。 小尺…

力扣题目 19:删除链表的倒数第N个节点 【python】

&#x1f464;作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a; 码上找工作http://t.csdnimg.cn/Q59WX 作者专栏每日更新&#xff1a; LeetCod…

Redis中的集群(六)

集群 ASK错误 在进行重新分片期间&#xff0c;源节点向目标节点迁移一个槽的过程中&#xff0c;可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面&#xff0c;而另一部分键值对则保存在目标节点里面。当客户端向源节点发送一个与数据库有关的命令&#xf…

Linux使用宝塔面板部署Discuz结合内网穿透实现公网访问本地论坛

文章目录 前言1.安装基础环境2.一键部署Discuz3.安装cpolar工具4.配置域名访问Discuz5.固定域名公网地址6.配置Discuz论坛 前言 Crossday Discuz! Board&#xff08;以下简称 Discuz!&#xff09;是一套通用的社区论坛软件系统&#xff0c;用户可以在不需要任何编程的基础上&a…

基于Linux C++多线程服务器 + Qt上位机开发 + STM32 + 8266WIFI的智慧无人超市

前言 针对传统超市购物车结账排队时间长、付款效率低的问题&#xff0c;提出了一种更符合现代社会人们购物方式-基于RFID的自助收银系统。习惯了快节奏生活的人们都会选择自助收银机结账&#xff0c;理由显而易见&#xff1a;自助收银机结账很方便&#xff0c;几乎不用排队&am…