UNPKG

flight-planner

Version:

Plan and route VFR flights

252 lines (251 loc) 8.78 kB
import { calculateWindVector, capitalizeWords } from './utils.js'; import { bearing, bearingToAzimuth, distance } from "@turf/turf"; /** * Represents a waypoint in the flight planning system * * A waypoint is a specified geographical location used for navigation purposes. * It can be associated with a METAR weather observation station. * * @example * ```typescript * const location: WaypointLocation = { lat: 37.62, lng: -122.38 }; * const sfo = new Waypoint("KSFO", location); * ``` */ export class Waypoint { name; location; metarStation; /** * @param name The name of the waypoint * @param location The location of the waypoint * @returns An instance of the Waypoint class */ constructor(name, location) { this.name = capitalizeWords(name); this.location = location; } /** * Returns a string representation of the waypoint. * * @returns A string representation of the waypoint */ toString() { return `${this.name}`; } /** * Calculates the distance to the given waypoint. * * @param waypoint The waypoint to calculate the distance to * @returns The distance in nautical miles */ distanceTo(waypoint) { // const distance = turf.distance(from, to, { units: 'nauticalmiles' }); const distanceInKm = distance(this.location, waypoint.location); const distanceInNm = distanceInKm * 0.539957; // TODO: Move to constants return distanceInNm; } /** * Calculates the heading to the given waypoint. * * @param waypoint The waypoint to calculate the heading to * @returns The heading in degrees */ headingTo(waypoint) { const bearingValue = bearing(this.location, waypoint.location); return bearingToAzimuth(bearingValue); } } /** * Represents a reporting point in the flight plan. * A reporting point is a waypoint that may be required for the flight. * * @extends Waypoint */ export class ReportingPoint extends Waypoint { required; /** * @param name The name of the reporting point * @param location The location of the reporting point * @param required Whether the reporting point is required * @returns An instance of the ReportingPoint class */ constructor(name, location, required = false) { super(name, location); this.required = required; } } /** * Enum representing various types of airport radio frequencies. * * @enum {number} * @readonly */ export var FrequencyType; (function (FrequencyType) { FrequencyType[FrequencyType["Approach"] = 0] = "Approach"; FrequencyType[FrequencyType["APRON"] = 1] = "APRON"; FrequencyType[FrequencyType["Arrival"] = 2] = "Arrival"; FrequencyType[FrequencyType["Center"] = 3] = "Center"; FrequencyType[FrequencyType["CTAF"] = 4] = "CTAF"; FrequencyType[FrequencyType["Delivery"] = 5] = "Delivery"; FrequencyType[FrequencyType["Departure"] = 6] = "Departure"; FrequencyType[FrequencyType["FIS"] = 7] = "FIS"; FrequencyType[FrequencyType["Gliding"] = 8] = "Gliding"; FrequencyType[FrequencyType["Ground"] = 9] = "Ground"; FrequencyType[FrequencyType["Information"] = 10] = "Information"; FrequencyType[FrequencyType["Multicom"] = 11] = "Multicom"; FrequencyType[FrequencyType["Unicom"] = 12] = "Unicom"; FrequencyType[FrequencyType["Radar"] = 13] = "Radar"; FrequencyType[FrequencyType["Tower"] = 14] = "Tower"; FrequencyType[FrequencyType["ATIS"] = 15] = "ATIS"; FrequencyType[FrequencyType["Radio"] = 16] = "Radio"; FrequencyType[FrequencyType["Other"] = 17] = "Other"; FrequencyType[FrequencyType["AIRMET"] = 18] = "AIRMET"; FrequencyType[FrequencyType["AWOS"] = 19] = "AWOS"; FrequencyType[FrequencyType["Lights"] = 20] = "Lights"; FrequencyType[FrequencyType["VOLMET"] = 21] = "VOLMET"; FrequencyType[FrequencyType["AFIS"] = 22] = "AFIS"; })(FrequencyType || (FrequencyType = {})); /** * Defines the various types of aerodromes. * * @enum {number} * @readonly */ export var AerodromeType; (function (AerodromeType) { AerodromeType[AerodromeType["Airport"] = 0] = "Airport"; AerodromeType[AerodromeType["GliderSite"] = 1] = "GliderSite"; AerodromeType[AerodromeType["AirfieldCivil"] = 2] = "AirfieldCivil"; AerodromeType[AerodromeType["InternationalAirport"] = 3] = "InternationalAirport"; AerodromeType[AerodromeType["HeliportMilitary"] = 4] = "HeliportMilitary"; AerodromeType[AerodromeType["MilitaryAerodrome"] = 5] = "MilitaryAerodrome"; AerodromeType[AerodromeType["UltraLightFlyingSite"] = 6] = "UltraLightFlyingSite"; AerodromeType[AerodromeType["HeliportCivil"] = 7] = "HeliportCivil"; AerodromeType[AerodromeType["AerodromeClosed"] = 8] = "AerodromeClosed"; AerodromeType[AerodromeType["AirportIFR"] = 9] = "AirportIFR"; AerodromeType[AerodromeType["AirfieldWater"] = 10] = "AirfieldWater"; AerodromeType[AerodromeType["LandingStrip"] = 11] = "LandingStrip"; AerodromeType[AerodromeType["AgriculturalLandingStrip"] = 12] = "AgriculturalLandingStrip"; AerodromeType[AerodromeType["Altiport"] = 13] = "Altiport"; })(AerodromeType || (AerodromeType = {})); /** * Converts a numeric frequency type to its enum value * * @param type The numeric value of the frequency type * @returns The corresponding FrequencyType enum value */ export const validateFrequencyType = (type) => { if (Object.values(FrequencyType).includes(type) && typeof type === 'number') { return type; } return FrequencyType.Other; }; /** * Represents an aerodrome (airport) in the flight planning system. * * @extends Waypoint */ export class Aerodrome extends Waypoint { options; /** * @param options The options for creating the Aerodrome instance * @returns An instance of the Aerodrome class */ constructor(options) { super(options.name, options.location); this.options = options; } /** * Returns the ICAO code of the airport. * * @returns The ICAO code of the airport */ get ICAO() { return this.options.ICAO; } /** * Returns the IATA code of the airport. * * @returns The IATA code of the airport */ get IATA() { return this.options.IATA; } /** * Returns the runways of the airport. * * @returns An array of runways associated with the airport */ get runways() { return this.options.runways; } /** * Returns the frequencies of the airport. * * @returns An array of frequencies associated with the airport */ get frequencies() { return this.options.frequencies; } /** * Returns the elevation of the airport. * * @returns The elevation of the airport in feet */ get fieldElevation() { return this.options.elevation; } /** * Returns a string representation of the airport. * * @returns A string representation of the airport */ toString() { return `${this.name} (${this.options.ICAO})`; } /** * Returns the QFE (atmospheric pressure at aerodrome elevation) value. * * @returns The QFE value in hPa (hectopascals), rounded to 2 decimal places, * or undefined if elevation or QNH data is not available */ get QFE() { if (!this.fieldElevation || !this.metarStation || !this.metarStation.metar.qnh) { return undefined; } // TODO: Standardize units return Math.round((this.metarStation.metar.qnh.value - (this.fieldElevation / 30)) * 100) / 100; } /** * Calculates the wind vectors for all runways of the airport. * * @returns The wind vectors for the runways in descending order of headwind */ get runwayWind() { if (!this.metarStation) { return undefined; } return this.options.runways .map(runway => Aerodrome.calculateRunwayWindVector(runway, this.metarStation.metar.wind)) .sort((a, b) => b.headwind - a.headwind); } /** * Calculates the wind vector for a specific runway. * * @param runway The runway to calculate the wind vector for * @param wind The current wind data from METAR * @returns The calculated runway wind vector * @private */ static calculateRunwayWindVector(runway, wind) { const windVector = calculateWindVector(wind, runway.heading); return { runway, windAngle: windVector.angle, headwind: Math.round(windVector.headwind), crosswind: Math.round(windVector.crosswind), }; } }