znlgis 博客

GIS开发与技术分享

第12章:集合运算与几何操作

几何集合运算是空间分析中的基础操作,类似于数学中的集合论。通过合并、求交、求差等运算,我们可以从已有的几何对象中生成新的几何形状,实现复杂的空间分析任务。本章将全面介绍 GeoPandas 中的集合运算与几何操作方法。


12.1 集合运算概述 - 几何布尔运算

12.1.1 什么是几何集合运算

几何集合运算(Set-theoretic Operations),也称为几何布尔运算,是将数学集合论应用于几何对象的操作。它们将两个几何对象视为点集,并对这些点集进行集合运算。

基本的几何集合运算包括:

运算 符号 说明
合并(Union) A ∪ B 所有属于 A 或 B 的点
交集(Intersection) A ∩ B 所有同时属于 A 和 B 的点
差集(Difference) A \ B 所有属于 A 但不属于 B 的点
对称差(Symmetric Difference) A △ B 属于 A 或 B,但不同时属于两者的点

12.1.2 集合运算在 GIS 中的应用

几何集合运算在 GIS 分析中有广泛的应用:

  • 区域合并:将多个行政区合并为一个大区域
  • 裁剪分析:用一个区域裁剪另一组数据
  • 缓冲区分析:合并多个缓冲区以确定服务覆盖范围
  • 冲突检测:计算两个区域的重叠部分
  • 空间排除:从一个区域中排除特定的子区域

12.1.3 GeoPandas 中的集合运算方法

GeoPandas 提供了两个层级的集合运算方法:

逐元素操作(Element-wise):

方法 说明
union(other) 两个 GeoSeries 逐元素合并
intersection(other) 两个 GeoSeries 逐元素求交
difference(other) 两个 GeoSeries 逐元素求差
symmetric_difference(other) 两个 GeoSeries 逐元素对称差

聚合操作(Aggregate):

方法 说明
unary_union 将一个 GeoSeries 中所有几何对象合并为一个
union_all() 将一个 GeoSeries 中所有几何对象合并为一个
intersection_all() 将一个 GeoSeries 中所有几何对象求交

12.2 union() - 几何合并

12.2.1 基本概念

union() 将两个几何对象合并为一个新的几何对象,结果包含两个原始几何对象的所有点。

12.2.2 两个 GeoSeries 逐元素合并

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

# 创建两个 GeoSeries
gs1 = gpd.GeoSeries([
    Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
    Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
])

gs2 = gpd.GeoSeries([
    Polygon([(1, 1), (3, 1), (3, 3), (1, 3)]),
    Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
])

# 逐元素合并
result = gs1.union(gs2)
print(result)
print(f"第一个合并结果面积: {result.iloc[0].area}")
print(f"第一个合并结果类型: {result.iloc[0].geom_type}")

12.2.3 GeoSeries 与单个几何对象合并

# 将所有几何对象与一个公共区域合并
common_area = Polygon([(1, 0), (3, 0), (3, 2), (1, 2)])

result = gs1.union(common_area)
print(result)

12.2.4 合并结果的几何类型

合并操作的结果类型取决于输入几何对象的关系:

输入关系 结果类型
两个重叠的多边形 Polygon
两个不重叠的多边形 MultiPolygon
多边形与内部线 Polygon(线被忽略)
两条相交的线 MultiLineString
两个相同的几何对象 与输入相同的几何对象
from shapely.geometry import Polygon

# 重叠的多边形 → Polygon
poly_a = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
poly_b = Polygon([(1, 1), (3, 1), (3, 3), (1, 3)])
gs = gpd.GeoSeries([poly_a])
result = gs.union(poly_b)
print(f"重叠多边形合并: {result.iloc[0].geom_type}")  # Polygon

# 不重叠的多边形 → MultiPolygon
poly_c = Polygon([(5, 5), (7, 5), (7, 7), (5, 7)])
result2 = gs.union(poly_c)
print(f"分离多边形合并: {result2.iloc[0].geom_type}")  # MultiPolygon

12.3 intersection() - 几何求交

12.3.1 基本概念

intersection() 计算两个几何对象的交集,结果只包含同时属于两个几何对象的点。

12.3.2 代码示例

import geopandas as gpd
from shapely.geometry import Polygon, LineString

# 两个重叠的多边形
poly_a = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
poly_b = Polygon([(1, 1), (3, 1), (3, 3), (1, 3)])

gs = gpd.GeoSeries([poly_a])
result = gs.intersection(poly_b)

print(f"交集类型: {result.iloc[0].geom_type}")  # Polygon
print(f"交集面积: {result.iloc[0].area}")        # 1.0
# 交集是 (1,1)-(2,1)-(2,2)-(1,2) 的正方形

12.3.3 不同类型几何对象的交集

# 多边形与线的交集
poly = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
line = LineString([(-1, 1), (3, 1)])  # 穿过多边形的线

gs = gpd.GeoSeries([poly])
result = gs.intersection(line)
print(f"多边形与线交集: {result.iloc[0]}")
# LineString [(0, 1), (2, 1)] - 线在多边形内的部分

# 不相交的几何对象 → 空几何
poly2 = Polygon([(5, 5), (6, 5), (6, 6), (5, 6)])
result2 = gs.intersection(poly2)
print(f"空交集: {result2.iloc[0].is_empty}")  # True

12.3.4 交集的维度降低

两个几何对象的交集可能比原始几何对象的维度低:

输入 可能的交集维度
面 ∩ 面 面、线、点、空
面 ∩ 线 线、点、空
线 ∩ 线 线、点、空
面 ∩ 点 点、空

12.3.5 实际应用:区域裁剪

import geopandas as gpd
from shapely.geometry import box

# 读取全国数据
land_use = gpd.read_file("land_use.shp")

# 定义研究区域
study_area = box(113.5, 22.0, 114.5, 23.0)  # 珠三角区域

# 裁剪到研究区域
clipped = land_use.copy()
clipped['geometry'] = land_use.intersection(study_area)

# 移除空几何
clipped = clipped[~clipped.is_empty]
print(f"裁剪后要素数: {len(clipped)}")

12.4 difference() - 几何求差

12.4.1 基本概念

difference() 计算两个几何对象的差集,结果包含属于第一个几何对象但不属于第二个几何对象的点。

注意: 差集运算不是对称的,即 A.difference(B) ≠ B.difference(A)

12.4.2 代码示例

import geopandas as gpd
from shapely.geometry import Polygon

# 两个重叠的多边形
poly_a = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
poly_b = Polygon([(1, 1), (3, 1), (3, 3), (1, 3)])

gs = gpd.GeoSeries([poly_a])

# A - B
diff_ab = gs.difference(poly_b)
print(f"A - B 面积: {diff_ab.iloc[0].area}")  # 3.0

# B - A
gs2 = gpd.GeoSeries([poly_b])
diff_ba = gs2.difference(poly_a)
print(f"B - A 面积: {diff_ba.iloc[0].area}")  # 3.0

# 验证:A的面积 = (A-B)面积 + (A∩B)面积
intersection_area = gs.intersection(poly_b).iloc[0].area
print(f"A面积 = {poly_a.area}, (A-B) + (A∩B) = {diff_ab.iloc[0].area + intersection_area}")

12.4.3 实际应用:排除区域

import geopandas as gpd

# 从城市区域中排除公园和水域
city_area = gpd.read_file("city_boundary.shp").geometry.iloc[0]
parks = gpd.read_file("parks.shp")
water = gpd.read_file("water_bodies.shp")

# 合并所有需要排除的区域
excluded = parks.union_all().union(water.union_all())

# 从城市区域中减去排除区域
buildable_area = gpd.GeoSeries([city_area]).difference(excluded)
print(f"可建设面积: {buildable_area.iloc[0].area:.2f}")

12.4.4 擦除操作(Erase)

在传统 GIS 软件中,差集运算常被称为”擦除”(Erase)操作。GeoPandas 的 overlay 函数支持此操作:

# 使用 overlay 进行擦除
result = gpd.overlay(gdf1, gdf2, how='difference')

12.5 symmetric_difference() - 对称差

12.5.1 基本概念

symmetric_difference() 计算两个几何对象的对称差,结果包含属于 A 或 B,但不同时属于两者的点。

A.symmetric_difference(B) = (A ∪ B) - (A ∩ B)
                           = (A - B) ∪ (B - A)

12.5.2 代码示例

import geopandas as gpd
from shapely.geometry import Polygon

poly_a = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
poly_b = Polygon([(1, 1), (3, 1), (3, 3), (1, 3)])

gs = gpd.GeoSeries([poly_a])
result = gs.symmetric_difference(poly_b)

print(f"对称差面积: {result.iloc[0].area}")  # 6.0
# A面积(4) + B面积(4) - 2×交集面积(1) = 6

print(f"对称差类型: {result.iloc[0].geom_type}")  # MultiPolygon(两块不相连的区域)

12.5.3 对称差的性质

  • 对称性A.symmetric_difference(B) == B.symmetric_difference(A)
  • 面积关系area(A △ B) = area(A) + area(B) - 2 × area(A ∩ B)
  • 等价表达A △ B = (A ∪ B) - (A ∩ B)

12.5.4 实际应用:变化检测

# 对比两个时期的城市建成区,找出变化区域
urban_2010 = gpd.read_file("urban_2010.shp").union_all()
urban_2020 = gpd.read_file("urban_2020.shp").union_all()

# 对称差即为变化区域(新增 + 减少)
changed_area = gpd.GeoSeries([urban_2010]).symmetric_difference(urban_2020)

# 分别计算新增和减少
newly_built = gpd.GeoSeries([urban_2020]).difference(urban_2010)  # 新增区域
demolished = gpd.GeoSeries([urban_2010]).difference(urban_2020)   # 减少区域

print(f"变化总面积: {changed_area.iloc[0].area:.2f}")
print(f"新增面积: {newly_built.iloc[0].area:.2f}")
print(f"减少面积: {demolished.iloc[0].area:.2f}")

12.6 snap() - 捕捉到几何

12.6.1 基本概念

snap() 方法将一个几何对象的顶点”捕捉”(snap)到另一个几何对象上最近的点,前提是两者的距离在指定的容差范围内。这在处理几何数据的微小不一致性时非常有用。

12.6.2 代码示例

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

# 两条几乎相连但有微小间隙的线
line_a = LineString([(0, 0), (1, 0)])
line_b = LineString([(1.01, 0), (2, 0)])  # 起点距离 line_a 终点 0.01

gs = gpd.GeoSeries([line_a])

# 将 line_a 捕捉到 line_b,容差 0.05
snapped = gs.snap(line_b, tolerance=0.05)
print(f"捕捉前终点: {list(line_a.coords)[-1]}")
print(f"捕捉后终点: {list(snapped.iloc[0].coords)[-1]}")
# 捕捉后 line_a 的终点会被移到 (1.01, 0)

12.6.3 实际应用

# 修复拓扑不一致:确保相邻多边形共享精确的边界
import geopandas as gpd

polygons = gpd.read_file("parcels.shp")

# 对于每对相邻多边形,将其捕捉到彼此
tolerance = 0.001  # 容差(与坐标系单位一致)

for i in range(len(polygons)):
    for j in range(i + 1, len(polygons)):
        if polygons.geometry.iloc[i].distance(polygons.geometry.iloc[j]) < tolerance:
            # 将几何 i 捕捉到几何 j
            polygons.geometry.iloc[i] = polygons.geometry.iloc[i].snap(
                polygons.geometry.iloc[j], tolerance
            )

12.6.4 注意事项

  • 容差值应根据数据精度合理设置
  • 过大的容差可能导致几何变形
  • snap() 只移动在容差范围内的顶点

12.7 unary_union - 所有几何合并为一个

12.7.1 基本概念

unary_union 属性将一个 GeoSeries 中的所有几何对象合并为一个几何对象。这相当于对所有几何对象依次进行 union() 操作。

12.7.2 代码示例

import geopandas as gpd
from shapely.geometry import Polygon

# 创建多个多边形
gs = gpd.GeoSeries([
    Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
    Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]),
    Polygon([(1, 1), (2, 1), (2, 2), (1, 2)]),
])

# 合并所有几何对象
merged = gs.unary_union
print(f"合并结果类型: {merged.geom_type}")  # Polygon
print(f"合并结果面积: {merged.area}")

12.7.3 dissolve 操作

unary_union 常与 dissolve() 方法结合使用,按属性分组合并几何对象:

import geopandas as gpd

# 读取县级行政区数据
counties = gpd.read_file("counties.shp")

# 按省份名称分组合并,得到省级边界
provinces = counties.dissolve(by='province_name')
print(f"县级区域数: {len(counties)}")
print(f"省级区域数: {len(provinces)}")

12.7.4 注意事项

  • unary_union 是一个属性(property),不是方法
  • 对于大量几何对象,unary_union 可能比较耗时
  • 结果可能是 Polygon、MultiPolygon 或 GeometryCollection,取决于输入

12.8 union_all() 与 intersection_all() - 聚合运算

12.8.1 union_all() 方法

union_all()unary_union 的方法版本,功能相同,但作为方法调用更加灵活。

import geopandas as gpd
from shapely.geometry import Polygon

gs = gpd.GeoSeries([
    Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
    Polygon([(0.5, 0), (1.5, 0), (1.5, 1), (0.5, 1)]),
    Polygon([(1, 0), (2, 0), (2, 1), (1, 1)]),
])

# 使用 union_all
merged = gs.union_all()
print(f"合并面积: {merged.area}")  # 2.0
print(f"合并类型: {merged.geom_type}")  # Polygon

12.8.2 intersection_all() 方法

intersection_all() 计算 GeoSeries 中所有几何对象的公共交集。

import geopandas as gpd
from shapely.geometry import Polygon

gs = gpd.GeoSeries([
    Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
    Polygon([(0.5, 0.5), (2.5, 0.5), (2.5, 2.5), (0.5, 2.5)]),
    Polygon([(1, 1), (3, 1), (3, 3), (1, 3)]),
])

# 计算所有几何对象的交集
common = gs.intersection_all()
print(f"公共区域面积: {common.area}")      # 1.0
print(f"公共区域类型: {common.geom_type}")  # Polygon
# 公共区域是 (1,1)-(2,1)-(2,2)-(1,2)

12.8.3 union_all vs unary_union

特性 unary_union union_all()
类型 属性 方法
调用方式 gs.unary_union gs.union_all()
功能 合并所有几何 合并所有几何
灵活性 无参数 可能有参数选项
推荐 传统用法 推荐用法

12.9 shared_paths() - 共享路径段

12.9.1 基本概念

shared_paths() 找出两个线性几何对象之间共享的路径段。返回的结果是一个 GeometryCollection,包含两类共享路径:

  1. 同方向共享的路径段
  2. 反方向共享的路径段

12.9.2 代码示例

import geopandas as gpd
from shapely.geometry import LineString, MultiLineString

# 两条部分重叠的线
line_a = LineString([(0, 0), (1, 0), (2, 0), (3, 0)])
line_b = LineString([(1, 0), (2, 0), (3, 0), (4, 0)])

gs = gpd.GeoSeries([line_a])
result = gs.shared_paths(line_b)

print(f"共享路径: {result.iloc[0]}")
# GeometryCollection,包含同方向和反方向的共享段

12.9.3 解读结果

from shapely.geometry import LineString
from shapely import shared_paths

line_a = LineString([(0, 0), (1, 0), (2, 0)])
line_b = LineString([(1, 0), (2, 0), (3, 0)])

result = shared_paths(line_a, line_b)
# result 是 GeometryCollection
# result.geoms[0]: 同方向共享段(MultiLineString)
# result.geoms[1]: 反方向共享段(MultiLineString)

print(f"同方向共享: {result.geoms[0]}")
print(f"反方向共享: {result.geoms[1]}")

12.9.4 实际应用

# 找出两条道路的共享路段
road_a = gpd.read_file("road_a.shp").geometry.iloc[0]
road_b = gpd.read_file("road_b.shp").geometry.iloc[0]

gs = gpd.GeoSeries([road_a])
shared = gs.shared_paths(road_b)

if not shared.iloc[0].is_empty:
    print("两条道路有共享路段")
    # 共享路段可能是同一条物理道路被两个数据源重复记录

12.10 shortest_line() - 最短连线

12.10.1 基本概念

shortest_line() 计算两个几何对象之间的最短连线。返回的是一条 LineString,其起点在第一个几何对象上,终点在第二个几何对象上。

12.10.2 代码示例

import geopandas as gpd
from shapely.geometry import Point, Polygon, LineString

# 点到多边形的最短连线
point = Point(5, 5)
poly = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])

gs = gpd.GeoSeries([poly])
shortest = gs.shortest_line(point)

print(f"最短连线: {shortest.iloc[0]}")
print(f"最短距离: {shortest.iloc[0].length:.4f}")  # 等于 distance()

12.10.3 多种几何类型

# 两条不相交的线之间的最短连线
line_a = LineString([(0, 0), (2, 0)])
line_b = LineString([(1, 1), (3, 1)])

gs = gpd.GeoSeries([line_a])
shortest = gs.shortest_line(line_b)
print(f"线到线最短连线: {shortest.iloc[0]}")
# LINESTRING (1 0, 1 1) - 垂直于两条线的连线

12.10.4 实际应用

# 为每个消防站找到最近的河流取水点
stations = gpd.read_file("fire_stations.shp")
rivers = gpd.read_file("rivers.shp")

# 合并所有河流为一个几何
all_rivers = rivers.union_all()

# 计算每个消防站到最近河流的最短连线
shortest_lines = stations.geometry.apply(
    lambda pt: pt.shortest_line(all_rivers) if hasattr(pt, 'shortest_line')
    else gpd.GeoSeries([pt]).shortest_line(all_rivers).iloc[0]
)

# 获取取水点(最短连线的终点)
water_access_points = shortest_lines.apply(
    lambda line: Point(line.coords[-1])
)

12.11 project() 与 interpolate() - 线性参考

12.11.1 线性参考的概念

线性参考(Linear Referencing)是沿线性几何对象定位点的方法。它使用沿线距离而不是二维坐标来描述位置。

12.11.2 project() - 投影到线上

project() 计算一个点在线性几何对象上的投影位置,返回沿线距离。

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

# 创建线段
line = LineString([(0, 0), (10, 0)])

# 要投影的点
points = gpd.GeoSeries([
    Point(3, 5),    # 在线上方
    Point(7, -3),   # 在线下方
    Point(-2, 1),   # 在线起点之前
    Point(12, 2),   # 在线终点之后
])

gs_line = gpd.GeoSeries([line] * 4)

# 计算沿线距离
distances = gs_line.project(points)
print(distances)
# 0     3.0  (投影到 (3, 0),沿线距离 3)
# 1     7.0  (投影到 (7, 0),沿线距离 7)
# 2     0.0  (投影到起点 (0, 0),限制为 0)
# 3    10.0  (投影到终点 (10, 0),限制为 10)

# 归一化距离(0-1范围)
normalized = gs_line.project(points, normalized=True)
print(normalized)
# 0    0.3
# 1    0.7
# 2    0.0
# 3    1.0

12.11.3 interpolate() - 沿线插值

interpolate() 根据沿线距离在线性几何对象上找到对应的点。

import geopandas as gpd
from shapely.geometry import LineString

# 创建线段
line = LineString([(0, 0), (5, 0), (5, 5)])

gs = gpd.GeoSeries([line, line, line])
distances = gpd.array.GeometryDtype  # 不同距离

# 沿线不同距离处插值
result = gs.interpolate([2.0, 5.0, 8.0])
print(result)
# 0    POINT (2 0)      距离 2: 在第一段上
# 1    POINT (5 0)      距离 5: 在转折点
# 2    POINT (5 3)      距离 8: 在第二段上

# 使用归一化距离
total_length = line.length  # 10.0
result_norm = gs.interpolate([0.2, 0.5, 0.8], normalized=True)
print(result_norm)

12.11.4 project 与 interpolate 的互逆关系

from shapely.geometry import LineString, Point

line = LineString([(0, 0), (5, 0), (5, 5)])

# 从点投影得到距离
point = Point(3, 2)
distance = line.project(point)
print(f"投影距离: {distance}")

# 从距离反插值得到线上的点
projected_point = line.interpolate(distance)
print(f"线上投影点: {projected_point}")
# 这是 point 在 line 上的最近点

12.11.5 实际应用:里程标记

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

# 道路中心线
road = LineString([(0, 0), (100, 0), (100, 50), (200, 50)])

# 每隔 50 米设置一个里程标记
gs = gpd.GeoSeries([road])
total_length = road.length
markers = []
distance = 0

while distance <= total_length:
    point = road.interpolate(distance)
    markers.append({
        'distance': distance,
        'geometry': point
    })
    distance += 50

markers_gdf = gpd.GeoDataFrame(markers)
print(f"共设置 {len(markers_gdf)} 个里程标记")

12.12 集合运算的对齐机制

12.12.1 align 参数

与空间谓词类似,集合运算方法也支持 align 参数来控制两个 GeoSeries 之间的索引对齐行为。

import geopandas as gpd
from shapely.geometry import Polygon

# 不同索引的 GeoSeries
gs1 = gpd.GeoSeries(
    [Polygon([(0,0),(2,0),(2,2),(0,2)])],
    index=[0]
)
gs2 = gpd.GeoSeries(
    [Polygon([(1,1),(3,1),(3,3),(1,3)])],
    index=[1]
)

# align=True(默认)
result_aligned = gs1.intersection(gs2, align=True)
print(f"对齐后结果长度: {len(result_aligned)}")
# 索引 0 和 1 都会出现,不匹配的变为 NaN

# align=False
result_positional = gs1.intersection(gs2, align=False)
print(f"按位置结果: {result_positional.iloc[0]}")
# 按位置配对:gs1[位置0] ∩ gs2[位置0]

12.12.2 最佳实践

  • 当两个 GeoSeries 索引相同时,align 参数无影响
  • 当与单个几何对象操作时,align 参数无影响
  • 建议在进行集合运算前先确认索引是否一致

12.13 性能考虑

12.13.1 大数据集的性能优化

几何集合运算在大数据集上可能比较耗时。以下是一些优化建议:

使用空间索引预筛选

import geopandas as gpd
from shapely.geometry import Polygon

# 而不是对所有要素进行集合运算,先用空间索引筛选
gdf = gpd.read_file("large_dataset.shp")
clip_area = Polygon([(...)]) 

# 先筛选可能相交的要素
possible_idx = gdf.sindex.query(clip_area, predicate='intersects')
candidates = gdf.iloc[possible_idx]

# 只对候选要素进行精确运算
result = candidates.intersection(clip_area)

分批处理

import numpy as np

# 对于大型 unary_union,可以分批处理
def batch_union(geometries, batch_size=1000):
    """分批合并几何对象"""
    batches = [geometries[i:i+batch_size]
               for i in range(0, len(geometries), batch_size)]

    partial_unions = [gpd.GeoSeries(batch).union_all()
                      for batch in batches]

    return gpd.GeoSeries(partial_unions).union_all()

12.13.2 常见性能陷阱

问题 说明 解决方案
大规模 unary_union 数万个几何对象合并很慢 分批合并
逐行循环运算 Python 循环慢 使用向量化操作
无空间索引 暴力搜索所有几何对象 使用 sindex 预筛选
复杂几何 高顶点数几何运算慢 先简化再运算

12.13.3 overlay vs 手动集合运算

GeoPandas 的 overlay() 函数封装了常用的集合运算,并自动处理属性合并:

import geopandas as gpd

gdf1 = gpd.read_file("layer1.shp")
gdf2 = gpd.read_file("layer2.shp")

# overlay 支持的运算类型
result_union = gpd.overlay(gdf1, gdf2, how='union')
result_intersection = gpd.overlay(gdf1, gdf2, how='intersection')
result_difference = gpd.overlay(gdf1, gdf2, how='difference')
result_sym_diff = gpd.overlay(gdf1, gdf2, how='symmetric_difference')
result_identity = gpd.overlay(gdf1, gdf2, how='identity')

overlay() 的优势:

  • 自动处理属性列的合并
  • 自动处理空间索引优化
  • API 更简洁

12.14 本章小结

本章全面介绍了 GeoPandas 中的集合运算与几何操作方法。主要内容回顾:

核心集合运算

运算 方法 说明
合并 union() A ∪ B
交集 intersection() A ∩ B
差集 difference() A \ B
对称差 symmetric_difference() A △ B

聚合运算

方法 说明
unary_union 合并所有几何为一个(属性)
union_all() 合并所有几何为一个(方法)
intersection_all() 求所有几何的公共交集

其他操作

方法 说明
snap() 捕捉到另一个几何
shared_paths() 查找共享路径段
shortest_line() 计算最短连线
project() 线性参考:投影距离
interpolate() 线性参考:沿线插值

使用建议

  1. 选择合适的方法:简单运算用逐元素方法,批量运算用 overlay()
  2. 注意对齐:两个 GeoSeries 操作时注意 align 参数
  3. 性能优化:大数据集使用空间索引预筛选
  4. 结果验证:检查结果的几何类型和有效性
  5. 坐标系一致:确保参与运算的几何对象使用相同的坐标系

下一章我们将学习构造性几何操作,探索如何从已有几何对象中构造新的几何特征。