import * as geolib from 'geolib'; import { computeIntersection } from '../utils/computeIntersection'; /** * @param crsIntoEndpoint Course into arc endpoint * @param crsFromOrigin Course from arc origin point * @param start Arc origin point * @param end Leg endpoint * @param turnDir Turn direction * @returns Line segments forming arc from `start`to `end` */ export const generateTangentArc = ( crsIntoEndpoint: number, crsFromOrigin: number, start: NavFix, end: NavFix, turnDir?: TurnDirection ) => { const line: LineSegment[] = [[start.longitude, start.latitude]]; // Check if there even is an arc if (!crsFromOrigin.equal(crsIntoEndpoint)) { // Course to the end of the arc let crsFromStartToEnd; if (!turnDir || turnDir === 'E') { let prov = crsFromOrigin - crsIntoEndpoint; prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov; turnDir = prov > 0 ? 'L' : 'R'; } if (turnDir === 'R') { const delta = (360 - crsFromOrigin + crsIntoEndpoint).normaliseDegrees(); crsFromStartToEnd = (crsFromOrigin + delta / 2).normaliseDegrees(); } else { const delta = (crsFromOrigin + 360 - crsIntoEndpoint).normaliseDegrees(); crsFromStartToEnd = (crsFromOrigin - delta / 2).normaliseDegrees(); } // Arc end const intcArcOnCrsIntoEndpoint = computeIntersection( start, crsFromStartToEnd, end, crsIntoEndpoint.reciprocalCourse() ); if (!intcArcOnCrsIntoEndpoint) return null; let crsOrthogonalOnOrigin; let crsOrthogonalOnEndpoint; if (turnDir === 'R') { crsOrthogonalOnOrigin = (crsFromOrigin + 90).normaliseDegrees(); crsOrthogonalOnEndpoint = (crsIntoEndpoint + 90).normaliseDegrees(); } else { crsOrthogonalOnOrigin = (crsFromOrigin - 90).normaliseDegrees(); crsOrthogonalOnEndpoint = (crsIntoEndpoint - 90).normaliseDegrees(); } // Generate arc const arcCenter = computeIntersection( start, crsOrthogonalOnOrigin, intcArcOnCrsIntoEndpoint, crsOrthogonalOnEndpoint ); if (!arcCenter) return null; const arcRad = geolib.getDistance(arcCenter, start); crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse(); crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse(); // Start turn immediately if (turnDir === 'R') { crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1; } else { crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1; } while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) { if (turnDir === 'R') { const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees(); crsOrthogonalOnOrigin += delta < 1 ? delta : 1; crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees(); } else { const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees(); crsOrthogonalOnOrigin -= delta < 1 ? delta : 1; crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees(); } const arcFix = geolib.computeDestinationPoint(arcCenter, arcRad, crsOrthogonalOnOrigin); line.push([arcFix.longitude, arcFix.latitude]); } } return line; };