You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 09:07:47