如何高效运用集合运算与几何操作解决复杂问题?

摘要:layout: default title: "第12章:集合运算与几何操作" 第12章:集合运算与几何操作 几何集合运算是空间分析中的基础操作,类似于数学中的集合论。通过合并、求交、求差
第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,包含两类共享路径: 同方向共享的路径段 反方向共享的路径段 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() 线性参考:沿线插值 使用建议 选择合适的方法:简单运算用逐元素方法,批量运算用 overlay() 注意对齐:两个 GeoSeries 操作时注意 align 参数 性能优化:大数据集使用空间索引预筛选 结果验证:检查结果的几何类型和有效性 坐标系一致:确保参与运算的几何对象使用相同的坐标系 下一章我们将学习构造性几何操作,探索如何从已有几何对象中构造新的几何特征。