@fboes/aerofly-patterns
Version:
Landegerät - Create random custom missions for Aerofly FS 4.
160 lines (143 loc) • 4.56 kB
text/typescript
import { Vector } from "@fboes/geojson";
import { Units } from "../../data/Units.js";
import { AviationWeatherNormalizedMetar } from "./AviationWeatherApi.js";
import { AeroflyMissionConditions } from "@fboes/aerofly-custom-missions";
export class Formatter {
static getLocalDaytime(date: Date, offset: number): string {
const localSolarTime = (date.getUTCHours() + offset + 24) % 24;
if (localSolarTime < 5 || localSolarTime >= 19) {
return "night";
}
if (localSolarTime < 8) {
return "early morning";
}
if (localSolarTime < 11) {
return "morning";
}
if (localSolarTime < 13) {
return "noon";
}
if (localSolarTime < 15) {
return "afternoon";
}
if (localSolarTime < 19) {
return "late afternoon";
}
return "day";
}
/**
* Get a readable direction
* @param {number} heading
* @returns {"north"|"north-east"|"east"|"south-east"|"south"|"south-west"|"west"|"north-west"}
*/
static getDirection(heading: number): string {
const headings = ["north", "north-east", "east", "south-east", "south", "south-west", "west", "north-west"];
return headings[Math.round((heading / 360) * headings.length) % headings.length];
}
/**
* @param {number} distance in meters
* @returns {string} in nautical miles
*/
static getDistance(distance: number): string {
const nauticalMiles = distance / Units.metersPerNauticalMile;
if (nauticalMiles > 10) {
return Math.round(nauticalMiles).toLocaleString("en") + " NM";
}
return (Math.round(nauticalMiles * 10) / 10).toLocaleString("en") + " NM";
}
/**
*
* @param {Vector} vector
* @param {string} onSite as fallback for distances less than 1 NM
* @returns {string}
*/
static getVector(vector: Vector, onSite: string = "on field"): string {
if (vector.meters < Units.metersPerNauticalMile) {
return onSite;
}
return Formatter.getDistance(vector.meters) + " to the " + Formatter.getDirection(vector.bearing);
}
/**
* Get a readable direction
* @param {number} heading
* @returns {string}
*/
static getDirectionArrow(heading: number): string {
const headings = ["↑", "↗", "→", "↘", "↓", "↙", "←", "↖"];
return headings[Math.round((heading / 360) * headings.length) % headings.length];
}
/**
* Get a readable direction
* @param {number} number
* @returns {string}
*/
static getNumberString(number: number): string {
const numbers = [
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
];
return numbers[Math.round(number)] ?? String(number);
}
/**
* @param {AviationWeatherNormalizedMetar} weather
* @returns {string}
*/
static getWeatherAdjectives(weather: AviationWeatherNormalizedMetar): string {
/**
* @type {string[]}
*/
const adjectives: string[] = [];
if (weather.wspd >= 48) {
adjectives.push("stormy");
} else if (weather.wspd >= 34) {
adjectives.push("very windy");
} else if (weather.wgst && weather.wgst >= 10) {
// Gusty being more interesting as windy
adjectives.push("gusty");
} else if (weather.wspd >= 22) {
adjectives.push("windy");
}
if (weather.visib <= 1) {
adjectives.push("foggy");
} else if (weather.visib <= 3) {
adjectives.push("misty");
} else {
switch (weather.clouds[0]?.cover) {
case "OVC":
adjectives.push("overcast");
break;
case "BKN":
adjectives.push("cloudy");
break;
case "CLR":
adjectives.push("clear");
break;
}
}
return adjectives.join(", ");
}
static getWindDescription(conditions: AeroflyMissionConditions): string {
return conditions.wind.speed
? `${Math.ceil(conditions.wind.speed)} kts @ ${conditions.wind.direction.toFixed(0).padStart(3, "0")}°`
: "Calm";
}
static getCloudsDescription(conditions: AeroflyMissionConditions): string {
return conditions.clouds[0]?.cover_code !== "CLR"
? `${conditions.clouds[0]?.cover_code} @ ${conditions.clouds[0]?.base_feet.toLocaleString("en")} ft`
: conditions.clouds[0]?.cover_code;
}
static getThermalStrengthDescription(conditions: AeroflyMissionConditions): string {
return conditions.thermalStrength > 0.8 ? "High" : conditions.thermalStrength > 0.2 ? "Medium" : "Low";
}
}