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.
@ 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 !
Comentario más útil
Para otras personas que llegan aquí desde Google, encontré un método muy simple:
(crédito a @feenster y @hunt3ri de https://github.com/hotosm/tasking-manager/blob/master/server/services/grid/grid_service.py :heart :)