executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
shapelyUse when performing computational geometry in Python — intersection, union, buffer, convex hull, simplification. Shapely: Python bindings for GEOS, the C++ geometry engine that powers PostGIS.
Independent GitHub snapshot · Automated classification · Original documentation governs
name: shapely description: "Use when performing computational geometry in Python — intersection, union, buffer, convex hull, simplification. Shapely: Python bindings for GEOS, the C++ geometry engine that powers PostGIS." tags:
项目地址: https://github.com/shapely/shapely
官方文档: https://shapely.readthedocs.io/
PyPI: https://pypi.org/project/shapely/
最新稳定版: 2.1.2(PyPI,截至2026年)
许可证: BSD-3-Clause
Shapely 是 Python 计算几何的核心库,基于 GEOS(Geometry Engine - Open Source)的 C++ 几何引擎。它为 PostGIS、GeoPandas、PyQGIS 等众多开源 GIS 项目提供几何计算底层支持。核心能力:
环境要求: Python 3.9+,Shapely 2.0+
| Type | Class | Description |
|---|---|---|
| Point | shapely.Point | Single coordinate (x, y [, z [, m]]) |
| LineString | shapely.LineString | Ordered sequence of 2+ points |
| LinearRing | shapely.LinearRing | Closed, simple LineString |
| Polygon | shapely.Polygon | Exterior ring + optional holes |
| MultiPoint | shapely.MultiPoint | Collection of Points |
| MultiLineString | shapely.MultiLineString | Collection of LineStrings |
| MultiPolygon | shapely.MultiPolygon | Collection of Polygons |
| GeometryCollection | shapely.GeometryCollection | Heterogeneous collection |
Z coordinates are ignored for all spatial analysis — operations are performed in the x-y plane only.
Shapely provides two APIs. Prefer the function-based API for arrays and performance; use the OOP API for scalar convenience.
All functions release the GIL during GEOS execution, support NumPy broadcasting, and handle arrays natively.
import shapely
import numpy as np
geoms = np.array([shapely.Point(0, 0), shapely.Point(1, 1)])
shapely.area(geoms) # array of floats
shapely.contains(polygon, geoms) # array of bools
shapely.buffer(geoms, 1.0) # array of polygons
shapely.distance(geoms, other) # array of distances
from shapely import Point, Polygon
p = Point(0, 0)
poly = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
p.buffer(10)
p.distance(Point(1, 1))
poly.area # property
poly.contains(p)
poly.intersection(other)
geom1 & geom2 # intersection
geom1 | geom2 # union
geom1 - geom2 # difference
geom1 ^ geom2 # symmetric_difference
from shapely import Point, LineString, LinearRing, Polygon
from shapely import MultiPoint, MultiLineString, MultiPolygon, GeometryCollection
Point(0, 0)
Point(0, 0, 5) # 3D
LineString([(0, 0), (1, 1), (2, 0)])
LinearRing([(0, 0), (1, 0), (1, 1), (0, 0)]) # must be closed
Polygon([(0, 0), (1, 0), (1, 1), (0, 0)]) # auto-closes shell
Polygon(shell_coords, [hole1_coords, hole2_coords])
MultiPoint([(0, 0), (1, 1)])
MultiLineString([[(0, 0), (1, 1)], [(2, 2), (3, 3)]])
MultiPolygon([poly1, poly2])
GeometryCollection([point, line, poly])
import shapely
shapely.points([[0, 1], [2, 3]]) # array of Points
shapely.points(0, 1) # scalar Point
shapely.linestrings([[[0, 0], [1, 1]], [[2, 2], [3, 3]]])
shapely.linearrings([[0, 0], [1, 0], [1, 1]]) # auto-closes
shapely.polygons(shell, holes=[hole1, hole2])
shapely.box(xmin, ymin, xmax, ymax) # rectangle(s)
shapely.multipoints([pt1, pt2])
shapely.multilinestrings(...)
shapely.multipolygons(...)
shapely.geometrycollections(...)
# WKT
shapely.from_wkt("POINT (0 0)")
shapely.to_wkt(geom, rounding_precision=6, trim=True)
# WKB
shapely.from_wkb(b'\x01\x01...')
shapely.to_wkb(geom, hex=False, flavor="extended")
# GeoJSON
shapely.from_geojson('{"type":"Point","coordinates":[0,0]}')
shapely.to_geojson(geom, indent=None)
# GeoArrow ragged arrays
shapely.from_ragged_array(geometry_type, coords, offsets)
shapely.to_ragged_array(geometries)
__geo_interface__)from shapely.geometry import shape, mapping
geom = shape({"type": "Point", "coordinates": [0, 0]})
d = mapping(geom) # {"type": "Point", "coordinates": (0.0, 0.0)}
构造操作、空间谓词、集合运算、测量、空间索引、仿射与坐标变换及线性参考的完整 API 见 reference/operations-reference.md
shapely.is_valid(geom)
shapely.is_valid_reason(geom)
shapely.make_valid(geom, method="linework") # or "structure"
Caches spatial index for repeated predicate tests against the same geometry.
shapely.prepare(geom) # in-place, returns True if newly prepared
shapely.destroy_prepared(geom) # free cache
shapely.is_prepared(geom)
None represents missing geometries. Consistent behavior:
FalsenanNoneUse shapely.is_missing(obj) to check, distinct from shapely.is_empty(geom).
Z ignored in analysis: All spatial operations work in x-y plane only. Geometries with different z values may still intersect or be equal.
contains excludes boundary: contains(line, endpoint) is False. Use covers() or intersects() if you need boundary inclusion.
set_coordinates modifies in-place: Copy geometry first with .copy() if originals must be preserved.
clip_by_rect may produce invalid output: The fast clip is not guaranteed to yield valid topology.
WKB drops LinearRing: LinearRings become LineStrings during WKB serialization.
to_geojson drops Z: Third dimension is silently discarded; LinearRing outputs as null.
OOP vs function buffer defaults differ: Point(0,0).buffer(1) uses quad_segs=16; shapely.buffer(Point(0,0), 1) uses quad_segs=8.
set_precision changes vertex order: Returned geometry is in "mild canonical form" — vertex order should not be relied upon.
NaN coordinates: Creating geometries with NaN/Inf is allowed by default. Use handle_nan='error' or handle_nan='skip' in creation functions to control this.
Prepared state not preserved: After any operation producing a new geometry, prepare() must be called again.
GEOS version gates: concave_hull, constrained_delaunay_triangles, remove_repeated_points require GEOS >= 3.11. coverage_simplify, has_m, get_m require GEOS >= 3.12.
from shapely import STRtree, points
pts = points(np.random.rand(10000, 2))
tree = STRtree(pts)
idx = tree.query(polygon, predicate="intersects")
inside_points = pts[idx]
result = shapely.union_all(polygon_array)
from pyproj import Transformer
transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
def project(coords):
x, y = transformer.transform(coords[:, 0], coords[:, 1])
return np.column_stack([x, y])
projected = shapely.transform(geom, project)
coords = shapely.get_coordinates(geom_array, include_z=False)
# coords shape: (N, 2)
tree = STRtree(right_geoms)
left_idx, right_idx = tree.query(left_geoms, predicate="intersects")
# left_idx[i] and right_idx[i] are paired matches
shapely.points() / shapely.linestrings() / shapely.polygons() 向量化创建,或 Point() / LineString() / Polygon() OOP 方式shapely.intersection() / shapely.buffer())批量处理,比逐个 OOP 调用快 10-100 倍shapely.contains() / shapely.intersects() 等函数式 API,返回 bool 数组STRtree 查询 + predicate="intersects" 加速批量空间关系判断shapely.from_wkt() / shapely.to_geojson() 等进行格式互转contains 不包含边界:如需边界判断使用 covers() 或 intersects()shapely.area(geoms))释放 GIL,支持 NumPy 广播,性能远优于 OOP(geom.area)set_precision 改变顶点顺序:返回的几何为"温和规范形式",不应依赖顶点顺序prepare()| 问题 | 解决 |
|---|---|
| 面积/距离为 0 或极小 | CRS 为地理坐标系(度),需要投影到投影坐标系再计算 |
contains 对边界上的点返回 False | 使用 covers() 或 intersects() 代替 |
| 几何操作产生 TopologyException | 几何无效,用 make_valid() 修复后再操作 |
| OOP 和函数式 API 结果不一致 | buffer 默认 quad_segs 不同(OOP=16,函数式=8),显式指定即可 |
None 参与运算报错 | 使用 shapely.is_missing() 先检查,或过滤掉 None 再运算 |
| STRtree 查询返回空数组 | 检查几何是否 None/Empty;确认查询几何与索引几何的 CRS 一致 |
| WKB 序列化丢失 LinearRing | WKB 规范不支持 LinearRing,会自动转为 LineString |
to_geojson 丢失 Z 坐标 | GeoJSON 标准不支持 Z,改用 WKB 或自定义序列化 |
shapely/
├── __init__.py # Top-level re-exports
├── geometry/ # OOP geometry classes
│ ├── base.py # BaseGeometry (all methods/properties)
│ ├── point.py, linestring.py, polygon.py
│ ├── multipoint.py, multilinestring.py, multipolygon.py
│ ├── collection.py # GeometryCollection
│ └── geo.py # shape(), mapping(), box()
├── constructive.py # buffer, simplify, hull, etc.
├── predicates.py # contains, intersects, etc.
├── set_operations.py # union, intersection, difference, etc.
├── measurement.py # area, length, distance, bounds
├── creation.py # points(), linestrings(), polygons(), box()
├── coordinates.py # transform, get/set_coordinates
├── io.py # from/to WKT, WKB, GeoJSON
├── strtree.py # STRtree spatial index
├── linear.py # line_interpolate_point, line_merge, etc.
├── affinity.py # rotate, scale, skew, translate
├── validation.py # make_valid
├── prepared.py # PreparedGeometry
├── ops.py # Legacy: split, nearest_points, etc.
├── algorithms/ # polylabel, oriented_envelope fallback
└── plotting.py # Matplotlib helpers (experimental)