@fboes/aerofly-data 1.9.1 → 1.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +2 -1
- package/data/aircraft-liveries-missing.md +107 -0
- package/data/aircraft-liveries.json +758 -379
- package/data/airport-coordinates-object.json +71 -71
- package/data/airport-coordinates.json +34 -34
- package/data/airports.geojson +11210 -11210
- package/docs/update-liveries.md +13 -0
- package/get-aircraft.js +69 -22
- package/get-airports.js +16 -18
- package/package.json +1 -1
- package/src/aircraft-functions.js +40 -125
- package/src/airport-functions.js +8 -8
- package/src/livery-to-icao.json +301 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Update livery-to-ICAO mappings
|
|
2
|
+
|
|
3
|
+
Review `data/aircraft-liveries-missing.md` and update `src/livery-to-icao.json` with mappings for entries that are clearly official airlines.
|
|
4
|
+
|
|
5
|
+
- Use each livery name, converted to lowercase, as the JSON key.
|
|
6
|
+
- Use the airline's official three-letter ICAO designator, not its IATA code.
|
|
7
|
+
- Add a mapping only when both the airline identity and its ICAO designator are unambiguous. Check reliable sources when needed.
|
|
8
|
+
- For clear airline-branded variants (such as cargo, anniversary, or special liveries), use the airline's ICAO designator.
|
|
9
|
+
- Leave ambiguous names, fictional liveries, military/government liveries, and other non-airline designs unmapped. Do not guess.
|
|
10
|
+
- Preserve all existing mappings; do not overwrite a mapping with a different code without confirming and explaining the correction.
|
|
11
|
+
- Keep the JSON valid and do not modify the missing-livery list.
|
|
12
|
+
|
|
13
|
+
After editing, verify that the JSON parses, all keys are lowercase, no duplicate keys were introduced, and the aircraft-functions module still loads.
|
package/get-aircraft.js
CHANGED
|
@@ -2,40 +2,73 @@
|
|
|
2
2
|
|
|
3
3
|
//@ts-check
|
|
4
4
|
|
|
5
|
-
import * as fs from "node:fs";
|
|
5
|
+
import * as fs from "node:fs/promises";
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import {
|
|
8
8
|
getAeroflyAircraft,
|
|
9
9
|
getSelectLiveryOptgroupOptions,
|
|
10
10
|
getSelectOptgroupOptions,
|
|
11
11
|
getSelectOptions,
|
|
12
|
+
MISSING_LIVERIES,
|
|
12
13
|
} from "./src/aircraft-functions.js";
|
|
13
14
|
|
|
14
15
|
const inputDirectory = process.argv[2] ?? ".";
|
|
15
16
|
const aeroflyAircraft = getAeroflyAircraft(inputDirectory);
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* @type {Promise<void>[]}
|
|
20
|
+
*/
|
|
21
|
+
const promises = [];
|
|
22
|
+
|
|
17
23
|
process.stdout
|
|
18
24
|
.write(`Found \x1b[92m${aeroflyAircraft.length}\x1b[0m Aerofly FS Aircraft with \x1b[92m${aeroflyAircraft.reduce((sum, aircraft) => sum + aircraft.liveries.length, 0)}\x1b[0m liveries
|
|
19
25
|
`);
|
|
20
26
|
|
|
21
27
|
// Ensure the output directory exists
|
|
22
28
|
const outputDirectory = path.join("data");
|
|
23
|
-
await fs.
|
|
29
|
+
await fs.mkdir(outputDirectory, { recursive: true });
|
|
24
30
|
|
|
25
31
|
// Write the full output (with liveries) to aircraft-liveries.json
|
|
26
32
|
const outputFilePathWithLiveries = path.join(outputDirectory, "aircraft-liveries.json");
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
promises.push(
|
|
34
|
+
fs.writeFile(outputFilePathWithLiveries, JSON.stringify(aeroflyAircraft, null, 2), "utf-8").then(() => {
|
|
35
|
+
process.stdout.write(
|
|
36
|
+
`Full aircraft data (with liveries) written to \x1b[92m${outputFilePathWithLiveries}\x1b[0m\n`,
|
|
37
|
+
);
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Write missing livery file
|
|
42
|
+
const outputMissingLiveries = path.join(outputDirectory, "aircraft-liveries-missing.md");
|
|
43
|
+
/**
|
|
44
|
+
* @type {string[]}
|
|
45
|
+
*/
|
|
46
|
+
const missingLiveries = [];
|
|
47
|
+
MISSING_LIVERIES.forEach((m) => {
|
|
48
|
+
missingLiveries.push(`- ${m}`);
|
|
49
|
+
});
|
|
50
|
+
const outputMissingLiveriesContent =
|
|
51
|
+
"# Aerofly FS: Liveries without ICAO code match\n\n" + missingLiveries.sort().join("\n") + "\n";
|
|
52
|
+
promises.push(
|
|
53
|
+
fs.writeFile(outputMissingLiveries, outputMissingLiveriesContent, "utf-8").then(() => {
|
|
54
|
+
process.stdout.write(`Missing livery file written to \x1b[92m${outputMissingLiveries}\x1b[0m\n`);
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
29
57
|
|
|
30
58
|
// Write the abbreviated output (without liveries) to aircraft.json
|
|
31
59
|
const outputFilePathWithoutLiveries = path.join(outputDirectory, "aircraft.json");
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
60
|
+
promises.push(
|
|
61
|
+
fs
|
|
62
|
+
.writeFile(
|
|
63
|
+
outputFilePathWithoutLiveries,
|
|
64
|
+
JSON.stringify(aeroflyAircraft, (key, value) => (key === "liveries" ? undefined : value), 2),
|
|
65
|
+
"utf-8",
|
|
66
|
+
)
|
|
67
|
+
.then(() => {
|
|
68
|
+
process.stdout.write(
|
|
69
|
+
`Abbreviated aircraft data (without liveries) written to \x1b[92m${outputFilePathWithoutLiveries}\x1b[0m\n`,
|
|
70
|
+
);
|
|
71
|
+
}),
|
|
39
72
|
);
|
|
40
73
|
|
|
41
74
|
// Sort aeroflyAircraft by nameFull
|
|
@@ -82,29 +115,43 @@ const summaryContent =
|
|
|
82
115
|
- Total Liveries: **${numberFormat(sortedAircraft.reduce((sum, aircraft) => sum + aircraft.liveries.length, 0))}**
|
|
83
116
|
`;
|
|
84
117
|
|
|
85
|
-
|
|
86
|
-
|
|
118
|
+
promises.push(
|
|
119
|
+
fs.writeFile(summaryFilePath, summaryContent, "utf-8").then(() => {
|
|
120
|
+
process.stdout.write(`Summary written to \x1b[92m${summaryFilePath}\x1b[0m\n`);
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
87
123
|
|
|
88
124
|
// Write HTML <select> options to aircraft-select.html
|
|
89
125
|
const selectFilePath = path.join(outputDirectory, "aircraft-select.html");
|
|
90
126
|
let selectContent = getSelectOptions(sortedAircraft);
|
|
91
|
-
|
|
92
|
-
|
|
127
|
+
promises.push(
|
|
128
|
+
fs.writeFile(selectFilePath, selectContent, "utf-8").then(() => {
|
|
129
|
+
process.stdout.write(`HTML <select> options written to \x1b[92m${selectFilePath}\x1b[0m\n`);
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
93
132
|
|
|
94
133
|
// Write HTML <select> with <optgroup> to aircraft-select-optgroup.html
|
|
95
134
|
const selectOptgroupFilePath = path.join(outputDirectory, "aircraft-select-optgroup.html");
|
|
96
135
|
let selectOptgroupContent = getSelectOptgroupOptions(sortedAircraft);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
136
|
+
promises.push(
|
|
137
|
+
fs.writeFile(selectOptgroupFilePath, selectOptgroupContent, "utf-8").then(() => {
|
|
138
|
+
process.stdout.write(
|
|
139
|
+
`HTML aircraft <select> with category <optgroup> options written to \x1b[92m${selectOptgroupFilePath}\x1b[0m\n`,
|
|
140
|
+
);
|
|
141
|
+
}),
|
|
100
142
|
);
|
|
101
143
|
|
|
102
144
|
// Write HTML <select> with <optgroup> to aircraft-select-optgroup.html
|
|
103
145
|
const selectOptgroupLiveryFilePath = path.join(outputDirectory, "aircraft-livery-select-optgroup.html");
|
|
104
146
|
let selectOptgroupLiveryContent = getSelectLiveryOptgroupOptions(sortedAircraft);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
147
|
+
promises.push(
|
|
148
|
+
fs.writeFile(selectOptgroupLiveryFilePath, selectOptgroupLiveryContent, "utf-8").then(() => {
|
|
149
|
+
process.stdout.write(
|
|
150
|
+
`HTML livery <select> with aircraft <optgroup> options written to \x1b[92m${selectOptgroupLiveryFilePath}\x1b[0m\n`,
|
|
151
|
+
);
|
|
152
|
+
}),
|
|
108
153
|
);
|
|
109
154
|
|
|
110
|
-
|
|
155
|
+
Promise.all(promises).then(() => {
|
|
156
|
+
process.stdout.write(`\nAll aircraft files written to \x1b[92m${path.resolve(outputDirectory)}\x1b[0m\n`);
|
|
157
|
+
});
|
package/get-airports.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//@ts-check
|
|
4
4
|
|
|
5
5
|
import GeoJSON from "@fboes/geojson";
|
|
6
|
-
import * as fs from "node:fs";
|
|
6
|
+
import * as fs from "node:fs/promises";
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { parse } from "csv-parse/sync";
|
|
9
9
|
import {
|
|
@@ -17,9 +17,7 @@ const inputDirectory = process.argv[2] ?? ".";
|
|
|
17
17
|
|
|
18
18
|
// Ensure the output directory exists
|
|
19
19
|
const outputDirectory = path.join("data");
|
|
20
|
-
|
|
21
|
-
fs.mkdirSync(outputDirectory, { recursive: true });
|
|
22
|
-
}
|
|
20
|
+
await fs.mkdir(outputDirectory, { recursive: true });
|
|
23
21
|
|
|
24
22
|
const aeroflyGeoJson = new GeoJSON.FeatureCollection();
|
|
25
23
|
const aeroflyAirports = new Map([
|
|
@@ -30,7 +28,7 @@ const aeroflyAirportsLength = aeroflyAirports.size;
|
|
|
30
28
|
process.stdout.write(`Found \x1b[92m${aeroflyAirports.size}\x1b[0m Aerofly FS Airports
|
|
31
29
|
`);
|
|
32
30
|
|
|
33
|
-
const airportsSource = fs.
|
|
31
|
+
const airportsSource = await fs.readFile(`tmp/airports.csv`);
|
|
34
32
|
|
|
35
33
|
/** @type {string[][]} with a single CSV line from airports.csv */
|
|
36
34
|
let airportsRecords = parse(airportsSource, { bom: true });
|
|
@@ -45,10 +43,10 @@ const icaoCodes = [];
|
|
|
45
43
|
|
|
46
44
|
/**
|
|
47
45
|
* @type {{
|
|
48
|
-
* code:
|
|
49
|
-
* name:
|
|
50
|
-
* lat:
|
|
51
|
-
* lon:
|
|
46
|
+
* code: string,
|
|
47
|
+
* name: string,
|
|
48
|
+
* lat: number,
|
|
49
|
+
* lon: number
|
|
52
50
|
* }[]}
|
|
53
51
|
*/
|
|
54
52
|
const icaoCoordinatesObject = [];
|
|
@@ -58,14 +56,14 @@ const icaoCoordinatesObject = [];
|
|
|
58
56
|
* @param {string[]} searchWords
|
|
59
57
|
* @returns {[number, string]|[undefined, undefined]} Returns the length of the airport and the code if found, otherwise undefined.
|
|
60
58
|
*/
|
|
61
|
-
|
|
59
|
+
function getAeroflyAirport(searchWords) {
|
|
62
60
|
for (const word of searchWords) {
|
|
63
61
|
if (aeroflyAirports.has(word)) {
|
|
64
62
|
return [aeroflyAirports.get(word) || 0, word];
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
65
|
return [undefined, undefined];
|
|
68
|
-
}
|
|
66
|
+
}
|
|
69
67
|
|
|
70
68
|
/**
|
|
71
69
|
* @param {string} aeroflyCode
|
|
@@ -73,7 +71,7 @@ const getAeroflyAirport = (searchWords) => {
|
|
|
73
71
|
* @param {string[]} airportsRecord
|
|
74
72
|
* @param {number} length
|
|
75
73
|
*/
|
|
76
|
-
|
|
74
|
+
function addRecord(aeroflyCode, bestCode, airportsRecord, length) {
|
|
77
75
|
// Remove airport from list of Aerofly FS4 Airports
|
|
78
76
|
aeroflyAirports.delete(aeroflyCode);
|
|
79
77
|
|
|
@@ -121,7 +119,7 @@ const addRecord = (aeroflyCode, bestCode, airportsRecord, length) => {
|
|
|
121
119
|
}
|
|
122
120
|
|
|
123
121
|
aeroflyGeoJson.addFeature(feature);
|
|
124
|
-
}
|
|
122
|
+
}
|
|
125
123
|
|
|
126
124
|
// -----------------------------------------------------------------------------
|
|
127
125
|
// PARSING
|
|
@@ -187,12 +185,12 @@ icaoCoordinatesObject.sort((a, b) => {
|
|
|
187
185
|
|
|
188
186
|
// Write the GeoJSON data to a file
|
|
189
187
|
const outputFilePath = path.join(outputDirectory, "airports.geojson");
|
|
190
|
-
fs.
|
|
188
|
+
await fs.writeFile(outputFilePath, JSON.stringify(aeroflyGeoJson, null, 2), "utf-8");
|
|
191
189
|
process.stdout.write(`GeoJSON data written to \x1b[92m${outputFilePath}\x1b[0m\n`);
|
|
192
190
|
|
|
193
191
|
// Write the ICAO codes to airport-list.json
|
|
194
192
|
const icaoListFilePath = path.join(outputDirectory, "airport-list.json");
|
|
195
|
-
fs.
|
|
193
|
+
await fs.writeFile(icaoListFilePath, JSON.stringify(icaoCodes.sort(), null, 2) + "\n", "utf-8");
|
|
196
194
|
process.stdout.write(`ICAO code list written to \x1b[92m${icaoListFilePath}\x1b[0m\n`);
|
|
197
195
|
|
|
198
196
|
// Write the ICAO codes with airport names and geocoordinates to airport-coordinates.json
|
|
@@ -200,12 +198,12 @@ const icaoCoordinates = icaoCoordinatesObject.map((feature) => {
|
|
|
200
198
|
return [feature.code, feature.name, feature.lat, feature.lon];
|
|
201
199
|
});
|
|
202
200
|
const icaoCoordinatesFilePath = path.join(outputDirectory, "airport-coordinates.json");
|
|
203
|
-
fs.
|
|
201
|
+
await fs.writeFile(icaoCoordinatesFilePath, JSON.stringify(icaoCoordinates, null, 2) + "\n", "utf-8");
|
|
204
202
|
process.stdout.write(`ICAO coordinates list written to \x1b[92m${icaoCoordinatesFilePath}\x1b[0m\n`);
|
|
205
203
|
|
|
206
204
|
// Write the ICAO codes with airport names and geocoordinates to airport-coordinates-object.json
|
|
207
205
|
const icaoCoordinatesObjectFilePath = path.join(outputDirectory, "airport-coordinates-object.json");
|
|
208
|
-
fs.
|
|
206
|
+
await fs.writeFile(icaoCoordinatesObjectFilePath, JSON.stringify(icaoCoordinatesObject, null, 2) + "\n", "utf-8");
|
|
209
207
|
process.stdout.write(`ICAO coordinates object list written to \x1b[92m${icaoCoordinatesObjectFilePath}\x1b[0m\n`);
|
|
210
208
|
|
|
211
209
|
// Write the missing codes to airports-unmatched.md
|
|
@@ -216,7 +214,7 @@ const unmatchedCodes = [...aeroflyAirports]
|
|
|
216
214
|
})
|
|
217
215
|
.sort()
|
|
218
216
|
.join("\n");
|
|
219
|
-
fs.
|
|
217
|
+
await fs.writeFile(
|
|
220
218
|
unmatchedFilePath,
|
|
221
219
|
`\
|
|
222
220
|
# Aerofly FS4 Unmatched Airports
|
package/package.json
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @type {Record<string, string>}
|
|
8
|
+
*/
|
|
9
|
+
const LIVERY_TO_ICAO = JSON.parse(fs.readFileSync(new URL("./livery-to-icao.json", import.meta.url), "utf8"));
|
|
10
|
+
|
|
6
11
|
/**
|
|
7
12
|
* @typedef AeroflyAircraftParsed
|
|
8
13
|
* @type {{
|
|
@@ -37,25 +42,31 @@ import * as path from "path";
|
|
|
37
42
|
* }}
|
|
38
43
|
*/
|
|
39
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Names of liveries which do not have an ICAO code
|
|
47
|
+
* @type {Set<string>}
|
|
48
|
+
*/
|
|
49
|
+
export const MISSING_LIVERIES = new Set();
|
|
50
|
+
|
|
40
51
|
/**
|
|
41
52
|
* Get all potential aircraft directories in the given directory.
|
|
42
53
|
* @param {string} directory
|
|
43
54
|
* @returns {fs.Dirent<string>[]}
|
|
44
55
|
*/
|
|
45
|
-
|
|
56
|
+
function getAeroflyAircraftDirectories(directory) {
|
|
46
57
|
return fs
|
|
47
58
|
.readdirSync(directory, { withFileTypes: true })
|
|
48
59
|
.filter((dirent) => dirent.isDirectory())
|
|
49
60
|
.sort()
|
|
50
61
|
.filter((dirent) => fs.existsSync(path.join(dirent.parentPath, dirent.name, dirent.name + ".tmc")));
|
|
51
|
-
}
|
|
62
|
+
}
|
|
52
63
|
|
|
53
64
|
/**
|
|
54
65
|
*
|
|
55
66
|
* @param {string} directory
|
|
56
67
|
* @returns {AeroflyAircraft[]}
|
|
57
68
|
*/
|
|
58
|
-
export
|
|
69
|
+
export function getAeroflyAircraft(directory) {
|
|
59
70
|
return getAeroflyAircraftDirectories(directory).map((dirent) => {
|
|
60
71
|
const tmdFileContent = fs.readFileSync(path.join(dirent.parentPath, dirent.name, dirent.name + ".tmc"), "utf8");
|
|
61
72
|
|
|
@@ -97,6 +108,8 @@ export const getAeroflyAircraft = (directory) => {
|
|
|
97
108
|
const liveryIcaoCode = getLiveryIcaoCode(livery.name);
|
|
98
109
|
if (liveryIcaoCode !== "") {
|
|
99
110
|
livery.icaoCode = liveryIcaoCode;
|
|
111
|
+
} else {
|
|
112
|
+
MISSING_LIVERIES.add(livery.name);
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
return livery;
|
|
@@ -108,121 +121,23 @@ export const getAeroflyAircraft = (directory) => {
|
|
|
108
121
|
liveries,
|
|
109
122
|
};
|
|
110
123
|
});
|
|
111
|
-
}
|
|
124
|
+
}
|
|
112
125
|
|
|
113
126
|
/**
|
|
114
127
|
* Convert airline name to airline ICA code
|
|
115
128
|
* @param {string} airlineName
|
|
116
129
|
* @returns {string} icaoCode of airline
|
|
117
130
|
*/
|
|
118
|
-
|
|
131
|
+
function getLiveryIcaoCode(airlineName) {
|
|
119
132
|
const name = airlineName.toLowerCase();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return "ANA";
|
|
123
|
-
case "american airlines":
|
|
124
|
-
case "american":
|
|
125
|
-
return "AAL";
|
|
126
|
-
case "all nippon airways":
|
|
127
|
-
return "ANA";
|
|
128
|
-
case "china eastern":
|
|
129
|
-
return "CES";
|
|
130
|
-
case "lufthansa":
|
|
131
|
-
case "lufthansa 100":
|
|
132
|
-
return "DLH";
|
|
133
|
-
case "united airlines":
|
|
134
|
-
case "united":
|
|
135
|
-
return "UAL";
|
|
136
|
-
case "delta air lines":
|
|
137
|
-
case "delta":
|
|
138
|
-
return "DAL";
|
|
139
|
-
case "emirates":
|
|
140
|
-
return "UAE";
|
|
141
|
-
case "singapore airlines":
|
|
142
|
-
return "SIA";
|
|
143
|
-
case "ethiopian":
|
|
144
|
-
return "ETH";
|
|
145
|
-
case "air france":
|
|
146
|
-
return "AFR";
|
|
147
|
-
case "british airways":
|
|
148
|
-
return "BAW";
|
|
149
|
-
case "china southern airlines":
|
|
150
|
-
return "CSN";
|
|
151
|
-
case "turkish airlines":
|
|
152
|
-
return "THY";
|
|
153
|
-
case "air canada":
|
|
154
|
-
return "ACA";
|
|
155
|
-
case "etihad airways":
|
|
156
|
-
case "ethiad":
|
|
157
|
-
return "ETD";
|
|
158
|
-
case "latam airlines":
|
|
159
|
-
case "latam":
|
|
160
|
-
return "LAN";
|
|
161
|
-
case "tam airlines":
|
|
162
|
-
case "tam":
|
|
163
|
-
return "TAM";
|
|
164
|
-
case "jetblue airways":
|
|
165
|
-
case "jetblue":
|
|
166
|
-
return "JBU";
|
|
167
|
-
case "azul brazilian airlines":
|
|
168
|
-
return "AZU";
|
|
169
|
-
case "china eastern airlines":
|
|
170
|
-
return "CES";
|
|
171
|
-
case "ethiopian airlines":
|
|
172
|
-
return "ETH";
|
|
173
|
-
case "klm royal dutch airlines":
|
|
174
|
-
case "klm":
|
|
175
|
-
return "KLM";
|
|
176
|
-
case "korean air":
|
|
177
|
-
case "korean air lines":
|
|
178
|
-
return "KAL";
|
|
179
|
-
case "qatar airways":
|
|
180
|
-
return "QTR";
|
|
181
|
-
case "swiss international air lines":
|
|
182
|
-
case "swiss":
|
|
183
|
-
return "SWR";
|
|
184
|
-
case "hawaiian airlines":
|
|
185
|
-
return "HVN";
|
|
186
|
-
case "eurowings":
|
|
187
|
-
return "EWG";
|
|
188
|
-
case "easyjet":
|
|
189
|
-
return "EZY";
|
|
190
|
-
case "iberia":
|
|
191
|
-
return "IBE";
|
|
192
|
-
case "sas":
|
|
193
|
-
return "SAS";
|
|
194
|
-
case "aeroflot":
|
|
195
|
-
return "AFL";
|
|
196
|
-
case "air china":
|
|
197
|
-
return "CCA";
|
|
198
|
-
case "alitalia":
|
|
199
|
-
return "AZA";
|
|
200
|
-
case "brussels airlines":
|
|
201
|
-
return "BEL";
|
|
202
|
-
case "cathay pacific":
|
|
203
|
-
return "CPA";
|
|
204
|
-
case "china southern":
|
|
205
|
-
return "CSN";
|
|
206
|
-
case "norwegian":
|
|
207
|
-
return "NAX";
|
|
208
|
-
case "aer lingus":
|
|
209
|
-
return "EIN";
|
|
210
|
-
case "avianca":
|
|
211
|
-
return "AVA";
|
|
212
|
-
case "finnair":
|
|
213
|
-
return "FIN";
|
|
214
|
-
case "tap portugal":
|
|
215
|
-
return "TAP";
|
|
216
|
-
default:
|
|
217
|
-
return "";
|
|
218
|
-
}
|
|
219
|
-
};
|
|
133
|
+
return Object.hasOwn(LIVERY_TO_ICAO, name) ? LIVERY_TO_ICAO[name] : "";
|
|
134
|
+
}
|
|
220
135
|
|
|
221
136
|
/**
|
|
222
137
|
* @param {string} tmdFileContent
|
|
223
138
|
* @returns {AeroflyAircraftParsed}
|
|
224
139
|
*/
|
|
225
|
-
export
|
|
140
|
+
export function parseAircraft(tmdFileContent) {
|
|
226
141
|
const tags = parseTmdLine(tmdFileContent, "Tags").trim().split(" ");
|
|
227
142
|
|
|
228
143
|
// type: ;
|
|
@@ -269,14 +184,14 @@ export const parseAircraft = (tmdFileContent) => {
|
|
|
269
184
|
operatingEmptyMassKg: Number(parseTmdLine(tmdFileContent, "OperatingEmptyMass")) || undefined,
|
|
270
185
|
maximumPersonsOnBoard: Number(parseTmdLine(tmdFileContent, "MaximumPersonsOnBoard")) || undefined,
|
|
271
186
|
};
|
|
272
|
-
}
|
|
187
|
+
}
|
|
273
188
|
|
|
274
189
|
/**
|
|
275
190
|
*
|
|
276
191
|
* @param {string} tmdFileContent
|
|
277
192
|
* @returns {string[]}
|
|
278
193
|
*/
|
|
279
|
-
|
|
194
|
+
function parseRequirements(tmdFileContent) {
|
|
280
195
|
const requirements = parseTmdLine(tmdFileContent, "Requirements").trim();
|
|
281
196
|
if (requirements === "") {
|
|
282
197
|
return [];
|
|
@@ -285,7 +200,7 @@ const parseRequirements = (tmdFileContent) => {
|
|
|
285
200
|
.split(" ")
|
|
286
201
|
.map((req) => req.trim())
|
|
287
202
|
.filter((req) => req !== "");
|
|
288
|
-
}
|
|
203
|
+
}
|
|
289
204
|
|
|
290
205
|
/**
|
|
291
206
|
*
|
|
@@ -293,45 +208,45 @@ const parseRequirements = (tmdFileContent) => {
|
|
|
293
208
|
* @param {string} key
|
|
294
209
|
* @returns {string}
|
|
295
210
|
*/
|
|
296
|
-
export
|
|
211
|
+
export function parseTmdLine(tmdFileContent, key) {
|
|
297
212
|
const r = new RegExp("\\[" + key + "\\]\\s*\\[(.+?)\\]");
|
|
298
213
|
const match = tmdFileContent.match(r);
|
|
299
214
|
return match && match[1] ? match[1] : "";
|
|
300
|
-
}
|
|
215
|
+
}
|
|
301
216
|
|
|
302
217
|
/**
|
|
303
218
|
*
|
|
304
219
|
* @param {string} speed in m/s
|
|
305
220
|
* @returns {number} in kts
|
|
306
221
|
*/
|
|
307
|
-
export
|
|
222
|
+
export function convertSpeed(speed) {
|
|
308
223
|
return Math.round(Number(speed) * 1.94384449);
|
|
309
|
-
}
|
|
224
|
+
}
|
|
310
225
|
|
|
311
226
|
/**
|
|
312
227
|
*
|
|
313
228
|
* @param {string} altitude in m
|
|
314
229
|
* @returns {number} in ft
|
|
315
230
|
*/
|
|
316
|
-
export
|
|
231
|
+
export function convertAltitude(altitude) {
|
|
317
232
|
return Math.round(Number(altitude) * 3.28084);
|
|
318
|
-
}
|
|
233
|
+
}
|
|
319
234
|
|
|
320
235
|
/**
|
|
321
236
|
*
|
|
322
237
|
* @param {string} range in m
|
|
323
238
|
* @returns {number} in NM
|
|
324
239
|
*/
|
|
325
|
-
export
|
|
240
|
+
export function convertDistance(range) {
|
|
326
241
|
return Math.round(Number(range) / 1852);
|
|
327
|
-
}
|
|
242
|
+
}
|
|
328
243
|
|
|
329
244
|
/**
|
|
330
245
|
*
|
|
331
246
|
* @param {AeroflyAircraft[]} sortedAircraft
|
|
332
247
|
* @returns {string}
|
|
333
248
|
*/
|
|
334
|
-
export
|
|
249
|
+
export function getSelectOptions(sortedAircraft) {
|
|
335
250
|
const selectContent = sortedAircraft
|
|
336
251
|
.map((aircraft) => {
|
|
337
252
|
return ` <option value="${aircraft.aeroflyCode}">${aircraft.nameFull}</option>`;
|
|
@@ -343,14 +258,14 @@ export const getSelectOptions = (sortedAircraft) => {
|
|
|
343
258
|
${selectContent}
|
|
344
259
|
</select>
|
|
345
260
|
`;
|
|
346
|
-
}
|
|
261
|
+
}
|
|
347
262
|
|
|
348
263
|
/**
|
|
349
264
|
*
|
|
350
265
|
* @param {AeroflyAircraft[]} sortedAircraft
|
|
351
266
|
* @returns {string}
|
|
352
267
|
*/
|
|
353
|
-
export
|
|
268
|
+
export function getSelectOptgroupOptions(sortedAircraft) {
|
|
354
269
|
/**
|
|
355
270
|
* @type {{
|
|
356
271
|
* [key: string]: {
|
|
@@ -438,14 +353,14 @@ export const getSelectOptgroupOptions = (sortedAircraft) => {
|
|
|
438
353
|
${html}\
|
|
439
354
|
</select>
|
|
440
355
|
`;
|
|
441
|
-
}
|
|
356
|
+
}
|
|
442
357
|
|
|
443
358
|
/**
|
|
444
359
|
*
|
|
445
360
|
* @param {AeroflyAircraft[]} sortedAircraft
|
|
446
361
|
* @returns {string}
|
|
447
362
|
*/
|
|
448
|
-
export
|
|
363
|
+
export function getSelectLiveryOptgroupOptions(sortedAircraft) {
|
|
449
364
|
let html = "";
|
|
450
365
|
for (const aircraft of sortedAircraft) {
|
|
451
366
|
if (aircraft.liveries.length === 0) {
|
|
@@ -463,12 +378,12 @@ export const getSelectLiveryOptgroupOptions = (sortedAircraft) => {
|
|
|
463
378
|
${html}\
|
|
464
379
|
</select>
|
|
465
380
|
`;
|
|
466
|
-
}
|
|
381
|
+
}
|
|
467
382
|
|
|
468
383
|
/**
|
|
469
384
|
* @param {AeroflyAircraft[]} sortedAircraft
|
|
470
385
|
* @returns {string[]}
|
|
471
386
|
*/
|
|
472
|
-
export
|
|
387
|
+
export function getTags(sortedAircraft) {
|
|
473
388
|
return [...new Set(sortedAircraft.flatMap((aircraft) => aircraft.tags))].sort();
|
|
474
|
-
}
|
|
389
|
+
}
|
package/src/airport-functions.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as path from "path";
|
|
|
10
10
|
* @param {number|undefined} length in Bytes
|
|
11
11
|
* @returns {string}
|
|
12
12
|
*/
|
|
13
|
-
export
|
|
13
|
+
export function geoJsonType(type, isMilitary, length) {
|
|
14
14
|
if (type === "heliport" && length !== undefined && length > 0) {
|
|
15
15
|
return type;
|
|
16
16
|
}
|
|
@@ -35,14 +35,14 @@ export const geoJsonType = (type, isMilitary, length) => {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
return type;
|
|
38
|
-
}
|
|
38
|
+
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
*
|
|
42
42
|
* @param {string} directory
|
|
43
43
|
* @returns {Map<string,number>}
|
|
44
44
|
*/
|
|
45
|
-
export
|
|
45
|
+
export function getAeroflyAirports(directory) {
|
|
46
46
|
const aeroflyAirports = new Map();
|
|
47
47
|
let maxLength = 0;
|
|
48
48
|
let minLength = 10000;
|
|
@@ -61,14 +61,14 @@ export const getAeroflyAirports = (directory) => {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
return aeroflyAirports;
|
|
64
|
-
}
|
|
64
|
+
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
*
|
|
68
68
|
* @param {string} filename
|
|
69
69
|
* @returns {Map<string,number>}
|
|
70
70
|
*/
|
|
71
|
-
export
|
|
71
|
+
export function addCustomAeroflyAirportsToMap(filename) {
|
|
72
72
|
const aeroflyAirports = new Map();
|
|
73
73
|
const fileContent = fs.readFileSync(filename, "utf-8");
|
|
74
74
|
const lines = fileContent.split(/\r?\n/).sort();
|
|
@@ -81,7 +81,7 @@ export const addCustomAeroflyAirportsToMap = (filename) => {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
return aeroflyAirports;
|
|
84
|
-
}
|
|
84
|
+
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
*
|
|
@@ -90,7 +90,7 @@ export const addCustomAeroflyAirportsToMap = (filename) => {
|
|
|
90
90
|
* @param {string[]} airportsRecord
|
|
91
91
|
* @returns {string[]}
|
|
92
92
|
*/
|
|
93
|
-
export
|
|
93
|
+
export function getAirportSearchWords(ident, icaoCode, airportsRecord) {
|
|
94
94
|
/**
|
|
95
95
|
* @type {string[]}
|
|
96
96
|
*/
|
|
@@ -109,4 +109,4 @@ export const getAirportSearchWords = (ident, icaoCode, airportsRecord) => {
|
|
|
109
109
|
...airportsRecord[18].split(/,\s*/),
|
|
110
110
|
...extraWords,
|
|
111
111
|
].filter((word) => word && word.length > 0);
|
|
112
|
-
}
|
|
112
|
+
}
|