@fboes/aerofly-data 1.8.0 → 1.8.2

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.0",
3
+ "version": "1.8.2",
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,
@@ -118,8 +121,6 @@ const getLiveryIcaoCode = (airlineName) => {
118
121
  return "AAL";
119
122
  case "all nippon airways":
120
123
  return "ANA";
121
- case "american airlines":
122
- return "AAL";
123
124
  case "lufthansa":
124
125
  return "DLH";
125
126
  case "united airlines":
@@ -224,6 +225,9 @@ export const parseAircraft = (tmdFileContent) => {
224
225
  ),
225
226
  MaximumSpeed: convertSpeed(parseTmdLine(tmdFileContent, "MaximumSpeed")),*/
226
227
  maximumRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumRange")),
228
+ maximumLoadRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumLoadRange")),
229
+ maximumFuelRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumFuelRange")),
230
+ maximumFerryRangeNm: convertDistance(parseTmdLine(tmdFileContent, "MaximumFerryRange")),
227
231
  /*FlapAirspeedRange: parseTmdLine(tmdFileContent, "FlapAirspeedRange")
228
232
  .trim()
229
233
  .split(" ")
@@ -293,7 +297,7 @@ export const convertAltitude = (altitude) => {
293
297
  /**
294
298
  *
295
299
  * @param {string} range in m
296
- * @returns {number} in kts
300
+ * @returns {number} in NM
297
301
  */
298
302
  export const convertDistance = (range) => {
299
303
  return Math.round(Number(range) / 1852);
@@ -437,3 +441,11 @@ ${html}\
437
441
  </select>
438
442
  `;
439
443
  };
444
+
445
+ /**
446
+ * @param {AeroflyAircraft[]} sortedAircraft
447
+ * @returns {string[]}
448
+ */
449
+ export const getTags = (sortedAircraft) => {
450
+ return [...new Set(sortedAircraft.flatMap((aircraft) => aircraft.tags))].sort();
451
+ };
@@ -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;