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.
Ubuntu 18.04.2 LTS
Python 3.6.7 - Shapely 1.6.4.post2 (instalado do PyPI usando pip)
OK. Eu vejo o problema aqui.
Visualmente, essas cadeias de linhas se parecem com isto:
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.
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 cruzamline
e tirar a diferença apenas deles. Mas isso não resolveria o caso em que uma única LineStringsplitter
gancho de volta para tocar oline
que cruza em outro ponto. Acho que a única maneira de obter um comportamento consistente aqui ao usardifference
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 deline
tocasplitter
. Na verdade, este caso foi testado . Mas nosso caso atual, onde o limite desplitter
tocaline
, 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.