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