使用 math.atan2 计算线段 (Python) 之间的角度
Calculating angles between line segments (Python) with math.atan2
我正在处理空间分析问题,此工作流的一部分是计算连接线段之间的角度。
每条线段只由两个点组成,每个点都有一对XY坐标(直角坐标)。这是来自 GeoGebra 的图片。 我一直对获得 0 到 180 度范围内的正角度感兴趣。但是,根据输入线段中顶点的顺序,我得到了各种角度。
我使用的输入数据以坐标元组的形式提供。根据顶点创建顺序,每个线段的 last/end 点可以不同。以下是 Python 代码中的一些案例。我得到它们的线段顺序是随机的,但在元组的元组中,第一个元素是起点,第二个元素是终点。例如,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
如您所见,根据线段中顶点的顺序和线段顺序,我得到了不同的值。我试图 post-通过找出源线有什么样的关系并翻转线,编辑角度等来处理角度。我已经结束了十几个这样的案例,并且在某些时候他们开始了重叠,我再也无法确定 -119.744 应该变为 60.255(锐角)还是保留为 119.744(钝角)等
是否有任何离散的方法来处理我从 math.atan2
收到的输出角度值,以仅获得 0 到 180 范围内的正值? 如果没有,我应该采取什么样的其他方法?
工作量太大。取两个向量的 dot product 的反余弦的绝对值除以每条线的长度。
解决此问题的最简单方法是使用点积。
试试这段代码(我几乎对所有内容都进行了评论):
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 的变体,所有答案都应该给出相同的答案。
使用公式的替代解决方案:
其中 '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)
我正在处理空间分析问题,此工作流的一部分是计算连接线段之间的角度。
每条线段只由两个点组成,每个点都有一对XY坐标(直角坐标)。这是来自 GeoGebra 的图片。 我一直对获得 0 到 180 度范围内的正角度感兴趣。但是,根据输入线段中顶点的顺序,我得到了各种角度。
我使用的输入数据以坐标元组的形式提供。根据顶点创建顺序,每个线段的 last/end 点可以不同。以下是 Python 代码中的一些案例。我得到它们的线段顺序是随机的,但在元组的元组中,第一个元素是起点,第二个元素是终点。例如,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
如您所见,根据线段中顶点的顺序和线段顺序,我得到了不同的值。我试图 post-通过找出源线有什么样的关系并翻转线,编辑角度等来处理角度。我已经结束了十几个这样的案例,并且在某些时候他们开始了重叠,我再也无法确定 -119.744 应该变为 60.255(锐角)还是保留为 119.744(钝角)等
是否有任何离散的方法来处理我从 math.atan2
收到的输出角度值,以仅获得 0 到 180 范围内的正值? 如果没有,我应该采取什么样的其他方法?
工作量太大。取两个向量的 dot product 的反余弦的绝对值除以每条线的长度。
解决此问题的最简单方法是使用点积。
试试这段代码(我几乎对所有内容都进行了评论):
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 的变体,所有答案都应该给出相同的答案。
使用公式的替代解决方案:
其中 '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)