射线法是最实用的通用解法:从点向右发水平射线,统计与多边形边的交点数,奇数则在内,偶数在外;需处理顶点穿透和边上点等边界情况,顶点顺序无关但需闭合。

射线法(Ray Casting)是最实用的通用解法
绝大多数实际场景下,直接用射线法判断点是否在多边形内更可靠——它不依赖多边形是否凸、是否自相交(只要按标准规则定义“内部”),且实现简洁、可读性强。
核心思路:从目标点向右水平发射一条射线,统计与多边形各边的交点数;奇数次相交则在内部,偶数次(含 0)则在外。注意边界情况处理:
- 射线恰好穿过顶点时,只计一次有效交点(通常约定:仅当边的两个端点 y 值严格一上一下时才计数)
- 点恰好落在边上需单独判断(可用
shapely的contains+touches组合,或手写点到线段距离 ≤ 容差) - 多边形顶点顺序(顺/逆时针)不影响结果,但顶点列表必须闭合(首尾点可不重复,代码中会自动处理)
示例(无依赖纯 Python):
def point_in_polygon(x, y, poly):
n = len(poly)
inside = False
p1x, p1y = poly[0]
for i in range(1, n + 1):
p2x, p2y = poly[i % n]
if y > min(p1y, p2y):
if y <= max(p1y, p2y):
if x <= max(p1x, p2x):
if p1y != p2y:
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
return inside
点积法仅适用于凸多边形且需预处理
点积本身不能直接判断点在多边形内;真正起作用的是“叉积符号一致性”或“所有相邻边构成的转向角同向”,这常被误称为“点积法”。它本质是检查点是否始终位于每条有向边的同一侧(如左侧)。
立即学习“Python免费学习笔记(深入)”;
该方法快(O(n) 且无除法/开方),但限制极严:
- 多边形必须是凸的,且顶点按顺时针或逆时针顺序排列
- 需预先将多边形转换为一致朝向(可用叉积和判断并翻转)
- 对浮点误差敏感,边界点易误判,建议加小容差(如
1e-10) - 无法处理凹多边形、带孔区域、自交图形
简化的凸多边形判断(假设顶点逆时针):
def point_in_convex_polygon(x, y, poly):
n = len(poly)
for i in range(n):
x1, y1 = poly[i]
x2, y2 = poly[(i + 1) % n]
# 叉积:(x2-x1, y2-y1) × (x-x1, y-y1)
cross = (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1)
if cross < -1e-10: # 点在边右侧 → 不在内部
return False
return True
别自己造轮子:优先用 shapely 处理真实数据
手写射线法或叉积逻辑适合学习和简单控制流,但生产环境强烈建议用 shapely——它已优化边界处理、支持带洞多边形、地理坐标系、缓存加速,且通过大量测试。
常见坑点:
-
Point(x, y).within(Polygon(poly))返回False当点在边界上;改用Polygon(poly).contains(Point(x, y))也不含边界;真正要“闭包”语义,得用Polygon(poly).covers(Point(x, y)) - 输入顶点若未闭合(首≠尾),
Polygon构造时会自动闭合,但若含重复点或退化边,可能触发TopologyException - 大规模批量判断时,先构建
PreparedGeometry可提速 5–10 倍:from shapely.prepared import prep; prepared_poly = prep(poly_obj)
性能与精度取舍的关键细节
纯 Python 射线法在万级点/百级多边形下仍够用,但若每帧判断上万次(如游戏、GIS 渲染),就得考虑空间索引或 C 扩展:
- 用
rtree+shapely先粗筛包围盒,再精判,可降 90%+ 计算量 - 避免在循环内反复构造
Point或Polygon对象;复用对象或用shapely.geometry.shape解析 GeoJSON 一次后缓存 - 经纬度坐标直接套用平面几何算法会偏移,必须先投影(如用
pyproj转 Web Mercator)或改用球面算法(如geopy不适用,应选pyproj.Geod.npts配合球面射线法)
真正容易被忽略的是:**“内部”的定义本身依赖业务场景**——GIS 中常用 even-odd 规则(射线法默认),而渲染引擎可能用 non-zero winding rule(需改用叉积累加符号)。没确认清楚这个,代码再快也白搭。


















