I am plotting a line on a MapboxGL (js) world map that follows the path of orbital objects. I do this by adding a new set of decimal longitude/latitude coordinates to a line geometry array as the orbit of the object updates.
There is a known issue with Mapbox (and others) that when drawing a line that crosses the 180° meridian (longitude), we do not get a nice straight line from a to b, we get a very long line that wraps around the whole globe from a to b:
instead of: we get: / / / /___ .../..... ......... 180° meridian / ___ / / / /
"Accepted" answers here and at Mapbox suggest shifting to a 0°/360° longitude range, but this just moves the problem from the equator to the pole. This is fine for most general proposes, but is still an issue for orbital tracking where we may be crossing the 0°/360° meridian.
My solution is to use MultiLine geometry and break up my coords into new arrays when I cross this meridian, however, this will always leave a wee gap, or, if I "180, lat" either side, we get a "kink" at the meridian:
gap: or kink: / / / / ........ .....|... 180° meridian / / / / /
So I need to figure out what the exact latitude would be if the longitude is on the meridian, knowing the start and end points either side:
+170 | p2 /: | / : | / : 180 -|-----/ pX? -- 180° meridian | /: : (lng) | / : : | / : : -170 |_/___:___:___ p1 x? (lat)
I need to solve for latitude x so I can generate pX (knowing p1 and p2 if longitude where 180). Once I have pX, I can add this to the end of the last line and to the beginning of the next, thus closing the gap (or smoothing the "kink").
I know this is basic Trig, but my old-man-brain has failed me .. again ....
SOLVED! With basic Trig (while writing the question - so I am posting it anyway, just incase it helps someone else):
We are basically playing with two right triangles: p1 to p2, and the smaller right triangle where the opposite side stops at the meridian, both with the same hypotenuse angle. So, we have:
+170 | p2 /| | / | | / | 180 -|-----/ pX? -- 180° meridian | /: | (lng) | / : A | | / B: | -170 |_/___:___|___ p1 x? (lat)
Where A is our p1 to p2 right angle triangle and B is the triangle from p1 longitude to the meridian, whose adjacent side we need to work out.
Pythagoras basically teaches us that all we need is two points of data (other then the right angle) of a right triangle to solve any other.
We already have the opposite and adjacent lengths of A:
+170 | p2 /| | /α| | / | 180 -|-- / | -- 180° meridian | / | (lng) | / A | oppositeA | / | -170 |_/β______|___ p1 adjacentA (lat)
So from here we need to calculate the hypotenuse of A to get the angle of the hypotenuse of A (α) so we can use it later:
// 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
Now we need the new opposite of B (p1.lng to 180) and our calculated angle of A to work out the new hypotenuse of B so we can get the new adjacent of 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
Now we add the new adjacent to p1 latitude, and we have x! So:
let pX = { lng: 180, lat: p1.lat + adjacentB }
End the last line array and start the next with pX, and the gap is perfectly closed!
Highschool math (well, the genius of Pythagoras) to the rescue! I knew it was rattling around in that old-man-brain somewhere .....