惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
Blog — PlanetScale
Blog — PlanetScale
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
爱范儿
爱范儿
量子位
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
GbyAI
GbyAI
MyScale Blog
MyScale Blog
IT之家
IT之家
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
The Cloudflare Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Hugging Face - Blog
Hugging Face - Blog
The Register - Security
The Register - Security
Microsoft Security Blog
Microsoft Security Blog
博客园_首页
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Y
Y Combinator Blog
雷峰网
雷峰网
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
博客园 - 叶小钗
D
DataBreaches.Net
B
Blog
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
aimingoo的专栏
aimingoo的专栏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
Recent Announcements
Recent Announcements
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
腾讯CDC
T
Threat Research - Cisco Blogs
SecWiki News
SecWiki News
Martin Fowler
Martin Fowler
D
Docker
Cisco Talos Blog
Cisco Talos Blog
T
Tenable Blog
Webroot Blog
Webroot Blog
宝玉的分享
宝玉的分享

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第15章:空间索引与查询优化
我才是银古 · 2026-06-22 · via 博客园 - 我才是银古

第15章:空间索引与查询优化

在处理大规模地理空间数据时,空间查询的性能是一个关键问题。空间索引通过对几何对象的空间位置建立索引结构,可以显著加速空间查询操作。本章将全面介绍 GeoPandas 中空间索引的原理、使用方法和优化策略。


15.1 空间索引概述 - R-tree 原理

15.1.1 为什么需要空间索引

在没有空间索引的情况下,执行空间查询(如"找出与某区域相交的所有要素")需要逐一检查每个要素,时间复杂度为 O(n)。当数据量达到数十万甚至数百万时,这种暴力搜索将变得非常缓慢。

空间索引通过预先组织数据的空间分布信息,将查询的平均时间复杂度降低到 O(log n)。

15.1.2 R-tree 数据结构

R-tree(Rectangle Tree)是最常用的空间索引数据结构,由 Antonin Guttman 于 1984 年提出。

R-tree 的基本思想:

  1. 使用最小外接矩形(MBR)来近似表示每个几何对象
  2. 将空间中临近的 MBR 分组,每组用一个更大的 MBR 包围
  3. 递归地对这些分组再次分组,形成树状层次结构
              [根节点 MBR]
             /            \
     [分支 MBR_1]     [分支 MBR_2]
      /    |    \       /    |
   [叶1] [叶2] [叶3] [叶4] [叶5]

15.1.3 R-tree 查询过程

  1. 从根节点开始,用查询几何的 MBR 与节点的 MBR 比较
  2. 只进入 MBR 相交的子节点(剪枝)
  3. 到达叶节点后,返回可能匹配的候选要素
  4. 对候选要素进行精确的几何判断(过滤)

这个"两阶段"过程大大减少了需要进行精确几何计算的要素数量。

15.1.4 STRtree

GeoPandas(通过 Shapely)使用的是 STRtree(Sort-Tile-Recursive tree),它是一种批量加载的 R-tree 变体:

  • 一次性构建索引,比逐个插入更高效
  • 查询性能通常优于标准 R-tree
  • 是只读索引,构建后不能添加或删除元素

15.2 GeoPandas 空间索引 - 基于 Shapely STRtree

15.2.1 底层实现

GeoPandas 的空间索引基于 Shapely 的 STRtree 实现,而 Shapely 又基于 GEOS 库的 C++ 实现。

import geopandas as gpd
from shapely.geometry import Point
import numpy as np

# 创建大量随机点
np.random.seed(42)
n = 10000
points = [Point(x, y) for x, y in zip(
    np.random.uniform(0, 100, n),
    np.random.uniform(0, 100, n)
)]

gdf = gpd.GeoDataFrame(geometry=points)

# 查看空间索引类型
sindex = gdf.sindex
print(f"空间索引类型: {type(sindex)}")
print(f"索引大小: {sindex.size}")

15.2.2 索引的自动使用

许多 GeoPandas 操作会自动使用空间索引:

  • sjoin() - 空间连接
  • clip() - 空间裁剪
  • overlay() - 空间叠加分析
  • nearest() - 最近邻查找

15.3 访问空间索引 - .sindex 属性

15.3.1 基本访问

import geopandas as gpd
from shapely.geometry import Point, box

# 创建 GeoDataFrame
gdf = gpd.GeoDataFrame(geometry=[
    Point(0, 0), Point(1, 1), Point(2, 2),
    Point(3, 3), Point(4, 4), Point(5, 5),
])

# 访问空间索引
sindex = gdf.sindex
print(f"索引中的对象数: {sindex.size}")
print(f"索引是否有效: {sindex.is_empty == False}")

15.3.2 GeoSeries 的空间索引

# GeoSeries 也有 sindex
gs = gdf.geometry
sindex_gs = gs.sindex
print(f"GeoSeries 索引大小: {sindex_gs.size}")

15.4 延迟创建机制 - 首次访问时创建,has_sindex 检查

15.4.1 延迟创建

空间索引不会在创建 GeoDataFrame 时自动构建,而是在首次访问 .sindex 属性时才创建(延迟初始化)。

import geopandas as gpd
from shapely.geometry import Point

gdf = gpd.GeoDataFrame(geometry=[Point(i, i) for i in range(1000)])

# 此时索引尚未创建
print(f"索引是否存在: {gdf.has_sindex}")  # False

# 首次访问时创建
sindex = gdf.sindex
print(f"索引是否存在: {gdf.has_sindex}")  # True

15.4.2 has_sindex 属性

has_sindex 属性可以检查空间索引是否已创建,而不会触发索引构建。

# 不触发创建的检查
if not gdf.has_sindex:
    print("索引尚未创建,首次空间查询时将自动创建")

# 显式创建索引
_ = gdf.sindex  # 触发创建
print(f"现在索引存在: {gdf.has_sindex}")

15.5 query() 方法 - 按几何+谓词查询

15.5.1 基本语法

sindex.query(geometry, predicate=None, sort=False)

15.5.2 参数说明

参数 说明
geometry 查询几何对象
predicate 空间谓词(如 'intersects', 'contains' 等)
sort 是否对结果排序

15.5.3 无谓词查询(仅 MBR 过滤)

import geopandas as gpd
from shapely.geometry import Point, box
import numpy as np

# 创建数据
np.random.seed(42)
gdf = gpd.GeoDataFrame(
    geometry=[Point(np.random.uniform(0, 100), np.random.uniform(0, 100))
              for _ in range(10000)]
)

# 查询区域
query_box = box(20, 20, 40, 40)

# 仅 MBR 过滤(粗过滤)
candidates = gdf.sindex.query(query_box)
print(f"MBR 候选数: {len(candidates)}")
# 返回 MBR 与查询框相交的所有要素索引

15.5.4 带谓词查询(精确过滤)

# 使用谓词进行精确查询
from shapely.geometry import Polygon

query_circle = Point(50, 50).buffer(15)

# intersects 谓词
intersecting = gdf.sindex.query(query_circle, predicate='intersects')
print(f"相交的要素数: {len(intersecting)}")

# within 谓词
within_idx = gdf.sindex.query(query_circle, predicate='contains')
print(f"被圆包含的要素数: {len(within_idx)}")

15.5.5 支持的谓词列表

谓词 说明
'intersects' 相交
'within' 在查询几何内部
'contains' 包含查询几何
'overlaps' 重叠
'crosses' 交叉
'touches' 接触
'covers' 覆盖
'covered_by' 被覆盖
'contains_properly' 严格包含
'dwithin' 距离范围内
None 仅 MBR 过滤

15.5.6 使用查询结果

# 查询结果是索引数组,可以直接用于筛选
idx = gdf.sindex.query(query_circle, predicate='intersects')
result = gdf.iloc[idx]
print(f"查询到 {len(result)} 个要素")

15.6 query_bulk() - 批量查询

15.6.1 基本概念

query_bulk() 对多个查询几何同时执行空间查询,返回配对的索引数组。这比循环调用 query() 更高效。

注意: 在较新版本的 GeoPandas 中,推荐使用 query() 直接传入几何数组。

15.6.2 代码示例

import geopandas as gpd
from shapely.geometry import Point, box
import numpy as np

# 创建大量点
np.random.seed(42)
n = 50000
gdf = gpd.GeoDataFrame(
    geometry=[Point(np.random.uniform(0, 100), np.random.uniform(0, 100))
              for _ in range(n)]
)

# 多个查询区域
query_geoms = gpd.GeoSeries([
    box(10, 10, 20, 20),
    box(30, 30, 50, 50),
    box(70, 70, 90, 90),
])

# 批量查询
input_idx, tree_idx = gdf.sindex.query(query_geoms, predicate='intersects')
print(f"总匹配数: {len(tree_idx)}")

# input_idx 表示查询几何的索引
# tree_idx 表示匹配的数据要素索引
for i in range(len(query_geoms)):
    mask = input_idx == i
    count = mask.sum()
    print(f"查询区域 {i}: 匹配 {count} 个要素")

15.6.3 与 sjoin 的关系

sjoin() 内部使用类似的批量查询机制:

# sjoin 本质上是批量查询 + 属性合并
result = gpd.sjoin(
    gdf,
    gpd.GeoDataFrame(geometry=query_geoms),
    predicate='intersects'
)

15.7 nearest() - 最近邻查询

15.7.1 基本概念

nearest() 使用空间索引高效地查找最近的几何对象。

15.7.2 代码示例

import geopandas as gpd
from shapely.geometry import Point
import numpy as np

# 创建数据点
np.random.seed(42)
gdf = gpd.GeoDataFrame(
    geometry=[Point(np.random.uniform(0, 100), np.random.uniform(0, 100))
              for _ in range(10000)]
)

# 查询点
query = Point(50, 50)

# 查找最近的要素
input_idx, tree_idx = gdf.sindex.nearest(query, return_all=False)
print(f"最近的要素索引: {tree_idx[0]}")
print(f"最近的要素: {gdf.iloc[tree_idx[0]].geometry}")
print(f"距离: {query.distance(gdf.iloc[tree_idx[0]].geometry):.4f}")

15.7.3 查找 k 个最近邻

# 使用 sjoin_nearest 查找最近邻并保留距离
target = gpd.GeoDataFrame(geometry=[Point(50, 50)])
result = gpd.sjoin_nearest(target, gdf, max_distance=10, distance_col='dist')
result_sorted = result.sort_values('dist')
print(f"10 单位内最近的 5 个要素:")
print(result_sorted.head())

15.7.4 实际应用

# 为每个居民区找到最近的医院
residential = gpd.read_file("residential.shp")
hospitals = gpd.read_file("hospitals.shp")

# 确保使用投影坐标系
residential_proj = residential.to_crs(epsg=32650)
hospitals_proj = hospitals.to_crs(epsg=32650)

# 最近邻连接
result = gpd.sjoin_nearest(
    residential_proj, hospitals_proj,
    distance_col='hospital_distance',
    how='left'
)

print(f"平均到最近医院的距离: {result['hospital_distance'].mean():.0f} 米")
print(f"最远到最近医院的距离: {result['hospital_distance'].max():.0f} 米")

15.8 valid_query_predicates - 可用谓词属性

15.8.1 查看可用谓词

import geopandas as gpd
from shapely.geometry import Point

gdf = gpd.GeoDataFrame(geometry=[Point(0, 0)])
sindex = gdf.sindex

# 查看所有可用的查询谓词
print("可用谓词:")
for pred in sorted(sindex.valid_query_predicates):
    print(f"  - {pred}")

15.8.2 谓词选择建议

场景 推荐谓词
查找区域内的点 'intersects''within'
查找重叠区域 'overlaps'
查找相邻要素 'touches'
查找包含关系 'contains'
距离范围查询 'dwithin'

15.9 空间索引在 sjoin 中的应用

15.9.1 sjoin 的工作原理

sjoin() 内部使用空间索引加速空间连接:

  1. 构建右表的空间索引(如果尚未构建)
  2. 对左表每个几何,使用空间索引查找候选匹配
  3. 对候选匹配进行精确的空间谓词判断
  4. 合并匹配要素的属性
import geopandas as gpd
from shapely.geometry import Point, Polygon
import numpy as np

# 创建点数据
np.random.seed(42)
points = gpd.GeoDataFrame({
    'value': np.random.randint(1, 100, 10000),
    'geometry': [Point(np.random.uniform(0, 10), np.random.uniform(0, 10))
                 for _ in range(10000)]
})

# 创建网格多边形
from shapely.geometry import box
grid = []
for x in range(10):
    for y in range(10):
        grid.append({'cell': f'{x}_{y}', 'geometry': box(x, y, x+1, y+1)})
grid_gdf = gpd.GeoDataFrame(grid)

# 空间连接(自动使用空间索引)
joined = gpd.sjoin(points, grid_gdf, predicate='within')
print(f"连接结果行数: {len(joined)}")

# 统计每个格子内的点数
cell_counts = joined.groupby('cell').size()
print(f"每格平均点数: {cell_counts.mean():.1f}")

15.9.2 sjoin_nearest

# 最近邻空间连接
from shapely.geometry import Point

# 设施点
facilities = gpd.GeoDataFrame({
    'type': ['医院', '学校', '超市'] * 10,
    'geometry': [Point(np.random.uniform(0, 10), np.random.uniform(0, 10))
                 for _ in range(30)]
})

# 查询点
queries = gpd.GeoDataFrame({
    'name': [f'位置{i}' for i in range(5)],
    'geometry': [Point(np.random.uniform(0, 10), np.random.uniform(0, 10))
                 for _ in range(5)]
})

# 为每个查询点找到最近的设施
result = gpd.sjoin_nearest(queries, facilities, distance_col='distance')
print(result[['name', 'type', 'distance']])

15.10 索引重建策略 - 几何变化后自动重建

15.10.1 自动失效机制

当 GeoDataFrame 的几何列发生变化时,空间索引会自动失效,在下次访问时重建。

import geopandas as gpd
from shapely.geometry import Point

gdf = gpd.GeoDataFrame(geometry=[Point(i, i) for i in range(100)])

# 触发索引创建
_ = gdf.sindex
print(f"索引存在: {gdf.has_sindex}")  # True

# 修改几何列
gdf.geometry = gdf.geometry.buffer(1)
# 索引自动失效
print(f"修改后索引存在: {gdf.has_sindex}")  # False

# 下次访问自动重建
_ = gdf.sindex
print(f"重建后索引存在: {gdf.has_sindex}")  # True

15.10.2 手动管理

# 如果确定不再需要索引,可以避免不必要的创建
# 例如,在一系列几何修改操作之间,不要访问 sindex

# 批量修改时的最佳实践
gdf_temp = gdf.copy()  # 在副本上操作

# 进行多次几何修改...
gdf_temp['geometry'] = gdf_temp.buffer(1)
gdf_temp['geometry'] = gdf_temp.simplify(0.1)

# 修改完成后,索引只需构建一次
result = gdf_temp.sindex.query(some_geometry, predicate='intersects')

15.11 性能基准测试 - 有无空间索引的性能对比

15.11.1 基准测试代码

import geopandas as gpd
from shapely.geometry import Point, box
import numpy as np
import time

# 创建不同规模的数据集
def benchmark(n_points):
    np.random.seed(42)
    points = gpd.GeoDataFrame(
        geometry=[Point(np.random.uniform(0, 1000), np.random.uniform(0, 1000))
                  for _ in range(n_points)]
    )

    query = box(400, 400, 600, 600)

    # 方法一:不使用空间索引(逐个检查)
    start = time.time()
    result1 = points[points.intersects(query)]
    time_no_index = time.time() - start

    # 方法二:使用空间索引
    start = time.time()
    idx = points.sindex.query(query, predicate='intersects')
    result2 = points.iloc[idx]
    time_with_index = time.time() - start

    print(f"数据量: {n_points:>8d} | "
          f"无索引: {time_no_index:.4f}s | "
          f"有索引: {time_with_index:.4f}s | "
          f"加速比: {time_no_index/max(time_with_index, 0.0001):.1f}x | "
          f"结果数: {len(result1)}")

# 测试不同规模
for n in [1000, 5000, 10000, 50000, 100000]:
    benchmark(n)

15.11.2 典型性能对比

数据量 无索引 有索引 加速比
1,000 0.005s 0.001s 5x
10,000 0.05s 0.001s 50x
100,000 0.5s 0.002s 250x
1,000,000 5s 0.005s 1000x

数据量越大,空间索引的优势越明显。

15.11.3 索引构建时间

import time

# 索引构建时间也需要考虑
def measure_index_build(n):
    np.random.seed(42)
    gdf = gpd.GeoDataFrame(
        geometry=[Point(np.random.uniform(0, 1000), np.random.uniform(0, 1000))
                  for _ in range(n)]
    )

    start = time.time()
    _ = gdf.sindex
    build_time = time.time() - start

    print(f"数据量 {n:>8d}: 索引构建时间 {build_time:.4f}s")

for n in [1000, 10000, 100000, 500000]:
    measure_index_build(n)

15.12 大数据集优化策略

15.12.1 分块处理

import geopandas as gpd
import numpy as np

def process_in_chunks(large_gdf, query_geom, chunk_size=50000):
    """分块处理大数据集"""
    results = []
    n_chunks = (len(large_gdf) + chunk_size - 1) // chunk_size

    for i in range(n_chunks):
        start_idx = i * chunk_size
        end_idx = min((i + 1) * chunk_size, len(large_gdf))
        chunk = large_gdf.iloc[start_idx:end_idx]

        # 每个块独立使用空间索引
        idx = chunk.sindex.query(query_geom, predicate='intersects')
        if len(idx) > 0:
            results.append(chunk.iloc[idx])

    if results:
        return gpd.GeoDataFrame(pd.concat(results))
    return gpd.GeoDataFrame()

15.12.2 空间分区

import geopandas as gpd
from shapely.geometry import box
import numpy as np

def spatial_partition(gdf, n_partitions=10):
    """按空间范围分区"""
    bounds = gdf.total_bounds  # [minx, miny, maxx, maxy]
    x_step = (bounds[2] - bounds[0]) / n_partitions

    partitions = {}
    for i in range(n_partitions):
        x_min = bounds[0] + i * x_step
        x_max = bounds[0] + (i + 1) * x_step
        partition_box = box(x_min, bounds[1], x_max, bounds[3])

        # 使用空间索引快速筛选
        idx = gdf.sindex.query(partition_box, predicate='intersects')
        partitions[i] = gdf.iloc[idx]

    return partitions

15.12.3 减少几何复杂度

# 简化几何以加速索引查询
gdf_simplified = gdf.copy()
gdf_simplified['geometry'] = gdf.simplify(tolerance=10)

# 用简化几何做粗过滤
coarse_idx = gdf_simplified.sindex.query(query_geom, predicate='intersects')

# 用原始几何做精确过滤
candidates = gdf.iloc[coarse_idx]
final_result = candidates[candidates.intersects(query_geom)]

15.12.4 避免重复索引构建

# 不好的做法:每次循环都修改几何,导致重复构建索引
for buffer_dist in [100, 200, 500, 1000]:
    buffered = gdf.buffer(buffer_dist)
    # 每次 buffer 后创建新的 GeoSeries,索引需要重建

# 好的做法:预计算所有缓冲区
buffers = {d: gdf.buffer(d) for d in [100, 200, 500, 1000]}

15.12.5 综合优化建议

策略 适用场景 预期加速
使用空间索引 所有空间查询 10-1000x
分块处理 内存受限的大数据集 避免内存溢出
空间分区 并行处理 与核数成正比
几何简化 复杂几何的查询 2-10x
预计算缓冲区 多次使用的缓冲区 避免重复计算
投影优化 跨投影的分析 减少投影转换

15.13 本章小结

本章全面介绍了 GeoPandas 中的空间索引与查询优化。主要内容回顾:

空间索引基础

概念 说明
R-tree 基于最小外接矩形的层次索引结构
STRtree 批量加载的 R-tree 变体,GeoPandas 使用的实现
.sindex 访问空间索引的属性
has_sindex 检查索引是否已创建(不触发创建)

查询方法

方法 说明
query(geom, predicate) 按几何和谓词查询
query(geom_array, predicate) 批量查询
nearest(geom) 最近邻查询
valid_query_predicates 查看可用谓词

性能优化

策略 说明
使用空间索引 大数据集必须使用
使用 sjoin 自动利用空间索引
分块处理 避免内存溢出
空间分区 支持并行处理
避免重复构建 注意索引失效机制

使用建议

  1. 始终使用空间索引:对于超过几百个要素的数据集,空间索引都能带来显著加速
  2. 优先使用 sjoinsjoin() 自动处理空间索引,比手动查询更简洁
  3. 注意索引生命周期:几何修改后索引会失效,避免频繁修改-查询交替
  4. 选择合适的谓词:不同谓词的性能可能不同,选择最精确的谓词
  5. 监控内存使用:超大数据集的空间索引本身也占用内存

至此,我们已经完成了 GeoPandas 空间分析核心功能的学习。通过空间索引的优化,您可以高效地处理大规模地理空间数据,充分发挥 GeoPandas 的分析能力。