Shapely: Ajouter une fonctionnalité pour supprimer la 3ème dimension

Créé le 5 mai 2019  ·  3Commentaires  ·  Source: Toblerity/Shapely

Il y a une vieille question sur GIS Stack Exchange à propos de la conversion de géométries 3D en 2D : Convert 3D WKT to 2D Shapely Geometry . Je pense que cette fonctionnalité devrait être incluse dans Shapely, afin que nous puissions l'utiliser, par exemple, comme :

>>> from shapely.geometry import Polygon
>>> p = Polygon([(0, 0, 0), (1, 0, 0), (1, 1, 0)])
>>> p.wkt
'POLYGON Z ((0 0 0, 1 0 0, 1 1 0, 0 0 0))'
>>> p2 = p.drop_z
>>> p2.wkt
'POLYGON ((0 0, 1 0, 1 1, 0 0))'

J'ai vu dans l'une des réponses et dans la documentation que _cette opération n'est pas nécessaire_ car la 3ème dimension _n'a aucun effet sur l'analyse géométrique_ . Mais lors de l'implémentation de ma propre fonction pour déterminer le côté gauche d'une géométrie fractionnée (ce n'est pas encore implémenté : https://github.com/Toblerity/Shapely/issues/589), j'ai vu que cette fonctionnalité pouvait être utile :

from shapely.geometry import (LinearRing, 
                              LineString, 
                              Polygon)


def is_left(polygon: Polygon,
            line: LineString) -> bool:
    """
    Determines if the polygon is on the left side of the line
    according to:
    https://stackoverflow.com/questions/50393718/determine-the-left-and-right-side-of-a-split-shapely-geometry
    """
    ring = LinearRing([*line.coords, *polygon.centroid.coords])
    return ring.is_ccw

Ce code échouera pour les géométries 3D :

p = Polygon([(0, 0, 0), (1, 0, 0), (1, 1, 0)])
l = LineString([(0, 0, 0), (1, 0, 0)])
is_left(p, l)

donnera cette erreur:

AttributeError                            Traceback (most recent call last)
~/miniconda3/lib/python3.7/site-packages/shapely/speedups/_speedups.pyx in shapely.speedups._speedups.geos_linearring_from_py()

AttributeError: 'list' object has no attribute '__array_interface__'

During handling of the above exception, another exception occurred:

IndexError                                Traceback (most recent call last)
<ipython-input-55-555b9e2533fa> in <module>()
----> 1 is_left(p, l)

<ipython-input-52-7fad75b19ce3> in is_left(polygon, line)
      6     https://stackoverflow.com/questions/50393718/determine-the-left-and-right-side-of-a-split-shapely-geometry
      7     """
----> 8     ring = LinearRing([*line.coords, *polygon.centroid.coords])
      9     return ring.is_ccw

~/miniconda3/lib/python3.7/site-packages/shapely/geometry/polygon.py in __init__(self, coordinates)
     51         BaseGeometry.__init__(self)
     52         if coordinates is not None:
---> 53             self._set_coords(coordinates)
     54 
     55     <strong i="18">@property</strong>

~/miniconda3/lib/python3.7/site-packages/shapely/geometry/polygon.py in _set_coords(self, coordinates)
     66     def _set_coords(self, coordinates):
     67         self.empty()
---> 68         ret = geos_linearring_from_py(coordinates)
     69         if ret is not None:
     70             self._geom, self._ndim = ret

~/miniconda3/lib/python3.7/site-packages/shapely/speedups/_speedups.pyx in shapely.speedups._speedups.geos_linearring_from_py()

IndexError: tuple index out of range

Cela est dû au fait que le centroïde d'un Polygon est toujours renvoyé en 2D (https://github.com/Toblerity/Shapely/issues/554), et qu'un LinearRing ne peut pas être construit à partir de points ayant un nombre différent de dimensions.

S'il y avait une méthode drop_z , j'écrirais simplement ring = LinearRing([*line.drop_z.coords, *polygon.centroid.coords]) au lieu d'encombrer le code avec des choses comme line = LineString([xy[:2] for xy in list(line.coords)]) ou d'implémenter une fonction pour cela. Ou mieux encore, je supprimerais la 3ème dimension redondante composée uniquement de zéros du polygone parent d'origine que je lis à partir d'un fichier au niveau supérieur, de sorte que toutes les géométries enfants n'auraient que 2 dimensions.


Version galbée : 1.6.4.post1, installée à partir de conda.

wontfix

Commentaire le plus utile

Pour les autres personnes arrivant ici de Google, j'ai trouvé une méthode très simple :

def _to_2d(x, y, z):
    return tuple(filter(None, [x, y]))

new_shape = shapely.ops.transform(_to_2d, shape)

(crédit à @feenster et @hunt3ri de https://github.com/hotosm/tasking-manager/blob/master/server/services/grid/grid_service.py :heart:)

Tous les 3 commentaires

@ LostFan123 Je pense qu'une méthode pour supprimer les valeurs Z serait un ajout approprié pour 1.7. Merci de l'avoir suggéré.

Pour les autres personnes arrivant ici de Google, j'ai trouvé une méthode très simple :

def _to_2d(x, y, z):
    return tuple(filter(None, [x, y]))

new_shape = shapely.ops.transform(_to_2d, shape)

(crédit à @feenster et @hunt3ri de https://github.com/hotosm/tasking-manager/blob/master/server/services/grid/grid_service.py :heart:)

Je vais fermer ce problème en faveur de la solution à 2 lignes ci-dessus. Merci @Juanlu001 !

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

mromanie picture mromanie  ·  3Commentaires

chivasblue picture chivasblue  ·  3Commentaires

sgillies picture sgillies  ·  5Commentaires

akadouri picture akadouri  ·  4Commentaires

FuriousRococo picture FuriousRococo  ·  5Commentaires