@fboes/aerofly-data 1.8.1 → 1.8.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/dist/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  /**
4
- * @global Loaded from Mapbox GL JS script in index.html
4
+ * @global
5
5
  * @type {any} mapboxgl
6
6
  */
7
7
  var mapboxgl;
package/get-airports.js CHANGED
@@ -14,8 +14,6 @@ import {
14
14
  } from "./src/airport-functions.js";
15
15
 
16
16
  const inputDirectory = process.argv[2] ?? ".";
17
- const icaoFilterArg = process.argv[3]?.replace(/[^A-Z]/, "").toUpperCase();
18
- const icaoFilter = icaoFilterArg ? new RegExp("^[" + icaoFilterArg + "]") : null;
19
17
 
20
18
  // Ensure the output directory exists
21
19
  const outputDirectory = path.join("data");
@@ -26,108 +24,144 @@ if (!fs.existsSync(outputDirectory)) {
26
24
  const aeroflyGeoJson = new GeoJSON.FeatureCollection();
27
25
  const aeroflyAirports = new Map([
28
26
  ...addCustomAeroflyAirportsToMap(path.join(outputDirectory, "airports-custom.md")),
29
- ...getAeroflyAirports(inputDirectory, icaoFilter),
27
+ ...getAeroflyAirports(inputDirectory),
30
28
  ]);
31
29
  const aeroflyAirportsLength = aeroflyAirports.size;
32
30
  process.stdout.write(`Found \x1b[92m${aeroflyAirports.size}\x1b[0m Aerofly FS Airports
33
31
  `);
34
32
 
35
33
  const airportsSource = fs.readFileSync(`tmp/airports.csv`);
36
- /** @type {string[][]} with a single CSV line from airports.csv */
37
- const airportsRecords = parse(airportsSource, { bom: true });
38
34
 
35
+ /** @type {string[][]} with a single CSV line from airports.csv */
36
+ let airportsRecords = parse(airportsSource, { bom: true });
39
37
  let airportsRecordsProcessed = 0;
40
38
 
41
39
  // Collect all ICAO codes
40
+
41
+ /**
42
+ * @type {string[]}
43
+ */
42
44
  const icaoCodes = [];
45
+
46
+ /**
47
+ * @type {{
48
+ * code: String,
49
+ * name: String,
50
+ * lat: Number,
51
+ * lon: Number
52
+ * }[]}
53
+ */
43
54
  const icaoCoordinatesObject = [];
44
55
 
45
- for (const airportsRecord of airportsRecords) {
46
- // 3685,"KMIA","large_airport","Miami International Airport",25.79319953918457,-80.29060363769531,8,"NA","US","US-FL","Miami","yes","KMIA","MIA","MIA","http://www.miami-airport.com/","https://en.wikipedia.org/wiki/
47
- // 19927,"KGBN","small_airport","Gila Bend Air Force Auxiliary Airport",32.887501,-112.720001,883,"NA","US","US-AZ","Gila Bend","no",,,"KGXF","GXF",,"https://en.wikipedia.org/wiki/Gila_Bend_Air_Force_Auxiliary_Field",
56
+ /**
57
+ *
58
+ * @param {string[]} searchWords
59
+ * @returns {[number, string]|[undefined, undefined]} Returns the length of the airport and the code if found, otherwise undefined.
60
+ */
61
+ const getAeroflyAirport = (searchWords) => {
62
+ for (const word of searchWords) {
63
+ if (aeroflyAirports.has(word)) {
64
+ return [aeroflyAirports.get(word) || 0, word];
65
+ }
66
+ }
67
+ return [undefined, undefined];
68
+ };
69
+
70
+ /**
71
+ * @param {string} aeroflyCode
72
+ * @param {string} bestCode
73
+ * @param {string[]} airportsRecord
74
+ * @param {number} length
75
+ */
76
+ const addRecord = (aeroflyCode, bestCode, airportsRecord, length) => {
77
+ // Remove airport from list of Aerofly FS4 Airports
78
+ aeroflyAirports.delete(aeroflyCode);
79
+
80
+ const isMilitary =
81
+ airportsRecord[3].match(/\b(base|rnas|raf|naval|air\s?force|coast\s?guard|army|afs|mod|cgas)\b/i) !== null;
82
+ let type = airportsRecord[2];
83
+ if (isMilitary) {
84
+ type = type.replace(/port/, "base");
85
+ }
48
86
 
49
- const ident = airportsRecord[1];
50
- const icaoCode = airportsRecord[12];
51
- const searchWords = getAirportSearchWords(ident, icaoCode, airportsRecord);
87
+ // Filter community airports and add regular airports to secondary lists
88
+ if (length > 0) {
89
+ icaoCodes.push(bestCode);
90
+ icaoCoordinatesObject.push({
91
+ code: bestCode,
92
+ name: airportsRecord[3],
93
+ lat: Number(airportsRecord[4]),
94
+ lon: Number(airportsRecord[5]),
95
+ });
96
+ }
52
97
 
53
- if (icaoFilter && !ident.match(icaoFilter) && !icaoCode.match(icaoFilter)) {
54
- continue;
98
+ const feature = new GeoJSON.Feature(
99
+ new GeoJSON.Point(Number(airportsRecord[5]), Number(airportsRecord[4]), Number(airportsRecord[6]) * 0.3048),
100
+ {
101
+ title: bestCode,
102
+ type: geoJsonType(type, isMilitary, length),
103
+ description: airportsRecord[3],
104
+ elevation: Number(airportsRecord[6]),
105
+ municipality: airportsRecord[10],
106
+ fileSize: Math.ceil(length),
107
+ "marker-symbol": airportsRecord[2].match(/heliport/)
108
+ ? "heliport"
109
+ : airportsRecord[2].match(/small/)
110
+ ? "airfield"
111
+ : "airport",
112
+ "marker-color": airportsRecord[2].match(/large/)
113
+ ? "#5e6eba"
114
+ : airportsRecord[2].match(/small/)
115
+ ? "#777777"
116
+ : "#555555",
117
+ },
118
+ );
119
+ if (isMilitary) {
120
+ feature.setProperty("isMilitary", true);
55
121
  }
56
122
 
57
- airportsRecordsProcessed++;
123
+ aeroflyGeoJson.addFeature(feature);
124
+ };
58
125
 
59
- /**
60
- *
61
- * @param {string[]} searchWords
62
- * @returns {[number, string]|[undefined, undefined]} Returns the length of the airport and the code if found, otherwise undefined.
63
- */
64
- const getAeroflyAirport = (searchWords) => {
65
- for (const word of searchWords) {
66
- if (aeroflyAirports.has(word)) {
67
- return [aeroflyAirports.get(word) || 0, word];
68
- }
69
- }
70
- return [undefined, undefined];
71
- };
126
+ // -----------------------------------------------------------------------------
127
+ // PARSING
72
128
 
73
- const [length, aeroflyCode] = getAeroflyAirport(searchWords);
129
+ // 3685,"KMIA","large_airport","Miami International Airport",25.79319953918457,-80.29060363769531,8,"NA","US","US-FL","Miami","yes","KMIA","MIA","MIA","http://www.miami-airport.com/","https://en.wikipedia.org/wiki/
130
+ // 19927,"KGBN","small_airport","Gila Bend Air Force Auxiliary Airport",32.887501,-112.720001,883,"NA","US","US-AZ","Gila Bend","no",,,"KGXF","GXF",,"https://en.wikipedia.org/wiki/Gila_Bend_Air_Force_Auxiliary_Field",
74
131
 
75
- if (length !== undefined && aeroflyCode !== undefined) {
76
- const bestCode = icaoCode || aeroflyCode || ident;
132
+ process.stdout.write("ROUND 1: ICAO codes\n");
133
+ for (const airportsRecord of airportsRecords) {
134
+ airportsRecordsProcessed++;
135
+ const icaoCode = airportsRecord[12];
136
+ const [length, aeroflyCode] = getAeroflyAirport([icaoCode]);
77
137
 
78
- // Remove airport from list of Aerofly FS4 Airports
79
- aeroflyAirports.delete(aeroflyCode);
138
+ if (length !== undefined && aeroflyCode !== undefined) {
139
+ addRecord(aeroflyCode, icaoCode, airportsRecord, length);
140
+ }
80
141
 
81
- if (aeroflyCode !== bestCode) {
82
- process.stdout
83
- .write(` > Mismatching codes for airport: Using \x1b[93m${bestCode}\x1b[0m, was \x1b[93m${aeroflyCode}\x1b[0m from Aerofly FS4 data
142
+ if (airportsRecordsProcessed % 5000 === 0) {
143
+ const index = aeroflyAirportsLength - aeroflyAirports.size;
144
+ process.stdout
145
+ .write(` Processed \x1b[92m${String(airportsRecordsProcessed).padStart(5)}\x1b[0m airport records, found \x1b[92m${String(index).padStart(5)}\x1b[0m Aerofly FS Airports
84
146
  `);
85
- }
147
+ }
148
+ }
86
149
 
87
- const isMilitary =
88
- airportsRecord[3].match(/\b(base|rnas|raf|naval|air\s?force|coast\s?guard|army|afs|mod|cgas)\b/i) !== null;
89
- let type = airportsRecord[2];
90
- if (isMilitary) {
91
- type = type.replace(/port/, "base");
92
- }
150
+ airportsRecords = parse(airportsSource, { bom: true });
151
+ airportsRecordsProcessed = 0;
93
152
 
94
- // Filter community airports and add regular airports to secondary lists
95
- if (length > 0) {
96
- icaoCodes.push(bestCode);
97
- icaoCoordinatesObject.push({
98
- code: bestCode,
99
- name: airportsRecord[3],
100
- lat: Number(airportsRecord[4]),
101
- lon: Number(airportsRecord[5]),
102
- });
103
- }
153
+ process.stdout.write("ROUND 2: Other codes\n");
154
+ for (const airportsRecord of airportsRecords) {
155
+ airportsRecordsProcessed++;
156
+ const ident = airportsRecord[1];
157
+ const icaoCode = airportsRecord[12];
158
+ const searchWords = getAirportSearchWords(ident, icaoCode, airportsRecord);
104
159
 
105
- const feature = new GeoJSON.Feature(
106
- new GeoJSON.Point(Number(airportsRecord[5]), Number(airportsRecord[4]), Number(airportsRecord[6]) * 0.3048),
107
- {
108
- title: bestCode,
109
- type: geoJsonType(type, isMilitary, length),
110
- description: airportsRecord[3],
111
- elevation: Number(airportsRecord[6]),
112
- municipality: airportsRecord[10],
113
- fileSize: Math.ceil(length),
114
- "marker-symbol": airportsRecord[2].match(/heliport/)
115
- ? "heliport"
116
- : airportsRecord[2].match(/small/)
117
- ? "airfield"
118
- : "airport",
119
- "marker-color": airportsRecord[2].match(/large/)
120
- ? "#5e6eba"
121
- : airportsRecord[2].match(/small/)
122
- ? "#777777"
123
- : "#555555",
124
- },
125
- );
126
- if (isMilitary) {
127
- feature.setProperty("isMilitary", true);
128
- }
160
+ const [length, aeroflyCode] = getAeroflyAirport(searchWords);
129
161
 
130
- aeroflyGeoJson.addFeature(feature);
162
+ if (length !== undefined && aeroflyCode !== undefined) {
163
+ const bestCode = icaoCode || aeroflyCode || ident;
164
+ addRecord(aeroflyCode, bestCode, airportsRecord, length);
131
165
  }
132
166
 
133
167
  if (airportsRecordsProcessed % 5000 === 0) {
@@ -138,6 +172,19 @@ for (const airportsRecord of airportsRecords) {
138
172
  }
139
173
  }
140
174
 
175
+ icaoCoordinatesObject.sort((a, b) => {
176
+ if (a.code < b.code) {
177
+ return -1;
178
+ }
179
+ if (a.code > b.code) {
180
+ return 1;
181
+ }
182
+ return 0;
183
+ });
184
+
185
+ // -----------------------------------------------------------------------------
186
+ // OUTPUT
187
+
141
188
  // Write the GeoJSON data to a file
142
189
  const outputFilePath = path.join(outputDirectory, "airports.geojson");
143
190
  fs.writeFileSync(outputFilePath, JSON.stringify(aeroflyGeoJson, null, 2), "utf-8");
@@ -184,7 +231,8 @@ process.stdout.write(`Unmatched airports file written to \x1b[92m${unmatchedFile
184
231
 
185
232
  if (aeroflyAirports.size > 0) {
186
233
  process.stdout.write(
187
- `Missing airport matches for \x1b[92m${aeroflyAirports.size}\x1b[0m Aerofly FS4 Airports, \x1b[92m${(
234
+ `\
235
+ Missing airport matches for \x1b[92m${aeroflyAirports.size}\x1b[0m Aerofly FS4 Airports, \x1b[92m${(
188
236
  (aeroflyAirports.size / aeroflyAirportsLength) *
189
237
  100
190
238
  ).toFixed(1)}%\x1b[0m
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fboes/aerofly-data",
3
- "version": "1.8.1",
3
+ "version": "1.8.3",
4
4
  "description": "This project contains data sets for airports and aircraft present in Aerofly FS 4.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,6 +14,9 @@ import * as path from "path";
14
14
  * cruiseAltitudeFt: number,
15
15
  * cruiseSpeedKts: number,
16
16
  * maximumRangeNm: number,
17
+ * maximumLoadRangeNm: number,
18
+ * maximumFuelRangeNm: number,
19
+ * maximumFerryRangeNm: number,
17
20
  * maximumFuelMassKg?: number,
18
21
  * maximumPayloadKg?: number,
19
22
  * maximumTakeoffMassKg?: number,
@@ -88,14 +91,16 @@ export const getAeroflyAircraft = (directory) => {
88
91
  requirements: parseRequirements(tmdFileContent),
89
92
  };
90
93
 
91
- const liveryIcaoCode = getLiveryIcaoCode(livery.name);
92
- if (liveryIcaoCode !== "") {
93
- livery.icaoCode = liveryIcaoCode;
94
- }
95
-
96
94
  return livery;
97
95
  }),
98
- ];
96
+ ].map((livery) => {
97
+ const liveryIcaoCode = getLiveryIcaoCode(livery.name);
98
+ if (liveryIcaoCode !== "") {
99
+ livery.icaoCode = liveryIcaoCode;
100
+ }
101
+
102
+ return livery;
103
+ });
99
104
 
100
105
  return {
101
106
  aeroflyCode: dirent.name,
@@ -113,13 +118,15 @@ export const getAeroflyAircraft = (directory) => {
113
118
  const getLiveryIcaoCode = (airlineName) => {
114
119
  const name = airlineName.toLowerCase();
115
120
  switch (name) {
121
+ case "all nippon airlines":
122
+ return "ANA";
116
123
  case "american airlines":
117
124
  case "american":
118
125
  return "AAL";
119
126
  case "all nippon airways":
120
127
  return "ANA";
121
- case "american airlines":
122
- return "AAL";
128
+ case "china eastern":
129
+ return "CES";
123
130
  case "lufthansa":
124
131
  return "DLH";
125
132
  case "united airlines":
@@ -128,6 +135,12 @@ const getLiveryIcaoCode = (airlineName) => {
128
135
  case "delta air lines":
129
136
  case "delta":
130
137
  return "DAL";
138
+ case "emirates":
139
+ return "UAE";
140
+ case "singapore airlines":
141
+ return "SIA";
142
+ case "ethiopian":
143
+ return "ETH";
131
144
  case "air france":
132
145
  return "AFR";
133
146
  case "british airways":
@@ -224,6 +237,9 @@ export const parseAircraft = (tmdFileContent) => {
224
237
  ),
225
238
  MaximumSpeed: convertSpeed(parseTmdLine(tmdFileContent, "MaximumSpeed")),*/
226
239
  maximumRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumRange")),
240
+ maximumLoadRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumLoadRange")),
241
+ maximumFuelRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumFuelRange")),
242
+ maximumFerryRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumFerryRange")),
227
243
  /*FlapAirspeedRange: parseTmdLine(tmdFileContent, "FlapAirspeedRange")
228
244
  .trim()
229
245
  .split(" ")
@@ -293,7 +309,7 @@ export const convertAltitude = (altitude) => {
293
309
  /**
294
310
  *
295
311
  * @param {string} range in m
296
- * @returns {number} in kts
312
+ * @returns {number} in NM
297
313
  */
298
314
  export const convertDistance = (range) => {
299
315
  return Math.round(Number(range) / 1852);
@@ -437,3 +453,11 @@ ${html}\
437
453
  </select>
438
454
  `;
439
455
  };
456
+
457
+ /**
458
+ * @param {AeroflyAircraft[]} sortedAircraft
459
+ * @returns {string[]}
460
+ */
461
+ export const getTags = (sortedAircraft) => {
462
+ return [...new Set(sortedAircraft.flatMap((aircraft) => aircraft.tags))].sort();
463
+ };
@@ -40,10 +40,9 @@ export const geoJsonType = (type, isMilitary, length) => {
40
40
  /**
41
41
  *
42
42
  * @param {string} directory
43
- * @param {RegExp?} icaoFilter
44
43
  * @returns {Map<string,number>}
45
44
  */
46
- export const getAeroflyAirports = (directory, icaoFilter) => {
45
+ export const getAeroflyAirports = (directory) => {
47
46
  const aeroflyAirports = new Map();
48
47
  let maxLength = 0;
49
48
  let minLength = 10000;
@@ -55,12 +54,10 @@ export const getAeroflyAirports = (directory, icaoFilter) => {
55
54
 
56
55
  for (const file of files) {
57
56
  const icaoCode = file.replace(/\.wad$/, "").toUpperCase();
58
- if (!icaoFilter || icaoCode.match(icaoFilter)) {
59
- const stats = fs.statSync(path.join(directory, file));
60
- maxLength = Math.max(maxLength, stats.size);
61
- minLength = Math.min(minLength, stats.size);
62
- aeroflyAirports.set(icaoCode, stats.size);
63
- }
57
+ const stats = fs.statSync(path.join(directory, file));
58
+ maxLength = Math.max(maxLength, stats.size);
59
+ minLength = Math.min(minLength, stats.size);
60
+ aeroflyAirports.set(icaoCode, stats.size);
64
61
  }
65
62
 
66
63
  return aeroflyAirports;