Unfortunately, Shapely doesn't provide the functionality to extract immediately all points from a MultiPolygon
object. Instead, you would have to first, iterate over individual polygons of a MultiPolygon
, and second, extract individual points of each Polygon
.
One could come up with different ways to approach the problem. For example, if you know that none of your polygons have holes, you could simply do the following:
points = []
for polygon in multipolygon:
points.extend(polygon.exterior.coords[:-1])
Note the [:-1]
which prevents duplicating the first vertex. You can remove it if you'd like to have a cleaner syntax and don't care about having one duplicate point for each polygon.
This can also be written in one line using list comprehension with two loops:
points = [point for polygon in multipolygon for point in polygon.exterior.coords[:-1]]
or with the help of itertools.chain.from_iterable
:
from itertools import chain
points = list(chain.from_iterable(polygon.exterior.coords[:-1] for polygon in multipolygon))
In general, when the polygons can contain holes, we could, for example, write the following function to extract coordinates from the interior rings:
def to_coords(multipolygon):
for polygon in multipolygon:
yield from polygon.exterior.coords[:-1]
yield from chain.from_iterable(interior.coords[:-1] for interior in polygon.interiors)
Example of usage:
mp = MultiPolygon([Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
Polygon([(2, 0), (3, 0), (3, 1), (2, 1)],
holes=[[(2.25, 0.25), (2.75, 0.25), (2.75, 0.75), (2.25, 0.75)]])])
points = list(to_coords(mp))
# [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0),
# (2.0, 0.0), (3.0, 0.0), (3.0, 1.0), (2.0, 1.0),
# (2.25, 0.25), (2.75, 0.25), (2.75, 0.75), (2.25, 0.75)]
One could go even furter and generalize this for any input geometry (Python ≥3.7):
from functools import singledispatch
from itertools import chain
from typing import (List,
Tuple,
TypeVar)
from shapely.geometry import (GeometryCollection,
LinearRing,
LineString,
Point,
Polygon)
from shapely.geometry.base import (BaseGeometry,
BaseMultipartGeometry)
Geometry = TypeVar('Geometry', bound=BaseGeometry)
@singledispatch
def to_coords(geometry: Geometry) -> List[Tuple[float, float]]:
"""Returns a list of unique vertices of a given geometry object."""
raise NotImplementedError(f"Unsupported Geometry {type(geometry)}")
@to_coords.register
def _(geometry: Point):
return [(geometry.x, geometry.y)]
@to_coords.register
def _(geometry: LineString):
return list(geometry.coords)
@to_coords.register
def _(geometry: LinearRing):
return list(geometry.coords[:-1])
@to_coords.register
def _(geometry: BaseMultipartGeometry):
return list(set(chain.from_iterable(map(to_coords, geometry))))
@to_coords.register
def _(geometry: Polygon):
return to_coords(GeometryCollection([geometry.exterior, *geometry.interiors]))
Example of usage:
from shapely.geometry import (MultiLineString,
MultiPoint,
MultiPolygon)
geometry_objects = [Point(0, 0),
LineString([(0, 0), (1, 1)]),
LinearRing([(0, 0), (1, 0), (1, 1)]),
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)],
holes=[[(0.25, 0.25), (0.75, 0.25), (0.75, 0.75), (0.25, 0.75)]]),
MultiPoint([(0, 0), (1, 1)]),
MultiLineString([LineString([(0, 0), (1, 1)]), LineString([(2, 0), (3, 1)])]),
MultiPolygon([Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
Polygon([(2, 0), (3, 0), (3, 1), (2, 1)],
holes=[[(2.25, 0.25), (2.75, 0.25), (2.75, 0.75), (2.25, 0.75)]])]),
GeometryCollection([Point(0, 0), LineString([(0, 0), (1, 1)])])]
for geometry in geometry_objects:
print(f"For {geometry.wkt}\nwe got:\n"
f"{to_coords(geometry)}\n")
Output:
For POINT (0 0)
we got:
[(0.0, 0.0)]
For LINESTRING (0 0, 1 1)
we got:
[(0.0, 0.0), (1.0, 1.0)]
For LINEARRING (0 0, 1 0, 1 1, 0 0)
we got:
[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)]
For POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0), (0.25 0.25, 0.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))
we got:
[(0.0, 1.0), (0.0, 0.0), (0.25, 0.25), (0.75, 0.25), (0.75, 0.75), (0.25, 0.75), (1.0, 0.0), (1.0, 1.0)]
For MULTIPOINT (0 0, 1 1)
we got:
[(0.0, 0.0), (1.0, 1.0)]
For MULTILINESTRING ((0 0, 1 1), (2 0, 3 1))
we got:
[(2.0, 0.0), (0.0, 0.0), (3.0, 1.0), (1.0, 1.0)]
For MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)), ((2 0, 3 0, 3 1, 2 1, 2 0), (2.25 0.25, 2.75 0.25, 2.75 0.75, 2.25 0.75, 2.25 0.25)))
we got:
[(0.0, 1.0), (0.0, 0.0), (3.0, 0.0), (3.0, 1.0), (2.0, 1.0), (2.0, 0.0), (2.25, 0.25), (2.75, 0.25), (2.75, 0.75), (2.25, 0.75), (1.0, 0.0), (1.0, 1.0)]
For GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (0 0, 1 1))
we got:
[(0.0, 0.0), (1.0, 1.0)]