149 lines
3.9 KiB
TypeScript

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;