import "./utils/extensions.ts"; import { handleTurnAtFix } from "./utils/handleTurnAtFix.ts"; import * as geolib from "geolib"; import geojson from "geojson"; class Parser { private static _instance: Parser; private waypoints: Waypoint[]; private runways: Runway[]; private terminals: Terminal[]; public static AC_SPEED = 250; private constructor( waypoints: Waypoint[], runways: Runway[], terminals: Terminal[] ) { this.waypoints = waypoints; this.runways = runways; this.terminals = terminals; } public static instance = async () => { if (!Parser._instance) { const waypoints = await (await fetch("navdata/Waypoints.json")).json(); const runways = await (await fetch("navdata/Runways.json")).json(); const terminals = await (await fetch("navdata/Terminals.json")).json(); Parser._instance = new Parser(waypoints, runways, terminals); } return Parser._instance; }; public parse = async (terminalID: number) => { // Private functions /** * @param line Line segments */ const updateLastCourse = (line: LineSegment[]) => { lastCourse = geolib.getGreatCircleBearing( { latitude: line.at(-2)![1], longitude: line.at(-2)![0], }, { latitude: line.at(-1)![1], longitude: line.at(-1)![0], } ); }; // Get Procedure main const terminal = this.terminals.find(({ ID }) => ID === terminalID); if (!terminal) throw new Error("Procedure does not exists"); // Get runway this procedure is for const runway = this.runways.find(({ ID }) => ID === terminal.RwyID); if (!runway) throw new Error("Procedure links to non existent Runway"); // Load procedure const procedure = (await ( await fetch(`navdata/TermID_${terminalID}.json`) ).json()) as TerminalEntry[]; // Output variables const navFixes: NavFix[] = []; const lineSegments: { line: LineSegment[] }[] = []; // Initials navFixes.push({ latitude: runway.Latitude, longitude: runway.Longitude, altitude: runway.Elevation, speed: 0, name: runway.Ident, }); let lastCourse = runway.TrueHeading; // Main for (let index = 0; index < procedure.length; index++) { const leg = procedure[index]; const previousFix = navFixes.at(-1)!; const waypoint = this.waypoints.filter(({ ID }) => ID === leg.WptID)[0]; switch (leg.TrackCode) { case "AF": case "CA": case "CD": break; case "CF": { const _leg = leg as CFTerminalEntry; const targetFix: NavFix = { latitude: _leg.WptLat, longitude: _leg.WptLon, name: waypoint?.Ident ?? undefined, "marker-color": _leg.IsFlyOver ? "#ff0000" : undefined, isFlyOver: _leg.IsFlyOver, altitude: previousFix.altitude, }; navFixes.push(targetFix); const line = handleTurnAtFix( _leg.Course.toTrue(previousFix), _leg.Course.toTrue(previousFix), lastCourse, previousFix, targetFix, _leg.TurnDir ); lineSegments.push({ line }); updateLastCourse(lineSegments.at(-1)!.line); break; } case "CI": case "CR": case "DF": case "FA": case "FC": case "FD": case "FM": case "HA": case "HF": case "HM": case "IF": case "PI": case "RF": case "TF": case "VA": case "VD": case "VI": case "VM": case "VR": default: console.error("Unknown TrackCode", leg.TrackCode); break; } } return geojson.parse([...navFixes, ...lineSegments], { Point: ["latitude", "longitude"], LineString: "line", }); }; } export default Parser;