You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

循环处理多几何图形时triangle库触发段错误问题求助

问题:Shapely多边形约束三角剖分的段错误与替代方案需求

我需要将Shapely多边形分解为三角形,约束条件是三角形顶点只能位于多边形边上。

目前代码多数情况运行正常,但处理某个特定多边形时触发段错误:

(进程退出码139,被信号11:SIGSEGV中断):POLYGON ((545155.9709965222 5378933.039350484,545155.8786686319 5378932.570194424, 545153.9828366871 5378934.823291995, 545154.3704756413 5378934.7733652275, 545155.9709965222 5378933.039350484))

更奇怪的是:逐行执行代码处理该特定多边形时完全正常,但放入循环遍历整个GeoDataFrame时就崩溃。

运行环境:Python 3.12,triangle库版本20230923。

另外求Python替代方案——Shapely自带的三角剖分会生成内部顶点,不满足我的约束需求。

我的代码如下:

import os
import geopandas as gpd
from shapely.geometry import Point, MultiPolygon, Polygon, GeometryCollection
from shapely.validation import explain_validity
import shapely.geometry as geom
import numpy as np
import triangle as tr


def is_triangle(geometry):
    if isinstance(geometry, Polygon):
        # 单个多边形:判断是否有3个顶点(闭合多边形首尾重复,所以坐标数为4)
        return len(list(geometry.exterior.coords)) == 4
    elif isinstance(geometry, MultiPolygon):
        # 多面多边形:检查每个子多边形都是三角形
        return all(len(list(poly.exterior.coords)) == 4 for poly in geometry.geoms)
    else:
        return False  # 不支持的几何类型


def process_geometry(geometry):
    # 检查几何有效性,无效则尝试用buffer(0)修复
    if not geometry.is_valid:
        print(f"Invalid geometry: {explain_validity(geometry)}")
        geometry = geometry.buffer(0)

    if is_triangle(geometry):
        return [geometry]  # 返回列表保持格式一致
    elif isinstance(geometry, Polygon):
        return triangulate_polygon(geometry)  # 剖分并返回三角形列表
    elif isinstance(geometry, MultiPolygon):
        triangles = []
        for poly in geometry.geoms:
            triangles.extend(triangulate_polygon(poly))  # 剖分每个子多边形
        return triangles
    else:
        raise ValueError("Geometry type not supported")

def triangulate_polygon(geometry):
    # 移除近重复顶点,避免浮点精度问题
    cleaned_coords = remove_near_duplicate_vertices(np.array(geometry.exterior.coords))
    polygon = Polygon(cleaned_coords)

    coords = np.array(polygon.exterior.coords)

    # 检查退化或共线段
    for i in range(len(coords) - 1):
        dist = np.linalg.norm(coords[i] - coords[i + 1])
        if dist < 1e-8:
            print(f"Nearly degenerate segment between points {i} and {i + 1}")

    # 确保有足够的唯一点用于剖分
    if len(coords) < 3:
        return []
    segments = [[i, (i + 1) % len(coords)] for i in range(len(coords) - 1)]

    # 检查线段索引是否越界
    for segment in segments:
        if any(idx >= len(coords) for idx in segment):
            print(f"Invalid segment index {segment}")

    # 使用triangle库执行剖分
    triangulation = tr.triangulate({'vertices': coords, 'segments': segments}, 'p')

    triangles = create_triangles_from_triangulation(triangulation)

    return triangles


def create_triangles_from_triangulation(triangulation):
    """将剖分结果转为Shapely多边形对象"""
    triangles = []
    if 'triangles' in triangulation:
        coords = triangulation['vertices']
        for tri_indices in triangulation['triangles']:
            triangle_coords = coords[tri_indices]
            triangles.append(geom.Polygon(triangle_coords))
    return triangles


all_triangles = []

for id, tri in non_triangles_gdf.iterrows():
    print(tri)
    print(id)
    print(tri['geometry'])
    geom = tri['geometry']
    all_triangles.extend(process_geometry(geom))
问题排查与修复建议

1. 补全缺失的函数

代码中调用了remove_near_duplicate_vertices但未实现,这可能导致循环中出现隐藏的状态异常,先补全该函数:

def remove_near_duplicate_vertices(coords, threshold=1e-8):
    """移除距离小于阈值的相邻顶点,确保多边形闭合"""
    cleaned = [coords[0]]
    for point in coords[1:]:
        if np.linalg.norm(point - cleaned[-1]) > threshold:
            cleaned.append(point)
    # 保证首尾点一致,维持多边形闭合
    if np.linalg.norm(cleaned[0] - cleaned[-1]) > threshold:
        cleaned.append(cleaned[0])
    return np.array(cleaned)

2. 解决triangle库的段错误

可能原因:

  • triangle库的C底层在循环中存在内存泄漏或状态残留,导致累积错误
  • 特定多边形的顶点过于接近,触发底层的退化处理逻辑崩溃

修复方案:

  • 进程隔离:在循环中用子进程处理每个多边形,避免状态残留。示例:
    from multiprocessing import Pool
    
    def process_single_geom(geom):
        try:
            return process_geometry(geom)
        except Exception as e:
            print(f"Error processing geometry: {e}")
            return []
    
    # 批量处理
    with Pool() as pool:
        results = pool.map(process_single_geom, non_triangles_gdf['geometry'].tolist())
    for res in results:
        all_triangles.extend(res)
    
  • 坐标缩放:将特定多边形的坐标放大(比如乘以1e6),剖分后再缩小,避免浮点精度问题:
    def triangulate_polygon(geometry):
        cleaned_coords = remove_near_duplicate_vertices(np.array(geometry.exterior.coords))
        # 缩放坐标
        scale = 1e6
        scaled_coords = cleaned_coords * scale
        polygon = Polygon(scaled_coords)
    
        coords = np.array(polygon.exterior.coords)
        # ... 原有检查逻辑 ...
    
        triangulation = tr.triangulate({'vertices': coords, 'segments': segments}, 'p')
    
        # 缩放回原坐标
        triangles = []
        if 'triangles' in triangulation:
            coords = triangulation['vertices'] / scale
            for tri_indices in triangulation['triangles']:
                triangle_coords = coords[tri_indices]
                triangles.append(geom.Polygon(triangle_coords))
        return triangles
    
替代方案

1. 使用trimesh库(易安装,支持约束剖分)

trimesh的平面网格生成会严格保留原多边形的顶点,满足约束需求:

import trimesh
import shapely.geometry as geom

def triangulate_with_trimesh(polygon):
    # 将Shapely多边形转为trimesh路径
    path = trimesh.load_path(polygon.exterior.coords)
    # 生成带约束的平面三角网格
    mesh = path.to_planar_mesh()
    # 转为Shapely三角形列表
    triangles = []
    for face in mesh.faces:
        tri_coords = mesh.vertices[face]
        # 闭合多边形
        tri_coords = np.vstack([tri_coords, tri_coords[0]])
        triangles.append(geom.Polygon(tri_coords))
    return triangles

2. 使用CGAL的约束Delaunay剖分(精度更高)

通过pycgal调用CGAL的约束三角剖分功能,需要先安装CGAL(可通过conda安装pycgal):

from CGAL.CGAL_Triangulation_2 import Constrained_Delaunay_triangulation_2
from CGAL.CGAL_Kernel import Point_2
import shapely.geometry as geom

def triangulate_with_cgal(polygon):
    # 提取多边形顶点(去掉闭合的重复点)
    coords = np.array(polygon.exterior.coords)[:-1]
    # 转为CGAL点对象
    cgal_points = [Point_2(x, y) for x, y in coords]
    # 初始化约束Delaunay剖分
    cdt = Constrained_Delaunay_triangulation_2()
    # 添加多边形边作为约束
    for i in range(len(cgal_points)):
        next_idx = (i + 1) % len(cgal_points)
        cdt.insert_constraint(cgal_points[i], cgal_points[next_idx])
    # 提取所有有限面(三角形)
    triangles = []
    for face in cdt.finite_faces():
        # 获取三个顶点坐标
        pts = [(face.vertex(j).point().x(), face.vertex(j).point().y()) for j in range(3)]
        # 闭合多边形
        pts.append(pts[0])
        triangles.append(geom.Polygon(pts))
    return triangles

内容的提问来源于stack exchange,提问作者Judith Levy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 19:27:02