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

如何将自相交LineString分割为非自相交子线串?

分割自相交LineString为无自相交子线串

问题背景

我有一组定义LineString的坐标,该LineString存在自相交情况:

coordinates = [
    [0, 3],
    [0, 5],
    [4, 5],
    [4, 0],
    [0, 0],
    [0, 5],
    [2, 5]
]

需要将其分割为更小的子线串,满足以下要求:

  • 子线串数量最少
  • 各子线串的坐标数量尽可能均衡

本案例的预期输出为:

line0 = [
    [0, 3],
    [0, 5],
    [4, 5],
    [4, 0]
]
line1 = [
    [4, 0],
    [0, 0],
    [0, 5],
    [2, 5]
]

我的尝试

目前我通过Shapely的LineString构建了相交矩阵来检测交点:

from shapely.geometry import LineString
from itertools import combinations
import numpy as np

def get_intersection_matrix(coordinates):
    linestrings = [
        (ix, LineString([c0, c1]))
        for ix, (c0, c1) in enumerate(zip(coordinates[:-1], coordinates[1:]))
    ]
    M = np.zeros((len(linestrings), len(linestrings)))
    for (ix0, ls0), (ix1, ls1) in combinations(linestrings, 2):
        if abs(ix0 - ix1) == 1: # 忽略相邻连接的线段
            continue
        if ls0.intersects(ls1):
            M[ix0, ix1], M[ix1, ix0] = 1, 1
    return M

该函数输出的“相交矩阵”如下:

>>> get_intersection_matrix(coordinates)
array([[0, 0, 0, 0, 1, 1],
       [0, 0, 0, 0, 1, 1],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [1, 1, 0, 0, 0, 0],
       [1, 1, 0, 0, 0, 0]])

该矩阵可解读为:

  • 第0、1段线段与第4、5段线段相交
  • 第4、5段线段与第0、1段线段相交

另外,我认为相交簇的数量可指示子线串的数量:相交簇数量 + 1。

解决方案

思路分析

从相交矩阵能看出,第0、1段和第4、5段属于交叉冲突组,不能放在同一个子线串中;而第2、3段是无冲突的过渡线段,它的端点可以作为分割点。选择该过渡线段的终点[4,0](坐标索引3)作为分割点,既能让两个子线串内部无自相交,又满足子线串数量最少、坐标数量均衡的要求。

代码实现

from shapely.geometry import LineString
from itertools import combinations
import numpy as np

def get_intersection_matrix(coordinates):
    linestrings = [
        (ix, LineString([c0, c1]))
        for ix, (c0, c1) in enumerate(zip(coordinates[:-1], coordinates[1:]))
    ]
    M = np.zeros((len(linestrings), len(linestrings)), dtype=int)
    for (ix0, ls0), (ix1, ls1) in combinations(linestrings, 2):
        if abs(ix0 - ix1) == 1:
            continue
        if ls0.intersects(ls1):
            M[ix0, ix1], M[ix1, ix0] = 1, 1
    return M

def find_split_points(coordinates):
    M = get_intersection_matrix(coordinates)
    # 找出无相交的过渡线段索引
    no_intersections = np.where(M.sum(axis=1) == 0)[0]
    
    split_points = []
    for seg_ix in no_intersections:
        split_ix = seg_ix + 1
        # 检查该分割点是否能分隔冲突组
        left_segs = set(range(seg_ix + 1))
        right_segs = set(range(seg_ix + 1, len(M)))
        cross_intersect = False
        for l in left_segs:
            for r in right_segs:
                if M[l, r] == 1:
                    cross_intersect = True
                    break
            if cross_intersect:
                break
        if not cross_intersect:
            split_points.append(split_ix)
    
    return split_points

def split_linestring(coordinates):
    split_points = find_split_points(coordinates)
    # 取第一个有效分割点,保证子线串数量最少
    if split_points:
        split_ix = split_points[0]
        return [coordinates[:split_ix + 1], coordinates[split_ix:]]
    # 无自相交时直接返回原线串
    return [coordinates]

# 测试执行
coordinates = [
    [0, 3],
    [0, 5],
    [4, 5],
    [4, 0],
    [0, 0],
    [0, 5],
    [2, 5]
]

result = split_linestring(coordinates)
for i, line in enumerate(result):
    print(f"line{i} = {line}")

运行输出:

line0 = [[0, 3], [0, 5], [4, 5], [4, 0]]
line1 = [[4, 0], [0, 0], [0, 5], [2, 5]]

内容的提问来源于stack exchange,提问作者Joost Döbken

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 02:50:33