Shapely: Comportamento inconsistente de ops.split com LineStrings e MultiLineStrings

Criado em 11 abr. 2019  ·  5Comentários  ·  Fonte: Toblerity/Shapely

Comportamento esperado e comportamento real e etapas para reproduzir o problema

Dividir uma cadeia de linha A com uma cadeia de linha de interseção B não produz a saída esperada (retorna A). Dividir A com uma cadeia multilinha contendo B e C (C também cruzando com A) produz a saída desejada (3 segmentos). Isso parece inconsistente.

from shapely.ops import split
from shapely.geometry import LineString, MultiLineString

A = LineString([(0, 0), (10, 0)])
B = LineString([(5, 0), (5, 5)])
C = LineString([(1, -1), (1, 1)])
assert (split(A, B).wkt == "GEOMETRYCOLLECTION (LINESTRING (0 0, 10 0))")
# Does not split
# Expected: "GEOMETRYCOLLECTION (LINESTRING (0 0, 5 0), LINESTRING (5 0, 10 0))"

assert (A.intersection(B).wkt == "POINT (5 0)")
# Although A and B do intersect

assert (split(A, MultiLineString([B, C])).wkt ==
        "GEOMETRYCOLLECTION (LINESTRING (0 0, 1 0), LINESTRING (1 0, 5 0), LINESTRING (5 0, 10 0))")
# OK - A is split by both B and C - but A was not split by B in the previous example.

Sistema operacional

Ubuntu 18.04.2 LTS

Versão bem formada e proveniência

Python 3.6.7 - Shapely 1.6.4.post2 (instalado do PyPI usando pip)

Comentários muito úteis

Acho que funcionaria bem, sim. E o comportamento faz sentido para mim.

Por um momento, pensei que poderíamos filtrar os goems em uma MultiLineString splitter para aqueles que cruzam line e tirar a diferença apenas deles. Mas isso não resolveria o caso em que uma única LineString splitter gancho de volta para tocar o line que cruza em outro ponto. Acho que a única maneira de obter um comportamento consistente aqui ao usar difference para o trabalho pesado é dividir as geometrias lineares de toque.

Eu suspeito que talvez crosses sido usado, então a função retornou cedo quando apenas o limite de line toca splitter . Na verdade, este caso foi testado . Mas nosso caso atual, onde o limite de splitter toca line , não está nos testes e pode ter sido um ponto cego. No entanto, sou um nativo Shapely sem muita experiência com outras bibliotecas de recursos simples. E não sei se apenas a divisão ao cruzar é o comportamento padrão para geometrias lineares.

Foi há 5 anos, mas se @georgeouzou se lembrar, talvez ele possa confirmar ou explicar por que crosses foi usado?

Podemos ter nosso bolo e comê-lo também se quisermos voltar mais cedo no caso testado, mas continuar com a divisão no caso desta edição. Isso exigiria o uso de relate diretamente. Mas eu duvido que haja uma grande diferença de desempenho de qualquer maneira.

Todos 5 comentários

OK. Eu vejo o problema aqui.

Visualmente, essas cadeias de linhas se parecem com isto:

image

Quando você divide a horizontal ( A ) pela grande vertical no topo ( B ) que apenas toca A , nada acontece. Mas dividir A por C funciona porque C cruza A.

Agora, quando você divide A por MultiLineString([B, C]) , ele se divide em três cadeias de linha, não duas como você esperaria. Eu concordo que isso parece inconsistente.

O código que causa isso é bastante acessível ( link permanente ).

    <strong i="26">@staticmethod</strong>
    def _split_line_with_line(line, splitter):
        """Split a LineString with another (Multi)LineString or (Multi)Polygon"""

        # if splitter is a polygon, pick it's boundary
        if splitter.type in ('Polygon', 'MultiPolygon'):
            splitter = splitter.boundary

        assert(isinstance(line, LineString))
        assert(isinstance(splitter, LineString) or isinstance(splitter, MultiLineString))

        if splitter.crosses(line):
            # The lines cross --> return multilinestring from the split
            return line.difference(splitter)
        elif splitter.relate_pattern(line, '1********'):
            # The lines overlap at some segment (linear intersection of interiors)
            raise ValueError('Input geometry segment overlaps with the splitter.')
        else:
            # The lines do not cross --> return collection with identity line
            return [line]

Você pode ver que splitter é necessário para cruzar line para que qualquer divisão comece. Mas uma vez que esse ponto de verificação é passado, uma chamada simples para difference é feita para gerar as geometrias divididas. Mas difference não discrimina entre cruzar e simplesmente tocar! Portanto, se em qualquer ponto splitter cruzar line , todos os pontos meramente tocantes serão divididos.

@ Jeremiah-England obrigado por se aprofundar nisso! Parece que, pelo menos para as linhas, poderíamos mudar o teste de cruzamentos para um teste de toques e obter resultados menos surpreendentes? O que você acha disso?

Acho que funcionaria bem, sim. E o comportamento faz sentido para mim.

Por um momento, pensei que poderíamos filtrar os goems em uma MultiLineString splitter para aqueles que cruzam line e tirar a diferença apenas deles. Mas isso não resolveria o caso em que uma única LineString splitter gancho de volta para tocar o line que cruza em outro ponto. Acho que a única maneira de obter um comportamento consistente aqui ao usar difference para o trabalho pesado é dividir as geometrias lineares de toque.

Eu suspeito que talvez crosses sido usado, então a função retornou cedo quando apenas o limite de line toca splitter . Na verdade, este caso foi testado . Mas nosso caso atual, onde o limite de splitter toca line , não está nos testes e pode ter sido um ponto cego. No entanto, sou um nativo Shapely sem muita experiência com outras bibliotecas de recursos simples. E não sei se apenas a divisão ao cruzar é o comportamento padrão para geometrias lineares.

Foi há 5 anos, mas se @georgeouzou se lembrar, talvez ele possa confirmar ou explicar por que crosses foi usado?

Podemos ter nosso bolo e comê-lo também se quisermos voltar mais cedo no caso testado, mas continuar com a divisão no caso desta edição. Isso exigiria o uso de relate diretamente. Mas eu duvido que haja uma grande diferença de desempenho de qualquer maneira.

@ Jeremiah-England minha memória está um pouco turva neste, mas vendo o código novamente posso confirmar que este era um ponto cego. O código também deve testar para que o caso atual seja concluído. Você poderia continuar com as mudanças?

Olá @georgeouzou , obrigado por

E, sim, se estiver tudo bem para todos, gostaria de resolver este caso e enviar um PR em algum momento.

Esta página foi útil?
0 / 5 - 0 avaliações