从地图到智能地图:空间索引技术如何改变我们的世界

图源:@WL
为什么空间索引很有用?
在处理地理空间数据时,空间索引是一个至关重要的功能,它决定了我们如何高效地从海量的地理数据中检索出所需的信息。想象一下,如果你正在处理一个包含数千万乃至数亿条记录的数据库,这些记录可能涵盖全球各城市、街道和建筑物的详细信息。在没有合适的索引的情况在,每次查询都可能不得不扫描整个数据集,这将是非常低效的。空间索引的引入,正是为了优化这一过程,确保查询操作既迅速又高效。在这篇文章中,我将讨论空间索引是如何实现的,以及它的优点和局限性。
什么是空间索引?
首先让我们重温下普通索引,普通索引就像我们经常会在书的末尾看到的:一个包含名词及其出现位置的列表。它可以帮助我们快速查找感兴趣的名词在特定内容中的引用。如果没有它,我们就不得不手动翻阅书中的每一页进行寻找。
在数据库中,查询和搜索一直都是十分常见且重要的功能。索引的引入通常使查找数据比遍历整个数据库更快,并且我们可以根据关注的列创建索引。而在对地理空间数据的处理中,通常需要执行诸如“交叉路口”或“在…附近”之类的操作。怎样才能建立一个空间索引,使这些操作尽可能快呢?接下来,让我们看一下数据库中的地理空间数据:

两个不相交的湖泊 图源:@WL (由Mapmost平台制作)
假设我们想要通过查询来确定这两个湖泊是否相交。通过构造,空间数据库将根据包含几何图形的边界框创建索引

两个湖泊的边界框 图源:@WL (由Mapmost平台制作)
为了判断这两个湖泊是否相交,数据库将比较两个边界框是否有任何共同区域,正如图所显示的。然而这可能直接导致错误的结论。为了解决这个问题,像PostGIS这样的空间数据库通常会将这些大的边界框分割成越来越小的边界框。

划分了小边界框的两个湖泊 图源:@WL (由Mapmost平台制作)
这种分区策略采用了一种叫做 R 树的结构进行存储。R树是一种具有层级特性的数据组织形式,它记录了一个较大的父级边界框,父级边界框下包含其所有子级边界框,以及子级边界框的子级,依次类推,形成了一个层级嵌套的体系。在这个结构中,每个父级边界框都完整地覆盖了其所有子级边界框的位置。

R-树结构示例 图源:@WL
得益于R-树结构的优势,判断“相交”与否将十分迅速:在执行相交查询时,数据库会遍历这棵树,检查每个节点的边界框,询问“当前的边界框是否与我们感兴趣的地理特征相交?”如果是,它将继续检查该边界框内的所有子框,并重复相同的相交检验。在这个过程中,数据库能够迅速地遍历整个树形结构,同时忽略那些显然不相交的分支,显著提高了查询效率。最终,系统会根据查询要求,返回所有与查询条件相交的数据。
空间索引到底有多快?
GeoPandas库同时提供了判断两个几何特征是否相交的普通方法,以及通过建立R-Tree空间索引后求解两个几何特征交集的方法,让我们采用GeoPandas库进行一个简单的对比测试吧,步骤如下。
第一步:数据准备,这里我们使用纽约市公共设施的数据集(通过开放数据下载,有需要的同学关注公众号获取)。
第二步:环境安装,通过anaconda官网下载安装包一键安装conda,接着通过conda搭建python环境,并安装GeoPandas及其依赖。
conda create -n geo_env
conda activate geo_env
conda config --env --add channels conda-forge
conda config --env --set channel_priority strict
conda install python=3 geopandas
第三步:编写测试用例,我们主要使用geopandas.GeoSeries.intersects()方法进行普通"交集"运算;通过调用geopandas.GeoSeries.sindex属性生成指定数据集的R-Tree空间索引;使用geopandas.sindex.SpatialIndex.query()方法利用建立的索引进行两个几何体的交集查询。
第四步:运行程序,结果:

从测试结果中,可以看出通过依次将34171条数据与固定的某个几何图形做“交集”运算,在不采用R-Tree空间索引的情况下耗时3.4秒,而使用了R-Tree空间索引之后耗时则是其十分之一不到。
R-Tree空间索引一定好用吗?
是否存在使用了R-Tree索引后没有任何好处的情况?是的。其中之一,便是由R-Tree空间索引存储数据的方式造成的。事实证明,原始数据的分布会影响边界框放入R-Tree中的位置。具体来说,如果大量数据集中在同一个地理空间中,它们往往会拥有相同的父节点,因此会被分组到相同的分支中。这将会导致树结构的倾斜,从而在查询时无法达到预期的优化。
Mapmost平台作为智能地图的工厂,在空间数据处理的API背后,自研并使用了多种结合特定场景的空间索引算法,致力于为用户提供极致性能体验,赶快扫码体验吧。
相关文章:
从地图到智能地图:空间索引技术如何改变我们的世界
图源:WL 为什么空间索引很有用? 在处理地理空间数据时,空间索引是一个至关重要的功能,它决定了我们如何高效地从海量的地理数据中检索出所需的信息。想象一下,如果你正在处理一个包含数千万乃至数亿条记录的数据库,…...
深入理解AI Agent架构,史上最全解析!赶紧码住!
AI Agent框架(LLM Agent):LLM驱动的智能体如何引领行业变革,应用探索与未来展望 1. AI Agent(LLM Agent)介绍 1.1. 术语 Agent:“代理” 通常是指有意行动的表现。在哲学领域,Agen…...
苹果iOS/ iPadOS18 RC 版、17.7 RC版更新发布
iPhone 16 / Pro 系列新机发布后,苹果一同推出了 iOS 18 和 iPadOS 18 的 RC 版本,iOS 18 RC 的内部版本号为22A3354,本次更新距离上次发布 Beta/RC 间隔 12 天。 在 iOS 18 中,苹果给我们带来了 Apple Intelligence,这…...
CAN集线器(工业级、隔离式)
型号: MS-HUB-C 概述 MS-HUB 是一款可通过一路 CAN ,一路 RS-232为主口扩展出 7 路 CAN 从口的工业级光电隔离型 CAN 分配器。可以有效的实现 CAN 网络的中继、扩展与隔离。采用先进的自动流控技术自动侦测CAN 信号流向。MS-HUB 具备光电隔离功能&#x…...
代码随想录训练营 Day57打卡 图论part07 53. 寻宝(prim,kruskal算法)
代码随想录训练营 Day57打卡 图论part07 卡码53. 寻宝 题目描述 在世界的某个区域,有一些分散的神秘岛屿,每个岛屿上都有一种珍稀的资源或者宝藏。国王打算在这些岛屿上建公路,方便运输。 不同岛屿之间,路途距离不同,…...
Hibernate QueryPlanCache 查询计划缓存引发的内存溢出
目录 1.排查方式2.结论3.解决办法 前言:在生产环境中有一个后端程序多次报oom然后导致程序中断。 1.排查方式 通过下载后端程序产生的oom文件,将oom文件导入MemoryAnalyzer程序分析程序堆内存使用情况。 1、将oom文件导入MemoryAnalyzer后可以看到概览信…...
前端开发的观察者模式
什么是观察者设计模式 观察者模式(Observer Pattern)是前端开发中常用的一种设计模式。它定义了一种一对多的依赖关系,使得当一个对象的状态发生改变时,其所有依赖对象都能收到通知并自动更新。观察者模式广泛应用于事件驱动的系…...
Pycharm 输入三个引号没有自动生成函数(方法)注释
配置项路径:pycharm–>Settins–>Tools–>Python Integrated Tools–>Docstrings–>Docstrings format选择对应的工程,如果有多个工程的话将 Docstrings format 的值从 Plain 换成 reStructuredText...
lammps后处理:多帧孔洞体积和孔隙率的计算
本文介绍lammps后处理技巧:多帧孔洞体积和孔隙率的计算方法。 在前面的专栏中,已经介绍了单帧孔洞体积的计算方法,有不少粉丝朋友咨询多帧孔洞体积的计算方法。 在上一次案例代码的基础上,稍加修改,添加一个for循环遍历所有的帧即可实现多帧孔洞体积的计算。 计算的结果…...
免费且实用:UI设计常用的颜色参考网站和一些Icon设计网站
用心去分享!请给我点个关注和点赞收藏!谢谢各位努力的人才! 1.在UI设计的时候,没有灵感,怎么办?可以参考这个网站(需要魔法能量) 网址如下: Color Hunt - Color Palette…...
log4j日志封装说明—slf4j对于log4j的日志封装-正确获取调用堆栈
日志是项目中必用的东西,日志产品里最普及应该就是log4j了。(logback这里暂不讨论。) 先看一下常用的log4j的用法,一般来说log4j都会配合slf4j或者common-logging使用,这里已slf4j为例。添加gradle依赖: dependencies { compile(l…...
JVM面试真题总结(六)
文章收录在网站:http://hardyfish.top/ 文章收录在网站:http://hardyfish.top/ 文章收录在网站:http://hardyfish.top/ 文章收录在网站:http://hardyfish.top/ 解释GC的标记-整理算法及其优点 GC(垃圾收集ÿ…...
C语言代码练习(第十八天)
今日练习: 48、猴子吃桃问题。猴子第1天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第2天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时&…...
linux上使用rpm的方式安装mysql
1.从mysql官网上下载需要的版本,根据操作系统版本,CPU架构,下载让rpm bundle,这个版本是个完整版,包含其他所有版本 上传到服务器的一个目录,进行解压 执行tar -xvf mysql*.tar tar -xvf mysql*.tar 2.卸载老版本m…...
html 中如何使用 uniapp 的部分方法
示例代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><…...
Samtec连接器小课堂 | 连接器电镀常识QA
【摘要/前言】 像大多数电子元件一样,无数子元件和工艺的质量直接影响到成品的质量和性能。对于PCB级连接器,这些因素包括针脚材料、塑料类型、模制塑料体的质量、尾部的共面性、表面处理(电镀)的质量、选择正确的连接器电镀、制…...
大模型备案全网最详细流程解读(附附件+重点解读)
文章目录 一、语料安全评估 二、黑盒测试 三、模型安全措施评估 四、性能评估 五、性能评估 六、安全性评估 七、可解释性评估 八、法律和合规性评估 九、应急管理措施 十、材料准备 十一、【线下流程】大模型备案线下详细步骤说明 十二、【线上流程】算法备案填报…...
基于2143规则编码的uint8_t如何转换成float
2143格式存储的float类型数据在解码时,需要将1和2互换,3和4互换,然后 通过数组转float,进行转换 uint8_t data[] {0x72, 0x02, 0xc8, 0x42}; // 字节数组 float bytesToFloat(uint8_t data[]) { uint32_t x; memcpy(&x, da…...
[项目][WebServer][整体框架设计]详细讲解
目录 0.框架 && 前言1.TcpServer类1.功能2.类设计 2.HttpServer类1.功能2.类设计 3.Request类 && Response类1.功能2.Request类设计3.Response类设计 4.EndPoint类1.功能2.类设计 5.Task类1.功能2.类设计 6.ThreadPool类1.功能2.类设计 0.框架 && 前言…...
SprinBoot+Vue网上购物商城的设计与实现
目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
