UNPKG

flight-planner

Version:

Plan and route VFR flights

223 lines (222 loc) 7.05 kB
import { normalizeICAO } from "./utils"; /** * Enumeration representing different flight rules categories. * * @enum {string} * @readonly * @property {string} VFR - Visual Flight Rules * @property {string} MVFR - Marginal Visual Flight Rules * @property {string} IFR - Instrument Flight Rules * @property {string} LIFR - Low Instrument Flight Rules */ export var FlightRules; (function (FlightRules) { FlightRules["VFR"] = "VFR"; FlightRules["MVFR"] = "MVFR"; FlightRules["IFR"] = "IFR"; FlightRules["LIFR"] = "LIFR"; })(FlightRules || (FlightRules = {})); /** * Calculate the ceiling height based on the cloud layers. * * @param clouds The cloud layers * @returns The ceiling height in feet, or undefined if the ceiling is unlimited. */ function calculateCeilingHeight(clouds) { const cloudCeilingQuantity = ['BKN', 'OVC']; const cloudCeiling = clouds.filter(cloud => cloudCeilingQuantity.includes(cloud.quantity)).sort((a, b) => (a.height ?? 0) - (b.height ?? 0)); if (cloudCeiling.length > 0) { return cloudCeiling[0].height ?? undefined; } return undefined; } /** * Calculate the flight rules based on the visibility and ceiling. * * @param metarData The METAR data * @returns The flight rules */ function calculateFlightRules(metarData) { let visibilityMeters; if (metarData.visibility !== undefined) { visibilityMeters = metarData.visibility.value; if (metarData.visibility.unit === 'sm') { visibilityMeters *= 1609.34; } } // Check for LIFR first (most restrictive) if (visibilityMeters !== undefined && visibilityMeters <= 1500 || metarData.ceiling !== undefined && metarData.ceiling <= 500) { return FlightRules.LIFR; } // Check for IFR if (visibilityMeters !== undefined && visibilityMeters <= 5000 || metarData.ceiling !== undefined && metarData.ceiling <= 1000) { return FlightRules.IFR; } // Check for MVFR if (visibilityMeters !== undefined && visibilityMeters <= 8000 || metarData.ceiling !== undefined && metarData.ceiling <= 3000) { return FlightRules.MVFR; } return FlightRules.VFR; } /** * Converts a METAR object to a MetarData object. * * @param metar The METAR object * @returns The MetarData object */ export function fromIMetar(metar) { const observationTime = new Date(); if (metar.day) { observationTime.setUTCDate(metar.day); } else { observationTime.setUTCDate(observationTime.getUTCDate() - 1); } if (metar.hour) { observationTime.setUTCHours(metar.hour); } if (metar.minute) { observationTime.setUTCMinutes(metar.minute); } observationTime.setUTCSeconds(0); observationTime.setUTCMilliseconds(0); const data = { station: normalizeICAO(metar.station), observationTime: observationTime, flightRules: 'VFR', raw: metar.message, windDirection: metar?.wind?.degrees, windDirectionMin: metar?.wind?.minVariation, windDirectionMax: metar?.wind?.maxVariation, windSpeed: metar?.wind?.speed, // TODO: check the unit windGust: metar?.wind?.gust, // TODO: check the unit temperature: metar.temperature, dewpoint: metar.dewPoint, visibility: metar.visibility ? { value: metar.visibility.value, unit: metar.visibility.unit === 'm' ? 'm' : 'sm', } : undefined, qnh: metar.altimeter ? { value: metar.altimeter.value, unit: metar.altimeter.unit === 'hPa' ? 'hPa' : 'inHg' } : undefined, ceiling: calculateCeilingHeight(metar.clouds), }; data.flightRules = calculateFlightRules(data); return data; } /** * Converts a MetarData object to a METAR string. * * @param metarData The MetarData object * @returns The METAR string */ export function formatWind(metarData) { if (!metarData.windDirection) { return 'Calm'; } let windString = `${metarData.windDirection}° ${metarData.windSpeed}kt`; if (metarData.windGust) { windString += ` gusting ${metarData.windGust}kt`; } if (metarData.windDirectionMin && metarData.windDirectionMax) { windString += ` (${metarData.windDirectionMin}° to ${metarData.windDirectionMax}°)`; } return windString; } /** * Converts a MetarData object to a METAR string. * * @param metarData The MetarData object * @returns The METAR string */ export function formatTemperature(metarData) { return `${metarData.temperature}°C`; } /** * Converts a MetarData object to a METAR string. * * @param metarData The MetarData object * @returns The METAR string */ export function formatDewpoint(metarData) { return `${metarData.dewpoint}°C`; } /** * Converts a MetarData object to a METAR string. * * @param metarData The MetarData object * @returns The METAR string */ export function formatVisibility(metarData) { if (metarData.visibility === undefined) { return '-'; } if (metarData.visibility.value >= 9999 && metarData.visibility.unit === 'm') { return '10 km+'; } else if (metarData.visibility.value >= 10 && metarData.visibility.unit === 'sm') { return '10 sm+'; } if (metarData.visibility.unit === 'm') { if (metarData.visibility.value < 1000) { return `${metarData.visibility.value} m`; } return `${(metarData.visibility.value / 1000).toFixed(1)} km`; } else { return `${metarData.visibility.value} sm`; } } /** * Converts a MetarData object to a METAR string. * * @param metarData The MetarData object * @returns The METAR string */ export function formatQNH(metarData) { if (metarData.qnh === undefined) { return '-'; } return `${metarData.qnh.value} ${metarData.qnh.unit}`; } /** * Converts a MetarData object to a METAR string. * * @param metarData The MetarData object * @returns The METAR string */ export function formatCeiling(metarData) { if (metarData.ceiling === undefined) { return '-'; } return `${metarData.ceiling} ft`; } /** * Returns a color string corresponding to the given flight rules. * * @param flightRules - The flight rules to convert to a color * @returns A string representing the color associated with the flight rules: * - 'green' for VFR (Visual Flight Rules) * - 'blue' for MVFR (Marginal Visual Flight Rules) * - 'red' for IFR (Instrument Flight Rules) * - 'purple' for LIFR (Low Instrument Flight Rules) * - 'black' for any undefined flight rules */ export function colorizeFlightRules(flightRules) { switch (flightRules) { case FlightRules.VFR: return 'green'; case FlightRules.MVFR: return 'blue'; case FlightRules.IFR: return 'red'; case FlightRules.LIFR: return 'purple'; default: return 'black'; } }