znlgis 博客

GIS开发与技术分享

第4章:几何对象的属性与方法

掌握几何对象的属性与方法是有效使用 Shapely 的关键。本章将全面介绍所有几何对象的通用属性、各类型特有属性、坐标访问模式、GeoJSON 接口、字符串表示、比较操作和运算符重载,每个属性和方法都配有详细的代码示例和输出说明。


4.1 通用属性

4.1.1 边界框 .bounds

.bounds 返回几何体的最小外接矩形,格式为 (minx, miny, maxx, maxy)

from shapely import Point, LineString, Polygon

# 点的边界框
p = Point(3, 5)
print(f"Point bounds: {p.bounds}")
# (3.0, 5.0, 3.0, 5.0)

# 线的边界框
line = LineString([(1, 2), (5, 8), (3, 1)])
print(f"Line bounds: {line.bounds}")
# (1.0, 1.0, 5.0, 8.0)

# 多边形的边界框
poly = Polygon([(0, 0), (10, 0), (10, 5), (5, 8), (0, 5)])
print(f"Polygon bounds: {poly.bounds}")
# (0.0, 0.0, 10.0, 8.0)

# 解包边界框
minx, miny, maxx, maxy = poly.bounds
print(f"宽度: {maxx - minx}, 高度: {maxy - miny}")
# 宽度: 10.0, 高度: 8.0

4.1.2 面积 .area

from shapely import Point, LineString, Polygon

# 点和线的面积为 0
print(f"Point area: {Point(1, 2).area}")              # 0.0
print(f"LineString area: {LineString([(0,0),(1,1)]).area}")  # 0.0

# 多边形的面积
rect = Polygon([(0, 0), (5, 0), (5, 3), (0, 3)])
print(f"矩形面积: {rect.area}")   # 15.0

triangle = Polygon([(0, 0), (6, 0), (3, 4)])
print(f"三角形面积: {triangle.area}")  # 12.0

# 带孔洞的多边形
poly_hole = Polygon(
    [(0, 0), (10, 0), (10, 10), (0, 10)],   # 外环
    [[(2, 2), (2, 4), (4, 4), (4, 2)]]       # 孔洞
)
print(f"带孔面积: {poly_hole.area}")  # 100 - 4 = 96.0

4.1.3 长度 .length

from shapely import Point, LineString, Polygon

# 点的长度为 0
print(f"Point length: {Point(1, 2).length}")  # 0.0

# 线的长度
line = LineString([(0, 0), (3, 0), (3, 4)])
print(f"Line length: {line.length}")  # 7.0(3 + 4)

# 多边形的长度 = 周长
rect = Polygon([(0, 0), (5, 0), (5, 3), (0, 3)])
print(f"矩形周长: {rect.length}")  # 16.0(5+3+5+3)

# 带孔洞的多边形的周长 = 外环周长 + 内环周长
poly_hole = Polygon(
    [(0, 0), (10, 0), (10, 10), (0, 10)],
    [[(2, 2), (2, 4), (4, 4), (4, 2)]]
)
print(f"外环+内环周长: {poly_hole.length}")  # 40 + 8 = 48.0

4.1.4 几何类型 .geom_type

from shapely import Point, LineString, Polygon, MultiPoint
from shapely import MultiLineString, MultiPolygon, GeometryCollection

geoms = [
    Point(0, 0),
    LineString([(0, 0), (1, 1)]),
    Polygon([(0, 0), (1, 0), (1, 1)]),
    MultiPoint([(0, 0), (1, 1)]),
    MultiLineString([[(0, 0), (1, 1)]]),
    MultiPolygon([Polygon([(0, 0), (1, 0), (1, 1)])]),
    GeometryCollection([Point(0, 0)])
]

for g in geoms:
    print(f"{g.geom_type:25s}{g.wkt[:50]}")

4.1.5 Z 坐标检测 .has_z

from shapely import Point, LineString

# 二维几何
p2d = Point(1, 2)
print(f"2D has_z: {p2d.has_z}")  # False

# 三维几何
p3d = Point(1, 2, 3)
print(f"3D has_z: {p3d.has_z}")  # True

line3d = LineString([(0, 0, 0), (1, 1, 1)])
print(f"3D Line has_z: {line3d.has_z}")  # True

4.1.6 空几何检测 .is_empty

from shapely import Point, Polygon

print(f"Point() is_empty: {Point().is_empty}")            # True
print(f"Point(1,2) is_empty: {Point(1, 2).is_empty}")     # False
print(f"Polygon() is_empty: {Polygon().is_empty}")         # True

4.1.7 有效性检测 .is_valid

from shapely import Polygon
from shapely.validation import explain_validity

# 有效的多边形
valid_poly = Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])
print(f"有效: {valid_poly.is_valid}")  # True
print(f"原因: {explain_validity(valid_poly)}")  # Valid Geometry

# 自交叉的多边形(蝴蝶结形状)——无效
invalid_poly = Polygon([(0, 0), (4, 4), (4, 0), (0, 4)])
print(f"有效: {invalid_poly.is_valid}")  # False
print(f"原因: {explain_validity(invalid_poly)}")
# Self-intersection[2 2]

4.1.8 环检测 .is_ring.is_closed

from shapely import LineString, LinearRing

# 非闭合线
open_line = LineString([(0, 0), (1, 1), (2, 0)])
print(f"open_line is_closed: {open_line.is_closed}")  # False
print(f"open_line is_ring: {open_line.is_ring}")      # False

# 闭合但自交叉的线
closed_crossing = LineString([(0, 0), (2, 2), (2, 0), (0, 2), (0, 0)])
print(f"closed_crossing is_closed: {closed_crossing.is_closed}")  # True
print(f"closed_crossing is_ring: {closed_crossing.is_ring}")      # False(自交叉)

# 简单闭合线(环)
ring = LinearRing([(0, 0), (2, 0), (2, 2), (0, 2)])
print(f"ring is_closed: {ring.is_closed}")  # True
print(f"ring is_ring: {ring.is_ring}")      # True

4.1.9 简单性检测 .is_simple

from shapely import LineString

# 简单线(不自交叉)
simple_line = LineString([(0, 0), (1, 1), (2, 0)])
print(f"简单: {simple_line.is_simple}")  # True

# 自交叉线(非简单)
crossing_line = LineString([(0, 0), (2, 2), (2, 0), (0, 2)])
print(f"简单: {crossing_line.is_simple}")  # False

4.2 序列化属性

4.2.1 WKT 表示 .wkt

from shapely import Point, Polygon

p = Point(1.123456789, 2.987654321)
print(f"WKT: {p.wkt}")
# POINT (1.123456789 2.987654321)

poly = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
print(f"WKT: {poly.wkt}")
# POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))

4.2.2 WKB 十六进制表示 .wkb_hex

from shapely import Point

p = Point(1, 2)
print(f"WKB Hex: {p.wkb_hex}")
# 0101000000000000000000F03F0000000000000040

# 从 WKB 恢复
from shapely import from_wkb
p_restored = from_wkb(p.wkb_hex)
print(f"恢复: {p_restored}")  # POINT (1 2)

4.2.3 WKB 二进制表示 .wkb

from shapely import Point

p = Point(1, 2)
wkb_bytes = p.wkb
print(f"WKB 类型: {type(wkb_bytes)}")  # <class 'bytes'>
print(f"WKB 长度: {len(wkb_bytes)}")   # 21 bytes

4.3 坐标访问

4.3.1 .coords 坐标序列

from shapely import Point, LineString

# Point 的坐标
p = Point(3, 4)
print(f"coords: {list(p.coords)}")     # [(3.0, 4.0)]
print(f"coords[0]: {p.coords[0]}")     # (3.0, 4.0)

# LineString 的坐标
line = LineString([(0, 0), (1, 1), (2, 0)])
print(f"coords: {list(line.coords)}")
# [(0.0, 0.0), (1.0, 1.0), (2.0, 0.0)]

# 坐标数量
print(f"坐标数: {len(line.coords)}")  # 3

# 通过索引访问
print(f"第一个: {line.coords[0]}")   # (0.0, 0.0)
print(f"最后一个: {line.coords[-1]}")  # (2.0, 0.0)

# 切片
print(f"前两个: {line.coords[:2]}")
# [(0.0, 0.0), (1.0, 1.0)]

4.3.2 获取坐标的不同方式

import numpy as np
from shapely import LineString, Polygon
import shapely

# 使用 shapely.get_coordinates() 获取 NumPy 数组
line = LineString([(0, 0), (1, 1), (2, 0)])
coords = shapely.get_coordinates(line)
print(f"NumPy 坐标:\n{coords}")
# [[0. 0.]
#  [1. 1.]
#  [2. 0.]]
print(f"类型: {type(coords)}")  # <class 'numpy.ndarray'>

# Polygon 的坐标
poly = Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])
poly_coords = shapely.get_coordinates(poly)
print(f"多边形坐标:\n{poly_coords}")
# [[0. 0.]
#  [4. 0.]
#  [4. 3.]
#  [0. 3.]
#  [0. 0.]]

4.4 Point 特有属性

4.4.1 .x, .y, .z

from shapely import Point

# 二维点
p2d = Point(116.4, 39.9)
print(f"x (经度): {p2d.x}")   # 116.4
print(f"y (纬度): {p2d.y}")   # 39.9

# 三维点
p3d = Point(116.4, 39.9, 50.0)
print(f"x: {p3d.x}")  # 116.4
print(f"y: {p3d.y}")  # 39.9
print(f"z: {p3d.z}")  # 50.0

# 注意:二维点访问 .z 会报错
try:
    _ = p2d.z
except AttributeError:
    print("二维点没有 z 坐标")

4.5 LineString 特有属性

4.5.1 坐标序列 .coords

from shapely import LineString

line = LineString([(0, 0), (1, 2), (3, 1), (5, 4)])

# 坐标元组列表
coords_list = list(line.coords)
print(f"坐标列表: {coords_list}")
# [(0.0, 0.0), (1.0, 2.0), (3.0, 1.0), (5.0, 4.0)]

# 坐标点数量
print(f"顶点数: {len(line.coords)}")  # 4

# 起点和终点
print(f"起点: {line.coords[0]}")    # (0.0, 0.0)
print(f"终点: {line.coords[-1]}")   # (5.0, 4.0)

4.6 Polygon 特有属性

4.6.1 外环 .exterior

from shapely import Polygon

poly = Polygon([(0, 0), (10, 0), (10, 5), (0, 5)])

# 外环
ext = poly.exterior
print(f"外环: {ext}")            # LINEARRING (0 0, 10 0, 10 5, 0 5, 0 0)
print(f"外环类型: {ext.geom_type}")  # LinearRing
print(f"外环坐标: {list(ext.coords)}")
# [(0.0, 0.0), (10.0, 0.0), (10.0, 5.0), (0.0, 5.0), (0.0, 0.0)]
print(f"外环长度: {ext.length}")  # 30.0

4.6.2 内环(孔洞) .interiors

from shapely import Polygon

# 带两个孔洞的多边形
exterior = [(0, 0), (20, 0), (20, 20), (0, 20)]
hole1 = [(2, 2), (2, 5), (5, 5), (5, 2)]
hole2 = [(10, 10), (10, 15), (15, 15), (15, 10)]

poly = Polygon(exterior, [hole1, hole2])

# 访问内环
interiors = list(poly.interiors)
print(f"内环数量: {len(interiors)}")  # 2

for i, ring in enumerate(interiors):
    print(f"内环 {i}: {ring}")
    print(f"  坐标: {list(ring.coords)}")
    print(f"  长度: {ring.length}")

4.7 Multi* 类型——.geoms 属性

4.7.1 遍历子几何体

from shapely import MultiPoint, MultiLineString, MultiPolygon
from shapely import Point, LineString, Polygon

# MultiPoint
mp = MultiPoint([(0, 0), (1, 1), (2, 2)])
print(f"MultiPoint 包含 {len(mp.geoms)} 个点")
for geom in mp.geoms:
    print(f"  {geom.geom_type}: ({geom.x}, {geom.y})")

# MultiLineString
mls = MultiLineString([
    [(0, 0), (1, 1)],
    [(2, 2), (3, 3)]
])
print(f"\nMultiLineString 包含 {len(mls.geoms)} 条线")
for geom in mls.geoms:
    print(f"  长度: {geom.length:.4f}")

# MultiPolygon
mpoly = MultiPolygon([
    Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
    Polygon([(3, 0), (5, 0), (5, 2), (3, 2)])
])
print(f"\nMultiPolygon 包含 {len(mpoly.geoms)} 个多边形")
for geom in mpoly.geoms:
    print(f"  面积: {geom.area}")

4.7.2 索引访问

from shapely import MultiPoint

mp = MultiPoint([(0, 0), (1, 1), (2, 2), (3, 3)])

# 通过 .geoms 索引
first = mp.geoms[0]
last = mp.geoms[-1]
print(f"第一个: {first}")  # POINT (0 0)
print(f"最后一个: {last}")  # POINT (3 3)

# 数量
print(f"子几何数量: {len(mp.geoms)}")  # 4

4.8 __geo_interface__ 属性

4.8.1 GeoJSON 兼容接口

__geo_interface__ 是 Python 社区约定的 GeoJSON 兼容接口,返回字典格式:

from shapely import Point, LineString, Polygon
import json

# Point
p = Point(116.4, 39.9)
print(json.dumps(p.__geo_interface__, indent=2))
# {
#   "type": "Point",
#   "coordinates": [116.4, 39.9]
# }

# LineString
line = LineString([(0, 0), (1, 1), (2, 0)])
print(json.dumps(line.__geo_interface__, indent=2))
# {
#   "type": "LineString",
#   "coordinates": [[0.0, 0.0], [1.0, 1.0], [2.0, 0.0]]
# }

# Polygon
poly = Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])
print(json.dumps(poly.__geo_interface__, indent=2))
# {
#   "type": "Polygon",
#   "coordinates": [[[0.0, 0.0], [4.0, 0.0], [4.0, 3.0], [0.0, 3.0], [0.0, 0.0]]]
# }

4.8.2 使用 mapping()shape() 函数

from shapely.geometry import mapping, shape
from shapely import Polygon

# Shapely → GeoJSON dict
poly = Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])
geojson_dict = mapping(poly)
print(geojson_dict)
# {'type': 'Polygon', 'coordinates': (((0.0, 0.0), (4.0, 0.0), ...),)}

# GeoJSON dict → Shapely
geojson = {
    "type": "Point",
    "coordinates": [116.4, 39.9]
}
point = shape(geojson)
print(f"还原: {point}")   # POINT (116.4 39.9)
print(f"类型: {type(point)}")  # <class 'shapely.geometry.point.Point'>

4.9 字符串表示

4.9.1 __repr____str__

from shapely import Point, Polygon

p = Point(1.5, 2.5)

# __str__ 返回 WKT
print(str(p))    # POINT (1.5 2.5)

# __repr__ 也返回 WKT
print(repr(p))   # POINT (1.5 2.5)

# 在 f-string 中直接使用
print(f"点的位置: {p}")  # 点的位置: POINT (1.5 2.5)

4.9.2 SVG 表示

from shapely import Point, Polygon

# Shapely 几何对象在 Jupyter Notebook 中自动渲染 SVG
p = Point(1, 2)
svg = p._repr_svg_()
print(f"SVG 长度: {len(svg)} 字符")

# 多边形的 SVG
poly = Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])
svg_poly = poly._repr_svg_()
print(f"Polygon SVG: {svg_poly[:100]}...")

4.10 比较操作

4.10.1 拓扑相等 ==

Shapely 2.0 中,== 运算符比较的是拓扑相等(equals),即两个几何体是否在几何上等价:

from shapely import Point, Polygon

# 相同坐标的点
p1 = Point(1, 2)
p2 = Point(1, 2)
print(f"p1 == p2: {p1 == p2}")  # True

# 不同坐标的点
p3 = Point(3, 4)
print(f"p1 == p3: {p1 == p3}")  # False

# 坐标顺序不同但拓扑相同的多边形
poly1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
poly2 = Polygon([(1, 0), (1, 1), (0, 1), (0, 0)])  # 不同起始点
print(f"poly1 == poly2: {poly1 == poly2}")  # True(拓扑相等)

4.10.2 equals() 方法

from shapely import Point, Polygon

p1 = Point(1, 2)
p2 = Point(1, 2)
print(f"equals: {p1.equals(p2)}")  # True

# 与 == 等价
print(f"== : {p1 == p2}")  # True

4.10.3 equals_exact(tolerance)

带容差的几何相等判断:

from shapely import Point

p1 = Point(1.0, 2.0)
p2 = Point(1.001, 2.001)

# 精确比较
print(f"equals: {p1.equals(p2)}")                    # False

# 容差比较
print(f"equals_exact(0.01): {p1.equals_exact(p2, 0.01)}")   # True
print(f"equals_exact(0.001): {p1.equals_exact(p2, 0.001)}")  # False
print(f"equals_exact(0.002): {p1.equals_exact(p2, 0.002)}")  # True

4.10.4 equals_identical()(Shapely 2.0+)

严格比较,要求 WKB 表示完全相同:

import shapely
from shapely import Polygon

# 拓扑相等但起始点不同
poly1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
poly2 = Polygon([(1, 0), (1, 1), (0, 1), (0, 0)])

print(f"equals: {poly1.equals(poly2)}")            # True(拓扑相等)
print(f"equals_identical: {shapely.equals_identical(poly1, poly2)}")  # False
# 因为坐标起始点不同,WKB 表示不同

# 完全相同的几何
poly3 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
print(f"equals_identical: {shapely.equals_identical(poly1, poly3)}")  # True

4.11 布尔运算符重载

4.11.1 集合运算

Shapely 重载了 Python 的位运算符用于几何集合运算:

from shapely import box

a = box(0, 0, 3, 3)
b = box(1, 1, 4, 4)

# 交集 intersection
inter = a & b
print(f"交集(&): {inter.wkt}")
print(f"交集面积: {inter.area}")  # 4.0

# 并集 union
union = a | b
print(f"并集(|)面积: {union.area}")  # 14.0

# 差集 difference
diff = a - b
print(f"差集(-)面积: {diff.area}")  # 5.0

# 对称差集 symmetric_difference
sym = a ^ b
print(f"对称差集(^)面积: {sym.area}")  # 10.0

4.11.2 等价方法调用

from shapely import box

a = box(0, 0, 3, 3)
b = box(1, 1, 4, 4)

# 运算符和方法完全等价
assert (a & b).equals(a.intersection(b))
assert (a | b).equals(a.union(b))
assert (a - b).equals(a.difference(b))
assert (a ^ b).equals(a.symmetric_difference(b))

print("所有运算符与方法结果一致 ✓")

4.12 派生几何属性

4.12.1 质心 .centroid

from shapely import Polygon, MultiPoint

# 多边形质心
rect = Polygon([(0, 0), (10, 0), (10, 6), (0, 6)])
print(f"矩形质心: {rect.centroid}")  # POINT (5 3)

# 三角形质心
tri = Polygon([(0, 0), (6, 0), (3, 6)])
print(f"三角形质心: {tri.centroid}")  # POINT (3 2)

# 多点质心
mp = MultiPoint([(0, 0), (4, 0), (4, 4), (0, 4)])
print(f"多点质心: {mp.centroid}")  # POINT (2 2)

4.12.2 边界 .boundary

from shapely import Point, LineString, Polygon

# 点没有边界
p = Point(1, 2)
print(f"Point boundary: {p.boundary}")
# GEOMETRYCOLLECTION EMPTY

# 线的边界是端点
line = LineString([(0, 0), (5, 5)])
print(f"Line boundary: {line.boundary}")
# MULTIPOINT (0 0, 5 5)

# 多边形的边界是外环和内环
poly = Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])
print(f"Polygon boundary: {poly.boundary}")
# LINEARRING (0 0, 4 0, 4 3, 0 3, 0 0)

4.12.3 凸包 .convex_hull

from shapely import MultiPoint, Polygon

# 点集的凸包
points = MultiPoint([(0, 0), (1, 3), (3, 1), (2, 4), (4, 2)])
hull = points.convex_hull
print(f"凸包: {hull}")
print(f"凸包面积: {hull.area}")

# 凹多边形的凸包
concave = Polygon([(0, 0), (4, 0), (4, 4), (2, 2), (0, 4)])
hull = concave.convex_hull
print(f"凹多边形面积: {concave.area}")
print(f"凸包面积: {hull.area}")
# 凸包面积 >= 原多边形面积

4.12.4 包络线 .envelope

from shapely import Polygon

# 不规则多边形的包络线(最小外接矩形,轴对齐)
poly = Polygon([(1, 1), (5, 1), (4, 4), (2, 5)])
envelope = poly.envelope
print(f"包络线: {envelope}")
# POLYGON ((1 1, 5 1, 5 5, 1 5, 1 1))
print(f"原面积: {poly.area}")
print(f"包络面积: {envelope.area}")

4.12.5 最小旋转矩形 .minimum_rotated_rectangle

from shapely import Polygon

# 倾斜的窄长多边形
poly = Polygon([(0, 0), (4, 3), (3, 4), (-1, 1)])
mrr = poly.minimum_rotated_rectangle
print(f"最小旋转矩形: {mrr}")
print(f"原面积: {poly.area}")
print(f"旋转矩形面积: {mrr.area}")

4.13 距离计算

4.13.1 .distance(other)

from shapely import Point, LineString, Polygon

# 点到点距离
p1 = Point(0, 0)
p2 = Point(3, 4)
print(f"点到点: {p1.distance(p2)}")  # 5.0

# 点到线距离
line = LineString([(0, 2), (4, 2)])
p = Point(2, 0)
print(f"点到线: {p.distance(line)}")  # 2.0

# 点到多边形距离
poly = Polygon([(5, 5), (10, 5), (10, 10), (5, 10)])
p = Point(0, 0)
print(f"点到多边形: {p.distance(poly):.4f}")  # 7.0711

# 点在多边形内部时距离为 0
p_inside = Point(7, 7)
print(f"内部点到多边形: {p_inside.distance(poly)}")  # 0.0

4.13.2 .hausdorff_distance(other)

Hausdorff 距离衡量两个几何体之间的最大最小距离:

from shapely import LineString

line1 = LineString([(0, 0), (1, 0), (2, 0)])
line2 = LineString([(0, 1), (1, 1), (2, 1)])
line3 = LineString([(0, 0), (1, 3), (2, 0)])

print(f"平行线 Hausdorff: {line1.hausdorff_distance(line2)}")  # 1.0
print(f"不规则线 Hausdorff: {line1.hausdorff_distance(line3)}")  # 3.0

4.14 其他实用方法

4.14.1 .buffer(distance)

from shapely import Point, LineString

# 点缓冲区(圆形)
p = Point(0, 0)
circle = p.buffer(5)
print(f"圆面积: {circle.area:.2f}")  # ≈ 78.54

# 线缓冲区(走廊)
line = LineString([(0, 0), (10, 0)])
corridor = line.buffer(2)
print(f"走廊面积: {corridor.area:.2f}")

# 负缓冲区(向内收缩)
from shapely import Polygon
poly = Polygon([(0, 0), (10, 0), (10, 10), (0, 10)])
shrunk = poly.buffer(-2)
print(f"收缩后面积: {shrunk.area:.2f}")  # 36.0 + 角落差异

4.14.2 .simplify(tolerance)

from shapely import LineString

# 复杂线简化
line = LineString([(0, 0), (1, 0.1), (2, -0.1), (3, 0.05), (4, 0), (5, 0)])
simplified = line.simplify(0.2)
print(f"原始顶点数: {len(line.coords)}")       # 6
print(f"简化后顶点数: {len(simplified.coords)}")  # 2
print(f"简化后: {simplified}")                     # LINESTRING (0 0, 5 0)

4.14.3 .representative_point()

返回保证在几何体内部的一个点(不一定是质心):

from shapely import Polygon

# U 形多边形(质心可能在外部)
u_shape = Polygon([
    (0, 0), (4, 0), (4, 4), (3, 4), (3, 1), (1, 1), (1, 4), (0, 4)
])

centroid = u_shape.centroid
rep_point = u_shape.representative_point()

print(f"质心: {centroid}")
print(f"质心在内部: {u_shape.contains(centroid)}")  # 可能 False

print(f"代表点: {rep_point}")
print(f"代表点在内部: {u_shape.contains(rep_point)}")  # 始终 True

4.15 本章小结

本章系统地介绍了 Shapely 几何对象的所有属性与方法:

类别 属性/方法 说明
度量 .area, .length 面积和长度
坐标 .bounds, .coords, .x/.y/.z 边界框和坐标访问
类型 .geom_type, .has_z, .is_empty 几何类型信息
验证 .is_valid, .is_simple, .is_ring, .is_closed 几何有效性
序列化 .wkt, .wkb, .wkb_hex WKT/WKB 格式
接口 __geo_interface__ GeoJSON 兼容
组件 .exterior, .interiors, .geoms 子几何访问
派生 .centroid, .boundary, .convex_hull, .envelope 派生几何
比较 ==, equals(), equals_exact(), equals_identical() 几何比较
运算符 &, \|, -, ^ 集合运算快捷方式
距离 .distance(), .hausdorff_distance() 距离计算

下一章我们将深入学习空间关系与谓词判断。