Shapely: Agregar funcionalidad para eliminar la tercera dimensión

Creado en 5 may. 2019  ·  3Comentarios  ·  Fuente: Toblerity/Shapely

Hay una vieja pregunta en GIS Stack Exchange sobre la conversión de geometrías 3D a 2D: Convert 3D WKT to 2D Shapely Geometry . Creo que esta funcionalidad debería incluirse en Shapely, por lo que podríamos usarla, por ejemplo, como:

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

He visto en una de las respuestas y en los documentos que _esta operación no es necesaria_ ya que la tercera dimensión _no tiene efecto en el análisis geométrico_ . Pero al implementar mi propia función para determinar el lado izquierdo de una geometría dividida (esto aún no está implementado: https://github.com/Toblerity/Shapely/issues/589), vi que esta funcionalidad podría ser útil:

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

Este código fallará para geometrías 3D:

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

dará este error:

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

Esto se debe al hecho de que el centroide de un Polygon siempre se devuelve en 2D (https://github.com/Toblerity/Shapely/issues/554), y un LinearRing no puede construirse a partir de puntos que tienen un número diferente de dimensiones.

Si hubiera un método drop_z , simplemente escribiría ring = LinearRing([*line.drop_z.coords, *polygon.centroid.coords]) en lugar de saturar el código con cosas como line = LineString([xy[:2] for xy in list(line.coords)]) o implementar una función para eso. O incluso mejor, eliminaría la tercera dimensión redundante que consiste solo en ceros del polígono principal original que leí de un archivo en el nivel superior, por lo que todas las geometrías secundarias tendrían solo 2 dimensiones.


Versión bien formada: 1.6.4.post1, instalada desde conda.

wontfix

Comentario más útil

Para otras personas que llegan aquí desde Google, encontré un método muy simple:

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

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

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

Todos 3 comentarios

@ LostFan123 Creo que un método para eliminar los valores Z sería una adición adecuada para 1.7. Gracias por sugerirlo.

Para otras personas que llegan aquí desde Google, encontré un método muy simple:

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

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

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

Voy a cerrar este problema a favor de la solución de 2 líneas anterior. Gracias @Juanlu001 !

¿Fue útil esta página
0 / 5 - 0 calificaciones