Le fractionnement d'une chaîne de lignes A avec une chaîne de lignes B en intersection ne produit pas la sortie attendue (renvoie A). La division de A avec une chaîne multiligne contenant B et C (C recoupant également A) produit la sortie souhaitée (3 segments). Cela semble incohérent.
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 (installé à partir de PyPI à l'aide de pip)
D'accord. Je vois le problème ici.
Visuellement, ces lignes de ligne ressemblent à ceci :
Lorsque vous divisez l'horizontale ( A ) par la grande verticale du haut ( B ) qui ne touche que A , rien ne se passe. Mais diviser A par C fonctionne car C traverse A .
Maintenant, lorsque vous divisez A par MultiLineString([B, C])
, il se divise en trois chaînes de ligne, et non en deux comme vous vous en doutez. Je suis d'accord que cela semble incohérent.
Le code à l'origine de cela est en fait assez accessible ( permalien ).
<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]
Vous pouvez voir que le splitter
doit traverser line
pour que tout fractionnement commence. Mais une fois ce point de contrôle passé, un simple appel à difference
est effectué pour générer les géométries fractionnées. Mais difference
ne fait pas de distinction entre le croisement et le simple toucher ! Donc, si à un moment quelconque splitter
croise line
, alors tous les points qui se touchent simplement seront divisés.
@Jeremiah-England merci d'avoir creusé cela ! Il semble que, pour les lignes au moins, nous pourrions changer le test des croix en un test des touches et obtenir des résultats moins surprenants ? Qu'est ce que tu penses de ça?
Je pense que cela fonctionnerait bien, oui. Et le comportement a du sens pour moi.
Pendant un moment, j'ai pensé que nous pourrions filtrer les goems dans une MultiLineString splitter
pour ceux qui croisent line
et prendre la différence entre ceux-ci uniquement. Mais cela ne résoudrait pas le cas où une seule chaîne splitter
ligne line
qu'elle croise à un autre point. Je pense que la seule façon d'obtenir un comportement cohérent ici tout en utilisant difference
pour le levage de charges lourdes est de diviser les géométries linéaires qui se touchent.
Je soupçonne que crosses
a peut-être été utilisé, donc la fonction est revenue tôt lorsque seule la limite de line
touche le splitter
. En fait, ce cas est testé pour . Mais notre cas actuel, où la frontière de splitter
touche line
, n'est pas dans les tests, et peut avoir été un angle mort. Cependant, je suis un natif de Shapely sans grande expérience avec d'autres bibliothèques de fonctionnalités simples. Et je ne sais pas si seul le fractionnement lors du croisement est un comportement standard pour les géométries linéaires.
C'était il y a 5 ans, mais si @georgeouzou se souvient peut-être qu'il pourrait confirmer ou expliquer pourquoi crosses
été utilisé ?
Nous pouvons avoir notre gâteau et le manger aussi si nous voulons revenir tôt dans le cas testé, mais continuer à diviser dans le cas de ce numéro. Cela nécessiterait d'utiliser directement relate
. Mais je doute qu'il y ait une grande différence de performance de toute façon.
@Jeremiah-England ma mémoire est un peu trouble sur celui-ci, mais en voyant à nouveau le code, je peux confirmer qu'il s'agissait d'un angle mort. Le code doit également vérifier que le cas actuel est complet. Pourriez-vous continuer avec les changements ?
Salut @georgeouzou , merci d'avoir pris le temps de regarder ça !
Et, oui, si tout le monde est d'accord, j'aimerais résoudre ce cas et soumettre un PR à un moment donné.
Commentaire le plus utile
Je pense que cela fonctionnerait bien, oui. Et le comportement a du sens pour moi.
Pendant un moment, j'ai pensé que nous pourrions filtrer les goems dans une MultiLineString
splitter
pour ceux qui croisentline
et prendre la différence entre ceux-ci uniquement. Mais cela ne résoudrait pas le cas où une seule chaînesplitter
ligneline
qu'elle croise à un autre point. Je pense que la seule façon d'obtenir un comportement cohérent ici tout en utilisantdifference
pour le levage de charges lourdes est de diviser les géométries linéaires qui se touchent.Je soupçonne que
crosses
a peut-être été utilisé, donc la fonction est revenue tôt lorsque seule la limite deline
touche lesplitter
. En fait, ce cas est testé pour . Mais notre cas actuel, où la frontière desplitter
toucheline
, n'est pas dans les tests, et peut avoir été un angle mort. Cependant, je suis un natif de Shapely sans grande expérience avec d'autres bibliothèques de fonctionnalités simples. Et je ne sais pas si seul le fractionnement lors du croisement est un comportement standard pour les géométries linéaires.C'était il y a 5 ans, mais si @georgeouzou se souvient peut-être qu'il pourrait confirmer ou expliquer pourquoi
crosses
été utilisé ?Nous pouvons avoir notre gâteau et le manger aussi si nous voulons revenir tôt dans le cas testé, mais continuer à diviser dans le cas de ce numéro. Cela nécessiterait d'utiliser directement
relate
. Mais je doute qu'il y ait une grande différence de performance de toute façon.