Shapely判断多部分/带洞多边形中点内外结果异常问题
解决Pyshp+Shapely处理多部分/带洞多边形点-in-面判断错误的问题
问题根源
核心问题是没正确解析Shapefile中多边形的结构,错误地用Shapely单个Polygon处理多部分图形,或未区分带洞多边形的外环与内环:
- 多部分分离多边形(如蓝色多边形A)是多个独立的外环;带洞多边形(如粉色多边形B)是1个外环+N个内环(洞)。
- 若把多部分所有环塞进单个Shapely Polygon,它会默认后续环为内环(洞),导致判断结果偏差;带洞多边形若未单独传入内环,也会混淆内外区域。
解决步骤
1. 明确Pyshp读取的几何结构
Pyshp读取的每个多边形shape有两个核心属性:
shape.points:按顺序排列的所有坐标点列表shape.parts:每个环的起始索引,比如[0,4]表示第一个环从第0个点开始,第二个从第4个点开始
2. 通过坐标方向区分外环/内环
Shapefile规则:
- 外环(多边形外边界)为逆时针方向
- 内环(洞)为顺时针方向
可通过计算多边形面积正负判断:面积为正对应逆时针(外环),为负对应顺时针(内环)。
3. 代码实现正确构造Shapely几何对象
以下是完整处理代码,包含Shapefile读取、多边形解析、Shapely对象构造及点-in-面判断:
import shapefile from shapely.geometry import Point, Polygon, MultiPolygon from shapely.geometry.polygon import orient def parse_shapefile_polygon(shape): # 分割坐标为各个环,补充结束索引方便切片 parts = shape.parts + [len(shape.points)] rings = [] for i in range(len(parts)-1): start_idx = parts[i] end_idx = parts[i+1] ring = shape.points[start_idx:end_idx] # 确保环闭合(部分Shapefile可能未自动闭合) if ring[0] != ring[-1]: ring.append(ring[0]) rings.append(ring) # 区分外环与内环 outer_rings = [] inner_rings = [] for ring in rings: poly = Polygon(ring) # 面积正为逆时针(外环),负为顺时针(内环) if poly.area > 0: outer_rings.append(ring) else: # 标准化内环方向(可选,确保符合Shapely要求) oriented_poly = orient(poly, sign=-1.0) inner_rings.append(list(oriented_poly.exterior.coords)) # 构造对应Shapely对象 if len(outer_rings) > 1: # 多部分分离多边形用MultiPolygon polygons = [Polygon(ring) for ring in outer_rings] return MultiPolygon(polygons) elif len(inner_rings) > 0: # 带洞多边形用Polygon(外环, [内环列表]) return Polygon(outer_rings[0], inner_rings) else: # 单部分多边形直接返回Polygon return Polygon(outer_rings[0]) # 读取目标Shapefile sf = shapefile.Reader("your_shapefile.shp") # 遍历处理每个多边形 for shape in sf.shapes(): if shape.shapeType == 5: # 5对应Shapefile的Polygon类型 shapely_poly = parse_shapefile_polygon(shape) # 测试点示例(根据你的实际坐标调整) test_point_a = Point(10, 10) # 假设在蓝色多边形A的其中一个正方形内 test_point_b_hole = Point(20, 20) # 假设在粉色多边形B的洞内 test_point_b_outer = Point(30, 30) # 假设在粉色多边形B的外环区域(非洞) print(f"点A是否在多边形内:{shapely_poly.contains(test_point_a)}") print(f"点B(洞内)是否在多边形内:{shapely_poly.contains(test_point_b_hole)}") print(f"点B(外环内)是否在多边形内:{shapely_poly.contains(test_point_b_outer)}")
关键说明
- 多部分分离多边形(如蓝色A)必须用
MultiPolygon包裹多个独立Polygon,Shapely才能正确识别每个独立区域,点在任意区域内都会返回True。 - 带洞多边形(如粉色B)需将内环作为第二个参数传入
Polygon构造函数,Shapely会自动排除洞内区域,点在洞内返回False。 - 代码中加入了环闭合判断,避免因Shapefile未闭合环导致的Shapely解析错误。
内容的提问来源于stack exchange,提问作者Mike Duke
相关产品推荐
相关产品推荐

