@fboes/aerofly-patterns
Advanced tools
Comparing version 2.3.6 to 2.3.7
# Changelog | ||
# 2.3.7 | ||
- Updated mission file generator, adding tags | ||
- Added Data Transfer Objects for Aviation Weather API | ||
## 2.3.6 | ||
@@ -4,0 +9,0 @@ |
// @ts-check | ||
import { AirportTest } from "./lib/Airport.test.js"; | ||
import { AviationWeatherApiTest, AviationWeatherApiHelpersTest } from "./lib/AviationWeatherApi.test.js"; | ||
import { AviationWeatherApiTest } from "./lib/AviationWeatherApi.test.js"; | ||
import { DateYielderTest } from "./lib/DateYielder.test.js"; | ||
@@ -10,3 +10,2 @@ import { DegreeTest } from "./lib/Degree.test.js"; | ||
new AviationWeatherApiTest(); | ||
new AviationWeatherApiHelpersTest(); | ||
new DateYielderTest(); | ||
@@ -13,0 +12,0 @@ new DegreeTest(); |
@@ -191,2 +191,3 @@ // @ts-check | ||
description: s.description ?? "", | ||
tags: s.tags, | ||
flightSetting: "cruise", | ||
@@ -312,3 +313,3 @@ aircraft: { | ||
? pad("Calm", 13) | ||
: `${pad(s.weather?.windDirection, 3, true)}° @ ${pad(s.weather?.windSpeed, 2, true)} kts`, | ||
: `${pad(s.weather?.windDirection, 3, true)}° @ ${pad(s.weather?.windSpeed, 2, true)} kts`, | ||
clouds, | ||
@@ -315,0 +316,0 @@ pad(Math.round(s.weather?.visibility ?? 0), 7, true) + " SM", |
@@ -8,3 +8,3 @@ // @ts-check | ||
import { Formatter } from "./Formatter.js"; | ||
import { AviationWeatherApiHelpers } from "./AviationWeatherApi.js"; | ||
import { AviationWeatherNormalizedAirport } from "./AviationWeatherApi.js"; | ||
@@ -17,23 +17,15 @@ /** | ||
* | ||
* @param {import('./AviationWeatherApi.js').AviationWeatherApiAirport} airportJson | ||
* @param {import('./AviationWeatherApi.js').AviationWeatherApiAirport} airportApiData | ||
* @param {import('./Configuration.js').Configuration?} configuration | ||
*/ | ||
constructor(airportJson, configuration = null) { | ||
this.id = airportJson.icaoId; | ||
this.position = new Point(airportJson.lon, airportJson.lat, airportJson.elev); | ||
constructor(airportApiData, configuration = null) { | ||
const airportNormalized = new AviationWeatherNormalizedAirport(airportApiData); | ||
this.id = airportNormalized.icaoId; | ||
this.position = new Point(airportNormalized.lon, airportNormalized.lat, airportNormalized.elev); | ||
/** | ||
* @type {string} | ||
*/ | ||
this.name = airportJson.name | ||
.replace(/_/g, " ") | ||
.trim() | ||
.replace(/\bINTL\b/g, "INTERNATIONAL") | ||
.replace(/\bRGNL\b/g, "REGIONAL") | ||
.replace(/\bFLD\b/g, "FIELD") | ||
.replace(/(\/)/g, " $1 ") | ||
.toLowerCase() | ||
.replace(/(^|\s)[a-z]/g, (char) => { | ||
return char.toUpperCase(); | ||
}); | ||
this.name = airportNormalized.name; | ||
@@ -50,3 +42,3 @@ // Remove municipality name if already present in airport name | ||
this.runways = []; | ||
airportJson.runways.map((r) => { | ||
airportNormalized.runways.map((r) => { | ||
this.buildRunways(r, this.position, configuration).forEach((runway) => { | ||
@@ -80,10 +72,3 @@ runway && this.runways.push(runway); | ||
*/ | ||
this.magneticDeclination = 0; | ||
const magdecMatch = airportJson.magdec.match(/^(\d+)(E|W)$/); | ||
if (magdecMatch) { | ||
this.magneticDeclination = Number(magdecMatch[1]); | ||
if (magdecMatch[2] === "W") { | ||
this.magneticDeclination *= -1; | ||
} | ||
} | ||
this.magneticDeclination = airportNormalized.magdec; | ||
@@ -99,3 +84,3 @@ /** | ||
*/ | ||
this.hasTower = airportJson.tower === "T"; | ||
this.hasTower = airportNormalized.tower; | ||
@@ -105,5 +90,5 @@ /** | ||
*/ | ||
this.hasBeacon = airportJson.beacon === "B"; | ||
this.hasBeacon = airportNormalized.beacon; | ||
const lclP = AviationWeatherApiHelpers.fixFrequencies(airportJson.freqs).find((f) => { | ||
const lclP = airportNormalized.freqs.find((f) => { | ||
return f.type === "LCL/P"; | ||
@@ -187,3 +172,3 @@ }); | ||
* | ||
* @param {import('./AviationWeatherApi.js').AviationWeatherApiRunway} runwayJson | ||
* @param {import('./AviationWeatherApi.js').AviationWeatherNormalizedRunway} runwayApiData | ||
* @param {Point} airportPosition | ||
@@ -193,10 +178,7 @@ * @param {import('./Configuration.js').Configuration?} configuration | ||
*/ | ||
buildRunways(runwayJson, airportPosition, configuration) { | ||
buildRunways(runwayApiData, airportPosition, configuration) { | ||
/** | ||
* @type {[string,string]} both directions | ||
*/ | ||
const id = ["", ""]; | ||
runwayJson.id.split("/").forEach((i, index) => { | ||
id[index] = i; | ||
}); | ||
const id = runwayApiData.id; | ||
@@ -206,16 +188,10 @@ /** | ||
*/ | ||
const dimension = [0, 0]; | ||
runwayJson.dimension | ||
.split("x") | ||
.map((x) => Number(x)) | ||
.forEach((d, index) => { | ||
dimension[index] = d; | ||
}); | ||
const dimension = runwayApiData.dimension; | ||
// Helipads & Water runways get an approximate alignment | ||
if (runwayJson.alignment === "-") { | ||
runwayJson.alignment = id[0].replace(/\D/g, "") + "0"; | ||
if (runwayApiData.alignment === null) { | ||
runwayApiData.alignment = Number(id[0].replace(/\D/g, "") + "0"); | ||
} | ||
const alignmentBase = Number(runwayJson.alignment); | ||
const alignmentBase = runwayApiData.alignment; | ||
if (isNaN(alignmentBase)) { | ||
@@ -222,0 +198,0 @@ return []; |
@@ -23,3 +23,3 @@ //@ts-check | ||
magdec: "02E", | ||
rwyNum: 3, | ||
rwyNum: "3", | ||
tower: "T", | ||
@@ -84,3 +84,3 @@ beacon: "B", | ||
magdec: "15W", | ||
rwyNum: 2, | ||
rwyNum: "2", | ||
tower: "T", | ||
@@ -130,3 +130,3 @@ beacon: "B", | ||
magdec: "14E", | ||
rwyNum: 3, | ||
rwyNum: "3", | ||
tower: "T", | ||
@@ -133,0 +133,0 @@ beacon: "B", |
// @ts-check | ||
import { Vector } from "@fboes/geojson"; | ||
import { Point } from "@fboes/geojson"; | ||
import { Vector, Point } from "@fboes/geojson"; | ||
/** | ||
* @typedef {object} AviationWeatherApiCloud | ||
* @property {"CAVOK"|"CLR"|"FEW"|"SCT"|"BKN"|"OVC"} cover with {CLR: 0, FEW: 1/8, SCT: 2/8, BKN: 4/8, OVC: 1} | ||
* @property {"CAVOK"|"CLR"|"SKC"|"FEW"|"SCT"|"BKN"|"OVC"} cover with {CLR: 0, FEW: 1/8, SCT: 2/8, BKN: 4/8, OVC: 1} | ||
* @property {number?} base 1900 ft AGL | ||
@@ -45,3 +44,3 @@ * @see https://aviationweather.gov/data/api/#/Data/dataMetar | ||
/** | ||
* @typedef {object} AviationWeatherApiFrequencies | ||
* @typedef {object} AviationWeatherApiFrequency | ||
* @property {string} type "LCL/P" or "-" | ||
@@ -60,7 +59,7 @@ * @property {number} [freq] 121.4 | ||
* @property {string} magdec "02E" for East, "--" obiously for true headings | ||
* @property {number} rwyNum | ||
* @property {string} rwyNum | ||
* @property {"T"|"-"|null} tower | ||
* @property {"B"|"-"|null} beacon | ||
* @property {AviationWeatherApiRunway[]} runways | ||
* @property {AviationWeatherApiFrequencies[]|string} freqs or "LCL/P,123.9;ATIS,124.7" | ||
* @property {AviationWeatherApiFrequency[]|string} freqs or "LCL/P,123.9;ATIS,124.7" | ||
* @see https://aviationweather.gov/data/api/#/Data/dataAirport | ||
@@ -97,3 +96,3 @@ */ | ||
// hours, | ||
// bbox: AviationWeatherApiHelpers.buildBbox(position, distance).join(","), | ||
// bbox: AviationWeatherApi.buildBbox(position, distance).join(","), | ||
date: date ? date.toISOString().replace(/\.\d+(Z)/, "$1") : "", | ||
@@ -114,3 +113,3 @@ }), | ||
ids: ids.join(","), | ||
// bbox: AviationWeatherApiHelpers.buildBbox(position, distance).join(","), | ||
// bbox: AviationWeatherApi.buildBbox(position, distance).join(","), | ||
format: "json", | ||
@@ -133,3 +132,3 @@ }), | ||
format: "json", | ||
bbox: AviationWeatherApiHelpers.buildBbox(position, distance).join(","), | ||
bbox: AviationWeatherApi.buildBbox(position, distance).join(","), | ||
}), | ||
@@ -155,33 +154,5 @@ ); | ||
} | ||
} | ||
export class AviationWeatherApiHelpers { | ||
/** | ||
* | ||
* @param {AviationWeatherApiFrequencies[]|string} freq | ||
* @returns {AviationWeatherApiFrequencies[]} | ||
*/ | ||
static fixFrequencies(freq) { | ||
if (typeof freq !== "string") { | ||
return freq; | ||
} | ||
return freq.split(";").map( | ||
/** | ||
* | ||
* @param {string} f | ||
* @returns {AviationWeatherApiFrequencies} | ||
*/ | ||
(f) => { | ||
const parts = f.split(","); | ||
return { | ||
type: parts[0], | ||
freq: parts[1] ? Number(parts[1]) : undefined, | ||
}; | ||
}, | ||
); | ||
} | ||
/** | ||
* | ||
* @param {Point} position | ||
@@ -197,1 +168,244 @@ * @param {number} [distance] in meters | ||
} | ||
export class AviationWeatherNormalizedAirport { | ||
/** | ||
* @param {AviationWeatherApiAirport} apiData | ||
*/ | ||
constructor({ icaoId, name, type, lat, lon, elev, magdec, rwyNum, tower, beacon, runways, freqs }) { | ||
/** | ||
* @type {string} | ||
*/ | ||
this.icaoId = icaoId; | ||
/** | ||
* @type {string} | ||
*/ | ||
this.name = name | ||
.replace(/_/g, " ") | ||
.trim() | ||
.replace(/\bINTL\b/g, "INTERNATIONAL") | ||
.replace(/\bRGNL\b/g, "REGIONAL") | ||
.replace(/\bFLD\b/g, "FIELD") | ||
.replace(/(\/)/g, " $1 ") | ||
.toLowerCase() | ||
.replace(/(^|\s)[a-z]/g, (char) => { | ||
return char.toUpperCase(); | ||
}); | ||
/** | ||
* @type {"ARP"|"HEL"} | ||
*/ | ||
this.type = type; | ||
/** | ||
* @type {number} | ||
*/ | ||
this.lat = lat; | ||
/** | ||
* @type {number} | ||
*/ | ||
this.lon = lon; | ||
/** | ||
* @type {number} in meters MSL | ||
*/ | ||
this.elev = elev; | ||
/** | ||
* @type {number} with "+" to the east and "-" to the west. Substracted to a true heading this will give the magnetic heading. | ||
*/ | ||
this.magdec = 0; | ||
const magdecMatch = magdec.match(/^(\d+)(E|W)$/); | ||
if (magdecMatch) { | ||
this.magdec = Number(magdecMatch[1]); | ||
if (magdecMatch[2] === "W") { | ||
this.magdec *= -1; | ||
} | ||
} | ||
/** | ||
* @type {number} | ||
*/ | ||
this.rwyNum = Number(rwyNum); | ||
/** | ||
* @type {boolean} | ||
*/ | ||
this.tower = tower === "T"; | ||
/** | ||
* @type {boolean} | ||
*/ | ||
this.beacon = beacon === "B"; | ||
/** | ||
* @type {AviationWeatherNormalizedRunway[]} | ||
*/ | ||
this.runways = runways.map((r) => { | ||
return new AviationWeatherNormalizedRunway(r); | ||
}); | ||
/** | ||
* @type {AviationWeatherApiFrequency[]} | ||
*/ | ||
this.freqs = | ||
typeof freqs !== "string" | ||
? freqs | ||
: freqs.split(";").map( | ||
/** | ||
* @param {string} f | ||
* @returns {AviationWeatherApiFrequency} | ||
*/ | ||
(f) => { | ||
const parts = f.split(","); | ||
return { | ||
type: parts[0], | ||
freq: parts[1] ? Number(parts[1]) : undefined, | ||
}; | ||
}, | ||
); | ||
} | ||
} | ||
export class AviationWeatherNormalizedRunway { | ||
/** | ||
* | ||
* @param {AviationWeatherApiRunway} apiData | ||
*/ | ||
constructor({ id, dimension, surface, alignment }) { | ||
/** | ||
* @type {[string,string]} both directions | ||
*/ | ||
this.id = ["", ""]; | ||
id.split("/").forEach((i, index) => { | ||
this.id[index] = i; | ||
}); | ||
/** | ||
* @type {[number,number]} length, width in ft | ||
*/ | ||
this.dimension = [0, 0]; | ||
dimension | ||
.split("x") | ||
.map((x) => Number(x)) | ||
.forEach((d, index) => { | ||
this.dimension[index] = d; | ||
}); | ||
/** | ||
* @type {AviationWeatherApiRunwaySurface} | ||
*/ | ||
this.surface = surface; | ||
/** | ||
* @type {number?} | ||
*/ | ||
this.alignment = alignment !== "-" ? Number(alignment) : null; | ||
} | ||
} | ||
export class AviationWeatherNormalizedMetar { | ||
/** | ||
* | ||
* @param {AviationWeatherApiMetar} apiData | ||
*/ | ||
constructor({ icaoId, reportTime, temp, dewp, wdir, wspd, wgst, visib, altim, lat, lon, elev, clouds }) { | ||
/** | ||
* @type {string} | ||
*/ | ||
this.icaoId = icaoId; | ||
/** | ||
* @type {Date} | ||
*/ | ||
this.reportTime = new Date(Date.parse(reportTime + " GMT")); | ||
/** | ||
* @type {number} in °C | ||
*/ | ||
this.temp = temp; | ||
/** | ||
* @type {number} in °C | ||
*/ | ||
this.dewp = dewp; | ||
/** | ||
* @type {number?} in °, null on VRB | ||
*/ | ||
this.wdir = wdir !== "VRB" ? wdir : null; | ||
/** | ||
* @type {number} in kts | ||
*/ | ||
this.wspd = wspd; | ||
/** | ||
* @type {number?} in kts | ||
*/ | ||
this.wgst = wgst; | ||
/** | ||
* @type {number} in SM, 99 on any distance being open-ended | ||
*/ | ||
this.visib = typeof visib === "string" ? 99 : visib; | ||
/** | ||
* @type {number} in hPa | ||
*/ | ||
this.altim = altim; | ||
/** | ||
* @type {number} | ||
*/ | ||
this.lat = lat; | ||
/** | ||
* @type {number} | ||
*/ | ||
this.lon = lon; | ||
/** | ||
* @type {number} meters MSL | ||
*/ | ||
this.elev = elev; | ||
/** | ||
* @type {AviationWeatherNormalizedCloud[]} | ||
*/ | ||
this.clouds = clouds.map((c) => { | ||
return new AviationWeatherNormalizedCloud(c); | ||
}); | ||
} | ||
} | ||
export class AviationWeatherNormalizedCloud { | ||
/** | ||
* @param {AviationWeatherApiCloud} apiData | ||
*/ | ||
constructor({ cover, base }) { | ||
/** | ||
* @type {"CLR"|"FEW"|"SCT"|"BKN"|"OVC"} | ||
*/ | ||
this.cover = cover === "CAVOK" || cover === "SKC" ? "CLR" : cover; | ||
const coverOctas = { | ||
CLR: 0, | ||
FEW: 1, | ||
SCT: 2, | ||
BKN: 4, | ||
OVC: 8, | ||
}; | ||
/** | ||
* @type {number} 0..8 | ||
*/ | ||
this.coverOctas = coverOctas[this.cover] ?? 0; | ||
/** | ||
* @type {number?} in feet AGL | ||
*/ | ||
this.base = base; | ||
} | ||
} |
// @ts-check | ||
import { strict as assert } from "node:assert"; | ||
import { AviationWeatherApi, AviationWeatherApiHelpers } from "./AviationWeatherApi.js"; | ||
import { AviationWeatherApi, AviationWeatherNormalizedAirport } from "./AviationWeatherApi.js"; | ||
@@ -21,15 +21,30 @@ export class AviationWeatherApiTest { | ||
airports.forEach((airport) => { | ||
assert.ok(airport.icaoId, "airport.icaoId"); | ||
assert.strictEqual(typeof airport.icaoId, "string", "airport.icaoId"); | ||
assert.ok(icaoCodes.indexOf(airport.icaoId) > -1); | ||
assert.ok(airport.name, "airport.name"); | ||
assert.ok(airport.type, "airport.type"); | ||
assert.ok(airport.lat, "airport.lat"); | ||
assert.ok(airport.lon, "airport.lon"); | ||
assert.ok(airport.elev, "airport.elev"); | ||
assert.ok(airport.magdec, "airport.magdec"); | ||
assert.ok(airport.rwyNum, "airport.rwyNum"); | ||
assert.ok(airport.tower, "airport.tower"); | ||
assert.ok(airport.beacon, "airport.beacon"); | ||
assert.strictEqual(typeof airport.name, "string", "airport.name"); | ||
assert.strictEqual(typeof airport.type, "string", "airport.type"); | ||
assert.strictEqual(typeof airport.lat, "number", "airport.lat"); | ||
assert.strictEqual(typeof airport.lon, "number", "airport.lon"); | ||
assert.strictEqual(typeof airport.elev, "number", "airport.elev"); | ||
assert.strictEqual(typeof airport.magdec, "string", "airport.magdec"); | ||
assert.strictEqual(typeof airport.rwyNum, "string", "airport.rwyNum"); | ||
assert.strictEqual(typeof airport.tower, "string", "airport.tower"); | ||
assert.strictEqual(typeof airport.beacon, "string", "airport.beacon"); | ||
assert.ok(Array.isArray(airport.runways), "airport.runways"); | ||
assert.ok(Array.isArray(airport.freqs) || typeof airport.freqs === "string", "airport.freqs"); | ||
const airportNormalized = new AviationWeatherNormalizedAirport(airport); | ||
assert.strictEqual(typeof airportNormalized.icaoId, "string", "airportNormalized.icaoId"); | ||
assert.ok(icaoCodes.indexOf(airportNormalized.icaoId) > -1); | ||
assert.strictEqual(typeof airportNormalized.name, "string", "airportNormalized.name"); | ||
assert.strictEqual(typeof airportNormalized.type, "string", "airportNormalized.type"); | ||
assert.strictEqual(typeof airportNormalized.lat, "number", "airportNormalized.lat"); | ||
assert.strictEqual(typeof airportNormalized.lon, "number", "airportNormalized.lon"); | ||
assert.strictEqual(typeof airportNormalized.elev, "number", "airportNormalized.elev"); | ||
assert.strictEqual(typeof airportNormalized.magdec, "number", "airportNormalized.magdec"); | ||
assert.strictEqual(typeof airportNormalized.rwyNum, "number", "airportNormalized.rwyNum"); | ||
assert.strictEqual(typeof airportNormalized.tower, "boolean", "airportNormalized.tower"); | ||
assert.strictEqual(typeof airportNormalized.beacon, "boolean", "airportNormalized.beacon"); | ||
assert.ok(Array.isArray(airportNormalized.runways), "airportNormalized.runways"); | ||
assert.ok(Array.isArray(airportNormalized.freqs), "airportNormalized.freqs"); | ||
}); | ||
@@ -45,41 +60,10 @@ | ||
metars.forEach((metar) => { | ||
assert.strictEqual(typeof metar.lat, "number", "metar.lat"); | ||
assert.strictEqual(typeof metar.lon, "number", "metar.lon"); | ||
assert.strictEqual(typeof metar.elev, "number", "metar.elev"); | ||
}); | ||
console.log(`✅ ${this.constructor.name}.fetchMetar successful`); | ||
} | ||
} | ||
export class AviationWeatherApiHelpersTest { | ||
constructor() { | ||
this.fixFrequencies(); | ||
} | ||
fixFrequencies() { | ||
{ | ||
const expected = AviationWeatherApiHelpers.fixFrequencies("LCL/P,123.9;ATIS,124.7"); | ||
assert.strictEqual(expected.length, 2); | ||
assert.strictEqual(expected[0].type, "LCL/P"); | ||
assert.strictEqual(expected[0].freq, 123.9); | ||
} | ||
{ | ||
const expected = AviationWeatherApiHelpers.fixFrequencies("-"); | ||
assert.strictEqual(expected.length, 1); | ||
assert.strictEqual(expected[0].type, "-"); | ||
assert.strictEqual(expected[0].freq, undefined); | ||
} | ||
{ | ||
const expected = AviationWeatherApiHelpers.fixFrequencies([ | ||
{ type: "LCL/P", freq: 123.9 }, | ||
{ type: "ATIS", freq: 124.7 }, | ||
]); | ||
assert.strictEqual(expected.length, 2); | ||
assert.strictEqual(expected[0].type, "LCL/P"); | ||
assert.strictEqual(expected[0].freq, 123.9); | ||
} | ||
console.log(`✅ ${this.constructor.name}.fixFrequencies successful`); | ||
} | ||
} |
@@ -6,2 +6,3 @@ //@ts-check | ||
import { ScenarioWeather, ScenarioWeatherCloud } from "./Scenario.js"; | ||
import { AviationWeatherNormalizedCloud } from "./AviationWeatherApi.js"; | ||
@@ -77,3 +78,3 @@ export class FormatterTest { | ||
w.clouds[0] = new ScenarioWeatherCloud("OVC", 1000); | ||
w.clouds[0] = new ScenarioWeatherCloud(new AviationWeatherNormalizedCloud({ cover: "OVC", base: 1000 })); | ||
assert.strictEqual("foggy", Formatter.getWeatherAdjectives(w)); | ||
@@ -80,0 +81,0 @@ |
@@ -6,3 +6,3 @@ // @ts-check | ||
import { Configuration } from "./Configuration.js"; | ||
import { AviationWeatherApi } from "./AviationWeatherApi.js"; | ||
import { AviationWeatherApi, AviationWeatherNormalizedMetar } from "./AviationWeatherApi.js"; | ||
import { Formatter } from "./Formatter.js"; | ||
@@ -226,2 +226,29 @@ import { AeroflyAircraftFinder } from "../data/AeroflyAircraft.js"; | ||
/** | ||
* @returns {string[]} | ||
*/ | ||
get tags() { | ||
const tags = ["approach", "pattern", "practice"]; | ||
if (this.activeRunwayCrosswindComponent > 4.5) { | ||
tags.push("crosswind"); | ||
} | ||
if (this.weather?.windSpeed && this.weather.windSpeed >= 22) { | ||
tags.push("windy"); | ||
} | ||
if (this.weather?.visibility && this.weather.visibility <= 3) { | ||
tags.push("low_visibility"); | ||
} | ||
if (Formatter.getLocalDaytime(this.date, this.airport.nauticalTimezone) === "night") { | ||
tags.push("night"); | ||
} | ||
if (this.activeRunway?.ilsFrequency) { | ||
tags.push("instruments"); | ||
} | ||
if(this.activeRunway?.dimension[0] && this.activeRunway.dimension[0] <= 2000) { | ||
tags.push("short_runway"); | ||
} | ||
return tags; | ||
} | ||
/** | ||
* @returns {string?} | ||
@@ -410,9 +437,10 @@ */ | ||
/** | ||
* @param {import('./AviationWeatherApi.js').AviationWeatherApiMetar} weatherJson | ||
* @param {import('./AviationWeatherApi.js').AviationWeatherApiMetar} weatherApiData | ||
*/ | ||
constructor(weatherJson) { | ||
constructor(weatherApiData) { | ||
const weather = new AviationWeatherNormalizedMetar(weatherApiData); | ||
/** | ||
* @type {number} in degree | ||
*/ | ||
this.windDirection = weatherJson.wdir === "VRB" ? 0 : Degree(weatherJson.wdir); | ||
this.windDirection = weather.wdir ?? 0; | ||
@@ -422,3 +450,3 @@ /** | ||
*/ | ||
this.windSpeed = weatherJson.wspd; | ||
this.windSpeed = weather.wspd; | ||
@@ -428,3 +456,3 @@ /** | ||
*/ | ||
this.windGusts = weatherJson.wgst ?? 0; | ||
this.windGusts = weather.wgst ?? 0; | ||
@@ -434,6 +462,6 @@ /** | ||
*/ | ||
this.visibility = typeof weatherJson.visib === "string" ? 15 : weatherJson.visib; | ||
this.visibility = Math.min(15, weather.visib); | ||
this.clouds = weatherJson.clouds.map((c) => { | ||
return new ScenarioWeatherCloud(c.cover, c.base); | ||
this.clouds = weather.clouds.map((c) => { | ||
return new ScenarioWeatherCloud(c); | ||
}); | ||
@@ -444,3 +472,3 @@ | ||
*/ | ||
this.temperature = weatherJson.temp; | ||
this.temperature = weather.temp; | ||
} | ||
@@ -458,60 +486,29 @@ | ||
/** | ||
* @type {number} 0..1 | ||
* @param {import('./AviationWeatherApi.js').AviationWeatherNormalizedCloud} cloud | ||
*/ | ||
#cloudCover = 0; | ||
/** | ||
* @type {"CLR"|"FEW"|"SCT"|"BKN"|"OVC"} | ||
*/ | ||
#cloudCoverCode = "CLR"; | ||
/** | ||
* @param {"CAVOK"|"CLR"|"FEW"|"SCT"|"BKN"|"OVC"} cover | ||
* @param {number?} base | ||
*/ | ||
constructor(cover, base) { | ||
this.cloudCoverCode = cover; | ||
constructor(cloud) { | ||
/** | ||
* @type {number} in ft | ||
* @type {"CLR"|"FEW"|"SCT"|"BKN"|"OVC"} | ||
*/ | ||
this.cloudBase = base ?? 0; | ||
} | ||
/** | ||
* @returns {number} 0..1 | ||
*/ | ||
get cloudCover() { | ||
return this.#cloudCover; | ||
} | ||
this.cloudCoverCode = cloud.cover; | ||
/** | ||
* | ||
* @param {"CAVOK"|"CLR"|"FEW"|"SCT"|"BKN"|"OVC"} cloudCoverCode | ||
*/ | ||
set cloudCoverCode(cloudCoverCode) { | ||
if (cloudCoverCode === "CAVOK") { | ||
cloudCoverCode = "CLR"; | ||
} | ||
this.#cloudCoverCode = cloudCoverCode; | ||
/** | ||
* @type {{[key:string]:[number,number]}} | ||
*/ | ||
const cover = { | ||
CLR: [0, 0], // 0 | ||
FEW: [1 / 8, 1 / 8], // 1/8 | ||
SCT: [2 / 8, 2 / 8], // 2/8 | ||
BKN: [4 / 8, 3 / 8], // 4/8 | ||
OVC: [7 / 8, 1 / 8], // 1 | ||
FEW: [1 / 8, 1 / 8], // 1/8 .. 2/8 | ||
SCT: [2 / 8, 2 / 8], // 2/8 .. 4/8 | ||
BKN: [4 / 8, 3 / 8], // 4/8 .. 7/8 | ||
OVC: [7 / 8, 1 / 8], // 7/8 .. 1 | ||
}; | ||
const actualCover = cover[cloudCoverCode] ? cover[cloudCoverCode] : cover.CLR; | ||
const actualCover = cover[this.cloudCoverCode] ? cover[this.cloudCoverCode] : cover.CLR; | ||
this.#cloudCover = actualCover[0] + Math.random() * actualCover[1]; | ||
} | ||
/** | ||
* @type {number} 0..1 | ||
*/ | ||
this.cloudCover = actualCover[0] + Math.random() * actualCover[1]; | ||
/** | ||
* @returns {"CLR"|"FEW"|"SCT"|"BKN"|"OVC"} | ||
*/ | ||
get cloudCoverCode() { | ||
return this.#cloudCoverCode; | ||
/** | ||
* @type {number} in ft | ||
*/ | ||
this.cloudBase = cloud.base ?? 0; | ||
} | ||
} |
{ | ||
"name": "@fboes/aerofly-patterns", | ||
"version": "2.3.6", | ||
"version": "2.3.7", | ||
"description": "Landegerät - Create landing pattern lessons for Aerofly FS 4.", | ||
@@ -30,3 +30,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@fboes/aerofly-custom-missions": "^1.0.0", | ||
"@fboes/aerofly-custom-missions": "^1.1.0", | ||
"@fboes/geojson": "^1.4.0" | ||
@@ -33,0 +33,0 @@ }, |
95818
2722