简体   繁体   English

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

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

I have a list of coordinates defining a line string that might intersect with itself:我有一个坐标列表,定义了一个可能与自身相交的线串:

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

How can I split the linestring into smaller linestrings so none of the linestrings intersects with itself?如何将线串拆分成更小的线串,使所有线串都不与自身相交?

  • smallest number of linestrings最小数量的线串
  • line strings should have equal number of coordinates as possible线串应尽可能具有相同数量的坐标

the desired outcome in this case would be:在这种情况下,期望的结果是:

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

My attempt我的尝试

In my attempt so far I construct an intersection matrix using Shapely Linestrings to find the intersections:到目前为止,在我的尝试中,我使用 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

which outputs what I call the "intersection matrix":它输出我所说的“交集矩阵”:

>> 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]])

That you can read as:你可以读作:

  • segment 1 intersects with segment 5 and 6第 1 段与第 5 段和第 6 段相交
  • segment 2 intersects with segment 5 and 6第 2 段与第 5 段和第 6 段相交
  • segment 5 intersects with segment 1 and 2第 5 段与第 1 段和第 2 段相交
  • segment 6 intersects with segment 1 and 2第 6 段与第 1 段和第 2 段相交

Also;还; I think that the number of "intersection clusters" indicate the number of linestrings: no_clusters + 1我认为“交叉点簇”的数量表示线串的数量: no_clusters + 1

How I solve it now... I changed my intersection matrix, so at no intersection the value is 1 and at any intersection the value is 0.我现在如何解决它......我改变了我的交叉点矩阵,所以在没有交叉点的情况下值为 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.]])
  • any combination of split indexes is given by: itertools.combinations(range(1, len(M)), nr_split_ixs) where also ix1 < ix2 <... < ixn拆分索引的任意组合由以下给出: itertools.combinations(range(1, len(M)), nr_split_ixs)其中ix1 < ix2 <... < ixn

at one split index you get two squares that should not contain any 0's, and the squares can be optimized by a minimum sum!在一个分割索引处,您会得到两个不应包含任何 0 的方块,并且这些方块可以通过最小和进行优化!

This is a legal (but not the best) split with split_ix = 4 and the sum of the two boxes is 16+4 = 20.这是一个合法的(但不是最好的)拆分, split_ix = 4 ,两个框的总和为 16+4 = 20。 在此处输入图像描述

This is a better legal (no zeros) split where the sum of the two boxes is 9+9=18这是一个更好的合法(无零)拆分,其中两个框的总和为 9+9=18

在此处输入图像描述

The method to calculate the scored split indexes:得分分割指数的计算方法:

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

if the return is empty there are no legal options and you should increase the number of splits.如果返回为空,则没有合法选项,您应该增加拆分次数。

Now return the best split option by increment the number of splits:现在通过增加拆分次数返回最佳拆分选项:

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,)

And finally wrap it all together:最后把它们包起来:

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