如何用Python快速提取单个GeoDataFrame中的重叠与孤立几何图形?
优化Geopandas缓冲多边形重叠/孤立区分的性能
问题背景
我用geopandas.GeoSeries.buffer给一个包含点几何的大型GeoDataFrame生成了缓冲多边形,现在需要把这些缓冲区分成两个GeoDataFrame:
- 包含所有重叠多边形的GeoDataFrame
- 包含所有孤立多边形的GeoDataFrame
在QGIS里我会用「按位置选择」工具,选择「重叠」几何谓词,让缓冲图层和自身对比,选出重叠的实体,反转选择就是孤立的。但在Python里我用循环遍历每个几何,调用geopandas.GeoSeries.overlaps逐个检查,速度慢得离谱——QGIS只需要40秒,Python跑了1小时20分钟。有没有办法让Python的处理速度接近QGIS?
我之前用的低效代码:
# 生成点缓冲 points = gpd.read_file("path_to_my_layer.gpkg") buffer = points.copy() buffer.geometry = points.geometry.buffer(3) # 检查重叠 overlaps_ids = np.array([], dtype=np.int64) for geom in buffer.geometry: # 这里耗时极长 overlaps = buffer.geometry.overlaps(geom) # 检查哪些要素和当前几何重叠 where_overlaps = np.where(overlaps == True) overlaps_ids = np.concatenate((overlaps_ids, where_overlaps[0])) overlaps_ids = np.unique(overlaps_ids) # 去重得到重叠要素的ID # 提取重叠的几何 buffer_overlaps = buffer.iloc[overlaps_ids] # 提取不重叠的几何 buffer_no_overlaps = buffer.drop(overlaps_ids)
低效原因
原来的代码是**O(n²)**的全量对比:每个要素都要和整个GeoSeries的所有要素做重叠检查,完全没有利用空间索引过滤候选要素,这也是为什么速度远慢于QGIS——QGIS底层默认用R-tree空间索引先筛选出可能重叠的要素,再做精确检查。
优化方案
以下两种方法都基于空间索引优化,性能可以接近QGIS:
方法1:手动利用空间索引筛选候选
import geopandas as gpd import numpy as np # 读取数据并生成缓冲 points = gpd.read_file("path_to_my_layer.gpkg") buffer = points.copy() buffer.geometry = points.geometry.buffer(3) # 构建R-tree空间索引 sindex = buffer.sindex # 用集合存储重叠ID(自动去重,比numpy数组拼接高效) overlapping_ids = set() for idx, geom in buffer.geometry.items(): # 用空间索引快速找到可能重叠的候选要素ID(排除自身) candidate_ids = list(sindex.query(geom, predicate="overlaps")) candidate_ids = [cid for cid in candidate_ids if cid != idx] if candidate_ids: # 将当前ID和候选ID都标记为重叠 overlapping_ids.add(idx) overlapping_ids.update(candidate_ids) # 转换为numpy数组方便索引 overlapping_ids = np.array(list(overlapping_ids), dtype=np.int64) # 提取重叠和孤立图层 buffer_overlaps = buffer.loc[overlapping_ids] buffer_no_overlaps = buffer.drop(overlapping_ids)
方法2:用空间自连接(更简洁)
Geopandas的gpd.sjoin底层已集成空间索引优化,直接用图层自连接实现QGIS「按位置选择」的逻辑:
import geopandas as gpd import numpy as np # 读取数据并生成缓冲 points = gpd.read_file("path_to_my_layer.gpkg") buffer = points.copy() buffer.geometry = points.geometry.buffer(3) # 缓冲图层自连接,只保留重叠的要素对 joined = gpd.sjoin(buffer, buffer, how="inner", predicate="overlaps") # 提取所有参与重叠的唯一要素ID overlapping_ids = np.unique(joined.index.unique()) # 提取重叠和孤立图层 buffer_overlaps = buffer.loc[overlapping_ids] buffer_no_overlaps = buffer.drop(overlapping_ids)
关键优化点
- 空间索引过滤:用R-tree空间索引快速缩小需要检查的要素范围,把复杂度从O(n²)降到O(n log n)
- 高效去重:用集合存储ID避免重复拼接数组,或者用
unique()直接提取连接结果的唯一索引 - 排除自身对比:提前排除要素和自身的对比(
overlaps方法本身也会返回False,但提前排除能减少不必要的计算)
内容的提问来源于stack exchange,提问作者Grobino
相关产品推荐
相关产品推荐

