不幸的是,Shapely没有提供立即从
MultiPolygon对象中提取所有点的功能。取而代之的是,您必须首先对a的各个多边形进行迭代
MultiPolygon,然后再提取每个的各个点
Polygon。
一个人可以想出不同的方法来解决这个问题。例如,如果您知道所有多边形都没有孔,则只需执行以下操作:
points = []for polygon in multipolygon: points.extend(polygon.exterior.coords[:-1])
请注意,
[:-1]这会防止复制第一个顶点。如果您希望使用一种更简洁的语法并且不关心每个多边形有一个重复点,则可以将其删除。
也可以使用列表理解和两个循环在一行中编写:
points = [point for polygon in multipolygon for point in polygon.exterior.coords[:-1]]
或借助
itertools.chain.from_iterable:
from itertools import chainpoints = list(chain.from_iterable(polygon.exterior.coords[:-1] for polygon in multipolygon))
通常,当多边形可以包含孔时,例如,我们可以编写以下函数从内部环中提取坐标:
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)
用法示例:
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)]
甚至可以对任何输入几何(Python≥3.7)将其概括化:
from functools import singledispatchfrom itertools import chainfrom 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)@singledispatchdef 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.registerdef _(geometry: Point): return [(geometry.x, geometry.y)]@to_coords.registerdef _(geometry: LineString): return list(geometry.coords)@to_coords.registerdef _(geometry: LinearRing): return list(geometry.coords[:-1])@to_coords.registerdef _(geometry: baseMultipartGeometry): return list(set(chain.from_iterable(map(to_coords, geometry))))@to_coords.registerdef _(geometry: Polygon): return to_coords(GeometryCollection([geometry.exterior, *geometry.interiors]))用法示例:
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")输出:
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)]



