| 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 | ["python","geometry","geos","wkt","wkb","geojson","spatial","numpy"] |
项目地址: https://github.com/shapely/shapely
官方文档: https://shapely.readthedocs.io/
PyPI: https://pypi.org/project/shapely/
许可证: BSD-3-Clause
概述
Shapely 是 Python 计算几何的核心库,基于 GEOS(Geometry Engine - Open Source)的 C++ 几何引擎。它为 PostGIS、GeoPandas、PyQGIS 等众多开源 GIS 项目提供几何计算底层支持。核心能力:
- 几何对象模型:Point、LineString、Polygon、Multi*、GeometryCollection
- 空间关系判断:contains、intersects、within、touches、covers、disjoint、relate(DE-9IM)
- 集合运算:intersection、union、difference、symmetric_difference
- 构造操作:buffer、simplify、convex_hull、delaunay_triangles、voronoi_polygons、make_valid
- 几何度量:area、length、distance、hausdorff_distance、frechet_distance
- 空间索引:STRtree,加速批量空间查询
- 格式读写:WKT、WKB、GeoJSON
- 双重 API:函数式(向量化,支持 NumPy 广播)+ OOP(标量便捷)
环境要求: Python 3.9+,Shapely 2.0+
Geometry Types
| 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.
Dual API Pattern
Shapely provides two APIs. Prefer the function-based API for arrays and performance; use the OOP API for scalar convenience.
Function-based (vectorized, NumPy ufunc)
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)
shapely.contains(polygon, geoms)
shapely.buffer(geoms, 1.0)
shapely.distance(geoms, other)
OOP (method-based, scalar)
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
poly.contains(p)
poly.intersection(other)
Operator overloading
geom1 & geom2
geom1 | geom2
geom1 - geom2
geom1 ^ geom2
Geometry Creation
From coordinates (OOP)
from shapely import Point, LineString, LinearRing, Polygon
from shapely import MultiPoint, MultiLineString, MultiPolygon, GeometryCollection
Point(0, 0)
Point(0, 0, 5)
LineString([(0, 0), (1, 1), (2, 0)])
LinearRing([(0, 0), (1, 0), (1, 1), (0, 0)])
Polygon([(0, 0), (1, 0), (1, 1), (0, 0)])
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])
From coordinates (vectorized)
import shapely
shapely.points([[0, 1], [2, 3]])
shapely.points(0, 1)
shapely.linestrings([[[0, 0], [1, 1]], [[2, 2], [3, 3]]])
shapely.linearrings([[0, 0], [1, 0], [1, 1]])
shapely.polygons(shell, holes=[hole1, hole2])
shapely.box(xmin, ymin, xmax, ymax)
shapely.multipoints([pt1, pt2])
shapely.multilinestrings(...)
shapely.multipolygons(...)
shapely.geometrycollections(...)
From serialization formats
shapely.from_wkt("POINT (0 0)")
shapely.to_wkt(geom, rounding_precision=6, trim=True)
shapely.from_wkb(b'\x01\x01...')
shapely.to_wkb(geom, hex=False, flavor="extended")
shapely.from_geojson('{"type":"Point","coordinates":[0,0]}')
shapely.to_geojson(geom, indent=None)
shapely.from_ragged_array(geometry_type, coords, offsets)
shapely.to_ragged_array(geometries)
From/to GeoJSON-like dict (__geo_interface__)
from shapely.geometry import shape, mapping
geom = shape({"type": "Point", "coordinates": [0, 0]})
d = mapping(geom)
Constructive Operations
All available as shapely.<function>(geometry, ...).
| Function | Description |
|---|
buffer(geom, distance, quad_segs=8, cap_style="round", join_style="round", mitre_limit=5.0, single_sided=False) | Minkowski sum/difference with circle |
offset_curve(geom, distance, ...) | Parallel curve at given distance |
centroid(geom) | Geometric center |
point_on_surface(geom) | Point guaranteed inside geometry |
boundary(geom) | Topological boundary |
convex_hull(geom) | Minimum convex enclosure |
concave_hull(geom, ratio) | Concave enclosure (GEOS >= 3.11) |
envelope(geom) | Axis-aligned bounding box |
oriented_envelope(geom) | Minimum-area rotated rectangle |
minimum_bounding_circle(geom) | Smallest enclosing circle |
simplify(geom, tolerance, preserve_topology=True) | Douglas-Peucker simplification |
snap(geom, reference, tolerance) | Snap vertices to reference |
segmentize(geom, max_segment_length) | Densify by adding vertices |
reverse(geom) | Reverse coordinate order |
normalize(geom) | Canonical coordinate ordering |
make_valid(geom, method="linework") | Repair invalid geometry |
build_area(geom) | Polygonize from linework |
clip_by_rect(geom, xmin, ymin, xmax, ymax) | Fast rectangle clip (may produce invalid output) |
delaunay_triangles(geom, tolerance=0.0, only_edges=False) | Delaunay triangulation |
constrained_delaunay_triangles(geom) | Constrained Delaunay (GEOS >= 3.11) |
voronoi_polygons(geom, tolerance=0.0, extend_to=None) |
Spatial Predicates
All return bool (or bool array). Available as shapely.<function>(a, b) or a.<method>(b).
| Function | Description |
|---|
contains(a, b) | B entirely inside A (boundary excluded) |
contains_properly(a, b) | B inside A with no shared boundary |
contains_xy(geom, x, y) | Fast point-in-geometry test |
covered_by(a, b) | No point of A outside B |
covers(a, b) | No point of B outside A |
crosses(a, b) | A and B spatially cross |
disjoint(a, b) | No shared space |
dwithin(a, b, distance) | Within given distance |
equals(a, b) | Topologically equal |
equals_exact(a, b, tolerance) | Structurally equal within tolerance |
intersects(a, b) | Share any space |
intersects_xy(geom, x, y) | Fast point-intersects test |
overlaps(a, b) | Partial overlap, same dimension |
touches(a, b) | Only boundaries touch |
within(a, b) | A entirely inside B |
relate(a, b) | DE-9IM intersection matrix string |
relate_pattern(a, b, pattern) | Test DE-9IM pattern match |
is_valid(geom) | Well-formed geometry |
is_valid_reason(geom) | Description if invalid |
is_empty(geom) | Empty geometry test |
is_simple(geom) | No self-intersections |
is_ring(geom) | Closed and simple |
is_closed(geom) | First == last point |
is_ccw(geom) |
Set-Theoretic Operations
| Function | Description |
|---|
intersection(a, b, grid_size=None) | Shared geometry |
intersection_all(geometries) | N-way intersection |
union(a, b, grid_size=None) | Merge two geometries |
union_all(geometries) | N-way union |
difference(a, b, grid_size=None) | A minus B |
symmetric_difference(a, b, grid_size=None) | XOR of A and B |
coverage_union(a, b) | Optimized union for non-overlapping polygons |
coverage_union_all(geometries) | N-way coverage union |
All binary operations support optional grid_size for precision snapping.
Measurements
| Function | Description |
|---|
area(geom) | Area of polygon(s) |
length(geom) | Length of line / polygon perimeter |
distance(a, b) | Minimum Cartesian distance |
hausdorff_distance(a, b, densify=None) | Hausdorff distance |
frechet_distance(a, b, densify=None) | Frechet distance |
bounds(geom) | (minx, miny, maxx, maxy) per geometry |
total_bounds(geometries) | Combined bounds of all geometries |
minimum_clearance(geom) | Smallest move to invalidate |
minimum_bounding_radius(geom) | Radius of minimum bounding circle |
STRtree Spatial Index
from shapely import STRtree
tree = STRtree(geometries, node_capacity=10)
idx = tree.query(geometry)
idx = tree.query(geom_array)
idx = tree.query(geom, predicate="intersects")
idx = tree.query(geom, predicate="dwithin", distance=5.0)
idx = tree.nearest(geometry)
result = tree.query_nearest(
geometry,
max_distance=10,
return_distance=True,
exclusive=False,
all_matches=True
)
Geometry Accessors
shapely.get_type_id(geom)
shapely.get_dimensions(geom)
shapely.get_coordinate_dimension(geom)
shapely.get_x(point)
shapely.get_y(point)
shapely.get_z(point)
shapely.get_num_points(linestring)
shapely.get_point(linestring, index)
shapely.get_exterior_ring(polygon)
shapely.get_interior_ring(polygon, index)
shapely.get_num_interior_rings(polygon)
shapely.get_geometry(collection, index)
shapely.get_num_geometries(collection)
shapely.get_parts(geom, return_index=False)
shapely.get_rings(polygon, return_index=False)
shapely.get_coordinates(geom, include_z=False, return_index=False)
shapely.set_coordinates(geom, new_coords)
shapely.count_coordinates(geom)
shapely.get_srid(geom) / shapely.set_srid(geom, srid)
shapely.get_precision(geom) / shapely.set_precision(geom, grid_size)
shapely.force_2d(geom) / shapely.force_3d(geom, z=0)
Affine Transforms
from shapely.affinity import affine_transform, rotate, scale, skew, translate
affine_transform(geom, [a, b, d, e, xoff, yoff])
affine_transform(geom, matrix_12)
rotate(geom, angle, origin="center")
scale(geom, xfact=1.0, yfact=1.0, zfact=1.0, origin="center")
skew(geom, xs=0, ys=0, origin="center")
translate(geom, xoff=0.0, yoff=0.0, zoff=0.0)
Coordinate Transforms
shapely.transform(geom, lambda coords: coords * 2)
shapely.transform(geom, func, include_z=True)
shapely.transform_coordseq(geom, func)
Linear Referencing
shapely.line_interpolate_point(line, distance, normalized=False)
shapely.line_locate_point(line, point, normalized=False)
shapely.line_merge(multilinestring, directed=False)
shapely.shared_paths(a, b)
shapely.shortest_line(a, b)
Validation
shapely.is_valid(geom)
shapely.is_valid_reason(geom)
shapely.make_valid(geom, method="linework")
Prepared Geometry
Caches spatial index for repeated predicate tests against the same geometry.
shapely.prepare(geom)
shapely.destroy_prepared(geom)
shapely.is_prepared(geom)
Missing Values
None represents missing geometries. Consistent behavior:
- Predicates return
False
- Measurements return
nan
- Constructive operations return
None
Use shapely.is_missing(obj) to check, distinct from shapely.is_empty(geom).
Important Caveats
-
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.
Common Patterns
Point-in-polygon test for many points
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]
Batch union of many polygons
result = shapely.union_all(polygon_array)
Coordinate projection (e.g., with pyproj)
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)
Extract all coordinates from mixed geometries
coords = shapely.get_coordinates(geom_array, include_z=False)
Spatial join pattern
tree = STRtree(right_geoms)
left_idx, right_idx = tree.query(left_geoms, predicate="intersects")
AI 使用建议
推荐工作流
- 创建几何:使用
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() 等进行格式互转
关键注意事项
- Z 坐标在分析中被忽略:所有空间运算仅在 x-y 平面进行
contains 不包含边界:如需边界判断使用 covers() 或 intersects()
- 向量化函数 vs OOP 方法:函数式 API(
shapely.area(geoms))释放 GIL,支持 NumPy 广播,性能远优于 OOP(geom.area)
set_precision 改变顶点顺序:返回的几何为"温和规范形式",不应依赖顶点顺序
- Prepared 状态不持久:任何操作产生新几何后需重新
prepare()
- WKB 会丢弃 LinearRing:WKB 序列化时 LinearRing 变为 LineString
常见问题
| 问题 | 解决 |
|---|
| 面积/距离为 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 或自定义序列化 |
相关技能
Package Structure
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)
参考资源