繁体   English   中英

将自相交线串拆分为非自相交线串

[英]Split self-intersecting linestring into non-self-intersecting linestrings

我有一个坐标列表,定义了一个可能与自身相交的线串:

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 Linestrings 构造了一个交集矩阵来查找交点:

from shapely.geometry import LineString
from itertools import product, zip_longest
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: # ignore connecting segments
            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]])

你可以读作:

  • 第 1 段与第 5 段和第 6 段相交
  • 第 2 段与第 5 段和第 6 段相交
  • 第 5 段与第 1 段和第 2 段相交
  • 第 6 段与第 1 段和第 2 段相交

还; 我认为“交叉点簇”的数量表示线串的数量: no_clusters + 1

我现在如何解决它......我改变了我的交叉点矩阵,所以在没有交叉点的情况下值为 1,在任何交叉点处的值为 0。

def get_intersection_matrix(coordinates):
    linestrings = [
        (ix, LineString([c0, c1]))
        for ix, (c0, c1) in enumerate(zip(coordinates[:-1], coordinates[1:]))
    ]
    M = np.ones((len(linestrings), len(linestrings)))
    for (ix0, ls0), (ix1, ls1) in combinations(linestrings, 2):
        if abs(ix0 - ix1) == 1:  # ignore connecting segments
            continue
        if ls0.intersects(ls1):
            M[ix0, ix1], M[ix1, ix0] = 0, 0
    return M
>> M = get_intersection_matrix(coordinates)
>> M
array([[1., 1., 1., 1., 0., 0.],
       [1., 1., 1., 1., 0., 0.],
       [1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1.],
       [0., 0., 1., 1., 1., 1.],
       [0., 0., 1., 1., 1., 1.]])
  • 拆分索引的任意组合由以下给出: itertools.combinations(range(1, len(M)), nr_split_ixs)其中ix1 < ix2 <... < ixn

在一个分割索引处,您会得到两个不应包含任何 0 的方块,并且这些方块可以通过最小和进行优化!

这是一个合法的(但不是最好的)拆分, split_ix = 4 ,两个框的总和为 16+4 = 20。 在此处输入图像描述

这是一个更好的合法(无零)拆分,其中两个框的总和为 9+9=18

在此处输入图像描述

得分分割指数的计算方法:

def get_scored_split_ixs_combination(M, nr_split_ixs):
    ixs_scores = []
    for ixs in combinations(range(1, len(M)), nr_split_ixs):
        splitted_matrix = [
            M[i0:i1, i0:i1] for i0, i1 in zip((0, *ixs), (*ixs, len(M)))
        ]
        # check if no matrices have zeros
        if not all([(m > 0).all() for m in splitted_matrix]):
            # ilegal ixs combination
            continue
        ixs_scores.append((ixs, sum([m.sum() for m in splitted_matrix])))
    return ixs_scores

如果返回为空,则没有合法选项,您应该增加拆分次数。

现在通过增加拆分次数返回最佳拆分选项:

def get_best_split_ixs_combination(M):
    nr_split_ixs = 0
    while True:
        ixs_scores = get_scored_split_ixs_combination(M, nr_split_ixs)
        if ixs_scores:
            return min(ixs_scores, key=lambda x: x[1])[0]
        nr_split_ixs +=1

>> get_best_split_ixs_combination(M)
(3,)

最后把它们包起来:

def get_non_intersecting_linestrings(coordinates):
    M = get_intersection_matrix(coordinates)
    split_indexes = get_best_split_ixs_combination(M)
    return [
        coordinates[i1:i2]
        for i1, i2 in zip([0] + split_indexes, split_indexes + [len(coordinates)])
    ]
>> get_non_intersecting_linestrings(coordinates)
[[[0, 3], [0, 5], [4, 5]], [[4, 0], [0, 0], [0, 5], [2, 5]]]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM