前言
在canvas绘制箭头的过程中我遇到这样的问题:"我已知线段起点和终点的坐标,想要求线段的方位角和长度"。这实际上就是测量学当中的坐标反算,在这篇博客中我就将简单的介绍一下坐标反算的原理,以及如何使用JS封装一个坐标反算的方法。
1.坐标反算介绍
我们先来看一下坐标反算的概念:
根据直线始点和终点的坐标,计算直线的水平距离和直线的坐标方位角,称为坐标反算。
坐标反算的大致公式如下:
其中公式6.6用于计算线段AB的长度,公式6.7用于计算线段的坐标方位角。
2.计算直线长度
(1)勾股定理计算直线长度
直线长度实际上就是根据勾股定理进行计算的,请看下图:
可以看到ABb构成了一个直角三角形,所以根据勾股定理可得以下的公式 ( 其中L就是直线AB的长度 ):
L2 = ∆x2 + ∆y2
(2)计算坐标增量
下一步就是要求∆x、∆y的值,它们是坐标增量,在坐标正算的时候我们也求过,只不过那时我们使用的是三角函数。而在坐标反算中计算坐标增量就比较简单了,直接使用结束点的坐标减去起始点的坐标。
tip:在计算长度的时候相减的顺序并不重要,可以反过来用起始点减结束点;但是在计算方位角时就不能随意更改顺序了,可能就会导致最终的结果是直线AB的反方位角。
∆x = XB - XA
∆y = YB - YA
(3)封装计算长度的方法
此时我们就可以封装计算直线长度的方法了:
JavaScript
// t-坐标反算-计算直线长度
/**
*
* @param {Array} from 直线起始点坐标
* @param {Array} to 直线结束点坐标
*/
function calcLineLength(from, to) {
return Math.hypot(to[0] - from[0], to[1] - from[1])
}
上面的的方法中我用到了 Math.hypot()
函数,这个函数会返回所有参数的平方根。
3.计算方位角
(1)反算方位角的步骤
我们知道在坐标正算的过程中是用到了坐标方位角的,坐标正算的详情可以参考我的写的这篇博客:canvas绘图学习:坐标正算-CSDN博客
因此我们只需要将上面的过程倒过来就能求得坐标方位角:
第一步,用结束点坐标减去起始点坐标得到坐标增量
第二步,使用坐标增量和反三角函数计算出象限角
第三步,将象限角转换为方位角
(2)计算坐标增量
这里计算坐标增量的公式与上面一致:
∆x = XB - XA
∆y = YB - YA
在之前的坐标正算、计算直线长度 和现在的计算方位角都涉及到坐标增量的计算。但实际上这三个地方的坐标增量都是略有不同的。
坐标正算 | 使用 cos() 和 sin() 计算出来的坐标增量都是正数 |
---|---|
计算直线长度 | 在计算坐标增量时,可以用结束点坐标减起始点坐标;也可以用起始点坐标减结束点坐标。它们计算出来的结果是一致的。通过这种方式计算出来的坐标增量有正负。 |
计算坐标方位角 | 用结束点坐标减起始点坐标最终计算出来的是直线的正方位角(这是我们需要的);用起始点坐标减结束点坐标最终计算出来的是直线的反方位角。通过这种方式计算出来的坐标增量有正负。例如 :如果用 B点坐标 - A点坐标 计算出来的是 αAB如果用 A点坐标 - B点坐标 计算出来的是 αBA |
(3)反三角函数arctan() 计算方位角
之前在坐标正算的时候我们是使用cos(R)
和sin(R)
计算出了 x、y方向上的坐标增量。现在则需要反过来利用两个坐标增量计算出象限角R ,因此这里需要使用反正切函数 arctan()
, 它是tan()
的反函数。例如: tan(45°) = 1 arctan(1) = 45°。
进而可以推导出如下的公式:
tan(R) = ∆y/∆x arctan(∆y/∆x) = R
这个公式用代码表示就是:
JavaScript
// R表示象限角
const R = Math.atan(deltaY / deltaX) * (180 / Math.PI)
在上一节提到过,在坐标反算中计算出来的坐标增量是有正有负的。因此arctan(∆y/∆x)
的取值范围在 π/2 ~ - π/2 之间。下表是几个不同象限的方位角对应的 arctan(∆y/∆x)
的值:
坐标方位角 | arctan(∆y/∆x) 的结果 |
---|---|
30° | 30° |
130° | -50° |
230° | 50° |
330° | -30° |
上面的结果再取一个绝对值就是我们所需的象限角 R = |arctan(∆y/∆x)|
。因此最终的代码如下:
JavaScript
// R表示象限角
const R = Math.abs(Math.atan(deltaY / deltaX) * (180 / Math.PI))
最后在将象限角转换位方位角,转换方式如下:
坐标增量的符号及其所在的象限 | 象限角转方位角 |
---|---|
∆x > 0 ∆y > 0(第一象限) | α = R |
∆x < 0 ∆y > 0(第二象限) | α = 180° - R |
∆x < 0 ∆y < 0(第三象限) | α = R + 180° |
∆x > 0 ∆y < 0 (第四象限) | α = 360° - R |
(4)反三角函数arctan2() 计算方位角
除了arctan()
外 也可以使用 arctan2()
函数来计算方位角。arctan2(y,x)
所表达的意思是坐标原点为起点,指向(x,y)
的射线在坐标平面上与x轴正方向之间的角的角度。
因此通过arctan2()
函数就可以计算出 起点和终点分别为 (0,0)
和(∆x,∆y)
的直线A2B2 与 x轴正方之间的夹角。而直线A2B2 与 已知的直线AB是平行关系,它们的方位角相同。
arctan2()
计算的结果R2的取值范围在 π ~ - π 之间 ,如果点 (∆x,∆y)
落在三四象限则R2为负,点 (∆x,∆y)
落在一二象限则R2为正。所以arctan2()
所计算出来的角度与方位角的关系如下:
R2的符号以及点 (∆x,∆y)所处的象限 | 与方位角的关系 |
---|---|
R2 > 0 ( 一、二象限) | α = R2 |
R2 < 0 (三、四象限) | α = R2 + 360° |
最终转换成代码就是:
JavaScript
const R2 = Math.atan2(∆y,∆x) * (180 / Math.PI)
if(R2 > 0) {
α = R2
}else{
α = R2 + 360°
}
(5)封装计算方位角的方法
使用arctan()
函数计算方位角:
JavaScript
// t-坐标反算-计算方位角arctan()
/**
*
* @param {Array} from 直线起始点坐标
* @param {Array} to 直线结束点坐标
*/
function calcLineBearing(from, to) {
const deltaX = to[0] - from[0],
deltaY = to[1] - from[1]
const qAngle = Math.abs((Math.atan(deltaY / deltaX) * 180) / Math.PI)
let bearing
if (deltaX > 0 && deltaY >= 0) {
bearing = qAngle
} else if (deltaX <= 0 && deltaY > 0) {
bearing = 180 - qAngle
} else if (deltaX < 0 && deltaY <= 0) {
bearing = 180 + qAngle
} else if (deltaX >= 0 && deltaY < 0) {
bearing = 360 - qAngle
}
return bearing
}
使用arctan2()
函数计算方位角:
JavaScript
// t-坐标反算-计算方位角arctan2()
/**
*
* @param {Array} from 直线起始点坐标
* @param {Array} to 直线结束点坐标
*/
function calcLineBearing2(from, to) {
const deltaX = to[0] - from[0],
deltaY = to[1] - from[1]
const qAngle2 = (Math.atan2(deltaY , deltaX) * 180) / Math.PI
let bearing
if (qAngle2 > 0) {
bearing = qAngle2
} else {
bearing = qAngle2 + 360
}
return bearing
}