如何判断Shapely Polygon是否为矩形?解决起点导致的判断失效问题
判断Shapely多边形是否为矩形的正确方法
你的问题我太懂了——之前我也试过用点的数量来判断,结果遇到和你一样的坑:当多边形的起点在边中间或者有共线的冗余点时,靠simplify(0)后的坐标数判断完全失效。
为什么原来的方法不行?
shapely的simplify(0)只会移除完全重合的点,不会处理共线的中间点。比如你的poly2,虽然本质是矩形,但因为坐标序列里包含了边中间的点(比如(0,1)、(2,2)),这些点和前后点共线但不重合,所以simplify(0)后依然保留,导致坐标数远大于5,误判为非矩形。
正确的判断思路
矩形的核心是几何特征,不是点的数量。我们需要基于以下几点验证:
- 多边形是简单闭合的(无自交)
- 清理掉所有共线的冗余点后,剩下4个唯一顶点
- 相邻边互相垂直
- 对边长度相等
- 对角线长度相等(可选,但能进一步确保正确性)
实现代码
这里有一个实用的判断函数,能处理你遇到的情况:
from shapely.geometry import Polygon import math def is_rectangle(polygon, tolerance=1e-9): # 第一步:检查是否为简单闭合多边形 if not polygon.is_simple or not polygon.exterior.is_closed: return False # 提取外环坐标,移除闭合重复的最后一个点 coords = list(polygon.exterior.coords) if coords[0] == coords[-1]: coords = coords[:-1] # 第二步:清理共线的冗余点 cleaned_vertices = [] for i in range(len(coords)): # 获取当前点、前一个点、后一个点(循环处理首尾) prev_point = coords[i-1] if i > 0 else coords[-1] curr_point = coords[i] next_point = coords[i+1] if i < len(coords)-1 else coords[0] # 用叉乘判断三点是否共线:叉乘为0则共线 cross_product = (curr_point[0] - prev_point[0]) * (next_point[1] - prev_point[1]) - \ (curr_point[1] - prev_point[1]) * (next_point[0] - prev_point[0]) if abs(cross_product) > tolerance: cleaned_vertices.append(curr_point) # 第三步:清理后必须恰好有4个唯一顶点 if len(cleaned_vertices) != 4: return False # 第四步:计算边的向量,验证相邻边垂直、对边等长 vectors = [] for i in range(4): x1, y1 = cleaned_vertices[i] x2, y2 = cleaned_vertices[(i+1)%4] vectors.append((x2 - x1, y2 - y1)) # 检查相邻向量垂直(点积接近0) for i in range(4): dot_product = vectors[i][0] * vectors[(i+1)%4][0] + vectors[i][1] * vectors[(i+1)%4][1] if abs(dot_product) > tolerance: return False # 检查对边长度相等 len0 = math.hypot(*vectors[0]) len1 = math.hypot(*vectors[1]) len2 = math.hypot(*vectors[2]) len3 = math.hypot(*vectors[3]) if abs(len0 - len2) > tolerance or abs(len1 - len3) > tolerance: return False # 可选:验证对角线长度相等,进一步确保是矩形而非菱形 diag1 = math.hypot(cleaned_vertices[2][0]-cleaned_vertices[0][0], cleaned_vertices[2][1]-cleaned_vertices[0][1]) diag2 = math.hypot(cleaned_vertices[3][0]-cleaned_vertices[1][0], cleaned_vertices[3][1]-cleaned_vertices[1][1]) if abs(diag1 - diag2) > tolerance: return False return True # 测试你的例子 poly1 = Polygon([(0, 0), (0, 1), (0, 3), (2, 3), (2, 2), (2, 0), (0, 0)]) print(is_rectangle(poly1)) # 输出: True poly2 = Polygon([(0, 1), (0, 3), (2, 3), (2, 2), (2, 0), (0, 0), (0, 1)]) print(is_rectangle(poly2)) # 输出: True
关键细节
- 用叉乘判断共线点:这是最可靠的方法,避免了浮点精度问题
- 加入
tolerance参数:处理Shapely计算中的浮点误差,避免因为微小数值差异导致误判 - 循环处理首尾点:确保多边形的闭合性不影响顶点清理
内容的提问来源于stack exchange,提问作者quentin
相关产品推荐
相关产品推荐

