我在MapboxGL(js)世界地图上绘制了一条线,它遵循轨道物体的路径。我通过在对象轨道更新时将一组新的十进制经度/纬度坐标添加到线几何阵列中来实现这一点。
Mapbox(和其他)有一个已知的问题,当画一条穿过 180° 子午线(经度)的线时,我们没有得到一条从 a 到 b 的漂亮直线,我们得到了一条很长的线,从 a 到 b 环绕整个地球:
instead of: we get: / / / /___ .../..... ......... 180° meridian / ___ / / / /
这里和Mapbox的“公认”答案建议转换到0 /360经度范围,但这只是将问题从赤道转移到了极点。这对于大多数一般的建议来说是没问题的,但是对于轨道跟踪来说仍然是一个问题,因为我们可能会穿过0 /360子午线。
我的解决方案是使用多线几何,当我穿过这条子午线时,将我的坐标分解成新的数组,然而,这将总是留下一个微小的间隙,或者,如果我“180,lat”任一侧,我们会在子午线处得到一个“纽结”:
gap: or kink: / / / / ........ .....|... 180° meridian / / / / /
所以我需要弄清楚如果经度在子午线上,确切的纬度是多少,知道两侧的起点和终点:
+170 | p2 /: | / : | / : 180 -|-----/ pX? -- 180° meridian | /: : (lng) | / : : | / : : -170 |_/___:___:___ p1 x? (lat)
我需要求解纬度x,这样我就可以生成pX(如果经度为180,则知道p1和p2)。一旦我有了pX,我可以将其添加到最后一行的末尾和下一行的开头,从而缩小差距(或平滑扭结)。
我知道这是基本的Trig,但我的老人大脑让我失望了..再。。。。
以这种方式分割线的简单方法是使用Turf的lineSplit
函数。类似的东西:
const meridian = turf.lineString([[180, -90], [180, 90]]);
const linePieces = turf.lineSplit(myline, meridian);
我还没有试过,所以不确定草皮本身在子午线上是否有任何奇怪之处。如果是这样,您可能需要临时将坐标转换到其他地方或其他地方。
在任何情况下都比自己做三角学要好,尤其是因为它可能会引入误差,因为世界不是平的。
解决!使用基本的Trig(在写问题时 - 所以我无论如何都要发布它,以防万一它对其他人有帮助):
我们基本上使用两个直角三角形:p1到p2,以及较小的直角三角形,其中相对的一侧停在子午线上,两者都具有相同的斜边角。因此,我们有:
+170 | p2 /| | / | | / | 180 -|-----/ pX? -- 180° meridian | /: | (lng) | / : A | | / B: | -170 |_/___:___|___ p1 x? (lat)
其中A是我们的p1到p2直角三角形,B是从p1经度到子午线的三角形,我们需要计算其相邻边。
毕达哥拉斯基本上告诉我们,我们只需要直角三角形的两个数据点(除了直角)来解决任何其他问题。
我们已经有了A的相反和相邻的长度:
+170 | p2 /| | /α| | / | 180 -|-- / | -- 180° meridian | / | (lng) | / A | oppositeA | / | -170 |_/β______|___ p1 adjacentA (lat)
因此,从这里我们需要计算A的斜边,以获得A (α)的斜边角度,这样我们可以在以后使用它:
// add 360 to a negative longitude to shift lng from -180/+180, to 0/360
p1 = { lng: p1.lng < 0 ? p1.lng + 360 : p1.lng, lat: p1.lat }
p2 = { lng: p2.lng < 0 ? p2.lng + 360 : p2.lng, lat: p2.lat }
let oppositeA = Math.abs(p2.lng - p1.lng) // get A opposite length
let adjacentA = Math.abs(p2.lat - p1.lat) // get A adjacent length
let hypotenuseA = Math.sqrt(Math.pow(oppositeA,2) + Math.pow(adjacentA,2)) // calc A hypotenuse
let angleA = Math.asin(oppositeA / hypotenuseA) // calc A hypotenuse angle
现在我们需要B的新的对立面(p1.lng到180°)和我们计算出的A的角度来算出B的新斜边,这样我们就可以得到B的新邻点:
+170 | p2 / | / | / 180 -|-- / -- 180° meridian | /: B (lng) | /α: | / : oppositeB -170 |_/___:___ ___ p1 adjacentB (lat)
let oppositeB = Math.abs(180 - p1.lng) // get B opposite
let hypotenuseB = oppositeB / Math.cos(angleA) // calc B hypotenuse using A angle
let adjacentB = Math.sqrt(Math.pow(oppositeB,2) + Math.pow(hypotenuseB,2)); calc B adjacent
现在我们在p1纬度附近添加新的,我们有x!所以:
let pX = { lng: 180, lat: p1.lat + adjacentB }
结束最后一行数组,然后用pX开始下一行,间隙完全闭合!
高中数学(嗯,毕达哥拉斯的天才)来拯救!我知道那老人的脑子里有个地方。。。。。