使用math.atan2计算线段之间的角度(Python)

使用math.atan2计算线段之间的角度(Python),python,trigonometry,angle,atan2,cartesian-coordinates,Python,Trigonometry,Angle,Atan2,Cartesian Coordinates,我正在研究一个空间分析问题,该工作流程的一部分是计算连接线段之间的角度 每个线段仅由两个点组成,每个点有一对XY坐标(笛卡尔坐标)。这是GeoGebra的图片我总是对在0到180范围内获得正角度感兴趣。但是,根据输入线段中顶点的顺序,我得到了所有类型的角度 我使用的输入数据是以坐标元组的形式提供的。根据顶点创建顺序的不同,每条线段的最后一个/端点可能不同。下面是Python代码中的一些示例。线段的顺序是随机的,但在元组中,第一个元素是起点,第二个元素是终点DE线段将具有((1,1.5)、(2,

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

每个线段仅由两个点组成,每个点有一对XY坐标(笛卡尔坐标)。这是GeoGebra的图片我总是对在0到180范围内获得正角度感兴趣。但是,根据输入线段中顶点的顺序,我得到了所有类型的角度

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

但是,我需要确保在
DE,DF
ED,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)
我已经编写了一个小函数,它将线的组合作为参数,并计算它们之间的角度。我使用的是
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范围内的正值?
如果没有,我应该采取什么样的其他方法?

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

解决这个问题最简单、最合乎逻辑的方法是使用点积

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

导入数学
def dot(vA、vB):
返回vA[0]*vB[0]+vA[1]*vB[1]
def ang(直线A、直线B):
#得到更好的向量形式
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])]
#打点
dot_prod=dot(vA,vB)
#得到震级
magA=点(vA,vA)**0.5
magB=点(vB,vB)**0.5
#获取余弦值
cos_uu=dot_prod/magA/magB
#以弧度为单位获取角度,然后转换为度
角度=math.acos(点探针/magB/magA)
#基本角度=0:
#如if语句
返回360度-ang_度
其他:
返回ang_deg

现在尝试一下lineA和lineB的变体,它们都会给出相同的答案

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

其中,“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)

测试代码和输出的要点是感谢代码片段,很好的开始。代码的问题在于它从不报告任何钝角(>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(min)和1(max)。
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)