【问题标题】:Calculating angles between line segments (Python) with math.atan2使用 math.atan2 计算线段之间的角度(Python)
【发布时间】:2015-02-01 08:44:16
【问题描述】:

我正在研究一个空间分析问题,这个工作流程的一部分是计算连接线段之间的角度。

每条线段只由两个点组成,每个点都有一对XY坐标(笛卡尔坐标)。这是来自 GeoGebra 的图像。 我总是对获得 0 到 180 范围内的正角感兴趣。但是,我会根据输入线段中顶点的顺序得到各种角度。

我使用的输入数据以坐标元组的形式提供。根据顶点创建顺序,每条线段的最后/结束点可能不同。以下是 Python 代码中的一些案例。我得到它们的线段的顺序是随机的,但是在元组的元组中,第一个元素是起点,第二个是终点。例如,DE 线段将具有 ((1,1.5),(2,2)),而 (1,1.5) 是起点,因为它在坐标元组中具有第一个位置。

但我需要确保DE,DFED,DF 之间的角度相同,等等。

vertexType = "same start point; order 1"
            #X, Y    X Y coords
lineA = ((1,1.5),(2,2)) #DE
lineB = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "same start point; order 2"
lineB = ((1,1.5),(2,2)) #DE
lineA = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)

vertexType = "same end point; order 1"
lineA = ((2,2),(1,1.5)) #ED
lineB = ((2.5,0.5),(1,1.5)) #FE
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "same end point; order 2"
lineB = ((2,2),(1,1.5)) #ED
lineA = ((2.5,0.5),(1,1.5)) #FE
calcAngle(lineA, lineB,vertexType)

vertexType = "one line after another - down; order 1"
lineA = ((2,2),(1,1.5)) #ED
lineB = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "one line after another - down; order 2"
lineB = ((2,2),(1,1.5)) #ED
lineA = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)

vertexType = "one line after another - up; line order 1"
lineA = ((1,1.5),(2,2)) #DE
lineB = ((2.5,0.5),(1,1.5)) #FD
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "one line after another - up; line order 2"
lineB = ((1,1.5),(2,2)) #DE
lineA = ((2.5,0.5),(1,1.5)) #FD
calcAngle(lineA, lineB,vertexType)

我编写了一个小函数,它将线条的组合作为 args 并计算它们之间的角度。我正在使用似乎最适合此的math.atan2

def calcAngle(lineA,lineB,vertexType):
    line1Y1 = lineA[0][1]
    line1X1 = lineA[0][0]
    line1Y2 = lineA[1][1]
    line1X2 = lineA[1][0]

    line2Y1 = lineB[0][1]
    line2X1 = lineB[0][0]
    line2Y2 = lineB[1][1]
    line2X2 = lineB[1][0]

    #calculate angle between pairs of lines
    angle1 = math.atan2(line1Y1-line1Y2,line1X1-line1X2)
    angle2 = math.atan2(line2Y1-line2Y2,line2X1-line2X2)
    angleDegrees = (angle1-angle2) * 360 / (2*math.pi)
    print angleDegrees, vertexType

我得到的输出是:

> -299.744881297 same start point; order 1
> 299.744881297 same start point; order 2
> 60.2551187031 same end point; order 1
> -60.2551187031 same end point; order 2
> -119.744881297 one line after another - down; order 1
> 119.744881297 one line after another - down; order 2
> -119.744881297 one line after another - up; line order 1
> 119.744881297 one line after another - up; line order 2

如您所见,根据线段中顶点的顺序和线段顺序,我得到不同的值。我试图通过找出源线具有什么样的关系并翻转线,编辑角度等来对角度进行后处理。我已经结束了十几个这样的情况,在某些时候它们开始重叠,我再也找不到 -119.744 应该变成 60.255(锐角)还是保留为 119.744(钝角)等。

是否有任何离散的方法来处理我从math.atan2 收到的输出角度值,以仅获得 0 到 180 范围内的正值? 如果没有,还有什么其他方法我应该服用吗?

【问题讨论】:

    标签: python trigonometry angle atan2 cartesian-coordinates


    【解决方案1】:

    解决此问题的最简单方法是使用点积。

    试试这段代码(我几乎已经评论了所有内容):

    import math
    
    def dot(vA, vB):
        return vA[0]*vB[0]+vA[1]*vB[1]
    
    def ang(lineA, lineB):
        # Get nicer vector form
        vA = [(lineA[0][0]-lineA[1][0]), (lineA[0][1]-lineA[1][1])]
        vB = [(lineB[0][0]-lineB[1][0]), (lineB[0][1]-lineB[1][1])]
        # Get dot prod
        dot_prod = dot(vA, vB)
        # Get magnitudes
        magA = dot(vA, vA)**0.5
        magB = dot(vB, vB)**0.5
        # Get cosine value
        cos_ = dot_prod/magA/magB
        # Get angle in radians and then convert to degrees
        angle = math.acos(dot_prod/magB/magA)
        # Basically doing angle <- angle mod 360
        ang_deg = math.degrees(angle)%360
        
        if ang_deg-180>=0:
            # As in if statement
            return 360 - ang_deg
        else: 
            
            return ang_deg
    

    现在试试你的 lineA 和 lineB 的变体,都应该给出相同的答案。

    【讨论】:

    • 测试代码和输出的要点是here
    • 感谢代码 sn-p,很好的开始。代码的问题在于它从不报告任何钝角 (>90)。试试lineA = ((0.6,3.6),(1.6,3)) lineB = ((1.6,3),(2,3.6))。它报告 87.27,但应该是 92.73。有什么办法可以解决这个问题?
    • 我已经编辑了我的帖子。我相信我已经修复了它以给出钝角。看看吧。
    • 感谢您的更新。我不得不使用我的旧代码,我必须翻转线段的方向,这样向量将具有相同的起点(否则你无法计算我想要的方式之间的角度)和你的代码(在编辑之前)。它工作得很好,非常感谢你的数学工作。
    • '%360' 是多余的,因为 arccos (math.acos) 的输出在区间 [0, pi] 弧度(或 [0, 180] 度)内。因此,您永远不应获得小于 0 或大于 180 度的角度。后面的 if-else 语句也是多余的。您还应该考虑将 math.acos 的输入剪裁为 -1(最小值)和 1(最大值)。
    【解决方案2】:

    使用公式的替代解决方案:

    其中“m1”是线 1 的斜率,“m2”是线 2 的斜率。如果线 1 由点 P1 = [x1, y1] 和 P2 = [x2, y2] 定义,则斜率 ' m' 是:

    通过使用上面的公式,您可以找到两条线之间的角度,如下所示:

    def slope(x1, y1, x2, y2): # Line slope given two points:
        return (y2-y1)/(x2-x1)
    
    def angle(s1, s2): 
        return math.degrees(math.atan((s2-s1)/(1+(s2*s1))))
    
    lineA = ((0.6, 3.6), (1.6, 3))
    lineB = ((1.6, 3), (2, 3.6))
    
    slope1 = slope(lineA[0][0], lineA[0][1], lineA[1][0], lineA[1][1])
    slope2 = slope(lineB[0][0], lineB[0][1], lineB[1][0], lineB[1][1])
    
    ang = angle(slope1, slope2)
    print('Angle in degrees = ', ang)
    

    【讨论】:

      【解决方案3】:

      工作量太大。取两个向量的dot product 的反余弦的绝对值除以每条线的长度。

      【讨论】:

        猜你喜欢
        • 2014-10-11
        • 2019-07-09
        • 2011-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-19
        相关资源
        最近更新 更多