Shapely
lastmod 2025-06-12created 2020-11-2319 edits
Geometric objects, predicates, and operations
Categories
- COCOAPI#UnionPolygons - Python code that merges the
segmentationregions of the entireannotationslist.
Geometry
Geometry types
Shapely_-_SpatialDataModel.png
-
Point(*args)- A geometry type that represents a single coordinate with x,y and possibly z values. -
LineString([coordinates])- A geometry type composed of one or more line segments. -
LinearRing([coordinates])- A geometry type composed of one or more line segments that forms a closed loop. -
Polygon([shell, holes])- A geometry type representing an area that is enclosed by a linear ring. -
MultiPoint([points])- A collection of one or more Points. -
MultiLineString([lines])- A collection of one or more LineStrings. -
MultiPolygon([polygons])- A collection of one or more Polygons. -
GeometryCollection([geoms])- A collection of one or more geometries that may contain more than one type of geometry.
APIs
- Area:
polygon.area - Length:
polygon.length - Bounding box:
polygon.bounds - Polygon coords to list:
list(polygon.exterior.coords)
difference
Shapely_-_difference.png
intersection vs symmetric_difference
Shapely_-_intersection-sym-difference.png
union
Shapely_-_union.png
When intersection returns a GeometryCollection
The result is not identical, but using convex_hull lets you get a Polygon.
from shapely.geometry import Polygon
def intersection_polygons(*polygons: Polygon) -> Polygon:
pivot = polygons[0]
for polygon in polygons[1:]:
pivot = pivot.intersection(polygon)
if pivot.geom_type == "GeometryCollection":
return pivot.convex_hull
elif pivot.geom_type == "MultiPolygon":
return pivot.convex_hull
elif pivot.geom_type == "Polygon":
return pivot
else:
raise NotImplementedError(f"Unsupported geom type: {pivot.geom_type}")
See Convex hull for details on convex_hull.
Access raw datas
Examples
Merge Polygons
- Stackoverflow - Make a union of polygons in GeoPandas, or Shapely (into a single geometry)
- How to Merge Polygons in Python
from shapely.geometry import Polygon
from shapely.ops import cascaded_union
polygon1 = Polygon([(0, 0), (5, 3), (5, 0)])
polygon2 = Polygon([(0, 0), (3, 10), (3, 0)])
polygons = [polygon1, polygon2]
u = cascaded_union(polygons)
Finding the intersection points of a shape and a line
Shapely_-_circle_and_line_segments.png
from shaply.geometry import Point, LineString
line = LineString([(-1., -1.),(1., 1.)])
# (-1,-1)과 (1,1)을 잇는 직선을 뜻하는 line이라는 객체를 생성할 수 있습니다.
circle = Point(0., 0.).buffer(1.).boundary
# Point 함수로 원의 중점을 지정해주고 buffer라는 operation을 이용하여 반지름을 지정해주면 됩니다.
# 저는 속이 꽉 찬원이 아닌, 원의 경계선을 만들어 주고 싶었기 때문에 .boundary 옵션을 붙여주었습니다.
segment = line.difference(circle)[1]
# 위와 같이 .difference 함수를 사용하면 차집합의 원소가 구해지고, segment들이 직선이니까 양 끝점의 좌표를 통해 표시됩니다.
# 인덱스를 통해 각 원소에 접근 가능한데, 0번 인덱스는 위 사진에서 segment1, 1번 인덱스는 segment2, 2번 인덱스는 segment3을 뜻합니다.
print(segment.coords[0], segment.coords[1])
# .coords 옵션을 통해 1번 인덱스에 해당하는 좌표를 뽑아보면
# `(-0.7071067811865475, -0.7071067811865475) (0.7071067811865474, 0.7071067811865474)`
# 결과는 우리가 설정해준대로 unit circle과 y=x 직선의 교점이 출력됨을 알 수 있습니다.
Geometry coordinates
How to access the coordinates of the 5 most commonly used geometry types:
| Geometry type | Access method |
| Point coordinates | |
| LineString coordinates | |
| MultiLineString coordinates | |
| Polygon coordinates | |
| MultiPolygon coordinates | |
Migrating from PyGEOS
Raw array
How to convert to plain Python data structures
# -*- coding: utf-8 -*-
from itertools import chain
from typing import List, Optional
from shapely import (
GeometryCollection,
LinearRing,
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
)
from shapely.geometry.base import BaseGeometry
NumberT = TypeVar("NumberT", int, float)
PointT = Tuple[NumberT, NumberT]
def raw_points(base: Optional[BaseGeometry]) -> List[PointT]:
if base is None:
return list()
elif base.is_empty:
return list()
elif base.geom_type == "GeometryCollection":
assert isinstance(base, GeometryCollection)
return list(chain(*[raw_points(geom) for geom in base.geoms]))
elif base.geom_type == "MultiPolygon":
assert isinstance(base, MultiPolygon)
raise NotImplementedError
elif base.geom_type == "Polygon":
assert isinstance(base, Polygon)
raise NotImplementedError
elif base.geom_type == "MultiLineString":
assert isinstance(base, MultiLineString)
return list(chain(*[raw_points(geom) for geom in base.geoms]))
elif base.geom_type == "LinearRing":
assert isinstance(base, LinearRing)
raise NotImplementedError
elif base.geom_type == "LineString":
assert isinstance(base, LineString)
return [(c[0], c[1]) for c in base.coords]
elif base.geom_type == "MultiPoint":
assert isinstance(base, MultiPoint)
return list(chain(*[raw_points(geom) for geom in base.geoms]))
elif base.geom_type == "Point":
assert isinstance(base, Point)
return [(base.x, base.y)]
else:
raise TypeError(f"Unsupported geom type: {base.geom_type}")
def filter_polygons(base: Optional[BaseGeometry]) -> List[Polygon]:
if base is None:
return list()
elif base.is_empty:
return list()
elif base.geom_type == "GeometryCollection":
assert isinstance(base, GeometryCollection)
return list(chain(*[filter_polygons(geom) for geom in base.geoms]))
elif base.geom_type == "MultiPolygon":
assert isinstance(base, MultiPolygon)
return list(chain(*[filter_polygons(geom) for geom in base.geoms]))
elif base.geom_type == "Polygon":
assert isinstance(base, Polygon)
return [base]
elif base.geom_type == "MultiLineString":
assert isinstance(base, MultiLineString)
return list()
elif base.geom_type == "LinearRing":
assert isinstance(base, LinearRing)
return list()
elif base.geom_type == "LineString":
assert isinstance(base, LineString)
return list()
elif base.geom_type == "MultiPoint":
assert isinstance(base, MultiPoint)
return list()
elif base.geom_type == "Point":
assert isinstance(base, Point)
return list()
else:
raise TypeError(f"Unsupported geom type: {base.geom_type}")
OpenCV Contour
How to convert to and from OpenCV's Contour
from typing import List, Tuple, TypeVar
from numpy import array, int32
from numpy.typing import NDArray
from shapely import LineString, MultiPolygon, Polygon
from shapely.geometry.base import BaseGeometry
NumberT = TypeVar("NumberT", int, float)
PointT = Tuple[NumberT, NumberT]
LineT = Tuple[PointT, PointT]
RectT = Tuple[NumberT, NumberT, NumberT, NumberT]
def cvt_contour2polygon(contour: NDArray) -> Polygon:
assert len(contour.shape) == 3
assert contour.shape[0] >= 4
assert contour.shape[1] == 1
assert contour.shape[2] == 2
return Polygon(contour[:, 0, :].tolist())
def cvt_contour2linestring(contour: NDArray) -> LineString:
assert len(contour.shape) == 3
assert contour.shape[0] >= 4
assert contour.shape[1] == 1
assert contour.shape[2] == 2
return LineString(contour[:, 0, :].tolist())
def cvt_line2linestring(line: LineT) -> LineString:
return LineString([*line])
def cvt_polygon2contour(polygon: Polygon, dtype=int32) -> NDArray:
if polygon.is_empty:
raise ValueError("Polygon must not be empty")
coords = polygon.exterior.coords
shape = len(coords), 1, 2
return array(coords, dtype=dtype).reshape(shape)
def cvt_multipolygon2contours(multipolygon: MultiPolygon, dtype=int32) -> List[NDArray]:
if multipolygon.is_empty:
raise ValueError("Polygon must not be empty")
return [cvt_polygon2contour(polygon, dtype) for polygon in multipolygon.geoms]
def cvt_geometry2contours(geom: BaseGeometry, dtype=int32) -> List[NDArray]:
if geom.is_empty:
raise ValueError("Geometry must not be empty")
if geom.geom_type == "Polygon":
assert isinstance(geom, Polygon)
return [cvt_polygon2contour(geom, dtype)]
elif geom.geom_type == "MultiPolygon":
assert isinstance(geom, MultiPolygon)
return cvt_multipolygon2contours(geom, dtype)
else:
raise TypeError(f"Unsupported geom type: {geom.geom_type}")
def cvt_roi2contour(roi: RectT, dtype=int32) -> NDArray:
x1, y1, x2, y2 = roi
shell = (x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1)
raw = [p for xy in shell for p in xy]
contour = array(raw, dtype=dtype).reshape((5, 1, 2))
assert len(contour.shape) == 3
assert contour.shape[0] == len(shell)
assert contour.shape[1] == 1
assert contour.shape[2] == 2
return contour
def cvt_roi2polygon(roi: RectT) -> Polygon:
x1, y1, x2, y2 = roi
return Polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)])
AreaType = Union[NDArray, RectT]
def cvt_area2polygon(area: AreaType) -> Polygon:
if isinstance(area, ndarray):
return cvt_contour2polygon(area)
elif isinstance(area, (tuple, list)):
if len(area) == 4:
return cvt_roi2polygon(area)
raise TypeError(f"Unsupported area type: {type(area).__name__}")
See also
Favorite site
- Shapely · PyPI
- [Recommended] The Shapely User Manual — Shapely 2.0.1 documentation
- Vector data analysis-2. Shapely & Fiona :: Programmer Psy