@fboes/aerofly-data 1.0.0 → 1.1.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ - Added `CHANGELOG.md`
11
+ - Added directory listing for `/icons`
12
+
13
+ ## [1.1.0] - 2025-05-20
14
+
15
+ - Added more data artifacts
16
+
17
+ ## [1.0.0] - 2025-05-18
18
+
19
+ - Initial release
package/README.md CHANGED
@@ -2,22 +2,23 @@
2
2
 
3
3
  This project contains data sets for airports and aircraft present in [Aerofly FS 4](https://www.aerofly.com/). This airport data is based on data from [OurAirports](https://ourairports.com/).
4
4
 
5
+ There is also a [ICAO code checker](https://fboes.github.io/aerofly-data/dist/), which checks if a given ICAO airport code exists in Aerofly FS 4.
6
+
5
7
  It also contains [airport and navigation aid icons](./icons/) suitable for maps.
6
8
 
7
9
  ## Enclosed files
8
10
 
9
11
  The `data` directory contains the following files:
10
12
 
11
- | File | Description |
12
- | --------------------------------------------------------- | --------------------------------------------------------------------------------------- |
13
- | [`aircraft-liveries.json`](./data/aircraft-liveries.json) | JSON file containing detailed information about all aircraft, including liveries. |
14
- | [`aircraft-select.html`](./data/aircraft-select.html) | HTML snippet file containing all aircraft |
15
- | [`aircraft.json`](./data/aircraft.json) | JSON file containing abbreviated information about all aircraft (without liveries). |
16
- | [`aircraft.md`](./data/aircraft.md) | Markdown file containing abbreviated information about all aircraft (without liveries). |
17
- | [`airport-list.json`](./data/airport-list.json) | JSON file containing an array of all ICAO codes. |
18
- | [`airports.geojson`](./data/airports.geojson) | GeoJSON file containing the location of all airports in Aerofly FS 4. |
19
-
20
- [](https://fboes.github.io/aerofly-data/dist/)
13
+ | File | Description |
14
+ | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
15
+ | [`aircraft-liveries.json`](./data/aircraft-liveries.json) | JSON file containing detailed information about all aircraft, including liveries. |
16
+ | [`aircraft-select-optgroup.html`](./data/aircraft-select-optgroup.html) | HTML snippet file containing all aircraft, sorted by category |
17
+ | [`aircraft-select.html`](./data/aircraft-select.html) | HTML snippet file containing all aircraft |
18
+ | [`aircraft.json`](./data/aircraft.json) | JSON file containing abbreviated information about all aircraft (without liveries). |
19
+ | [`aircraft.md`](./data/aircraft.md) | Markdown file containing abbreviated information about all aircraft (without liveries). |
20
+ | [`airport-list.json`](./data/airport-list.json) | JSON file containing an array of all ICAO codes. |
21
+ | [`airports.geojson`](./data/airports.geojson) | GeoJSON file containing the location of all airports in Aerofly FS 4. |
21
22
 
22
23
  ## Building
23
24
 
File without changes
@@ -0,0 +1,55 @@
1
+ <select id="aircraft-select-optgroup">
2
+ <optgroup label="Airliner">
3
+ <option value="a319">Airbus A319-115</option>
4
+ <option value="a320">Airbus A320-214</option>
5
+ <option value="a321">Airbus A321-213</option>
6
+ <option value="a350_1000">Airbus A350-1000</option>
7
+ <option value="a380">Airbus A380-800</option>
8
+ <option value="b737_max9">Boeing 737 MAX 9</option>
9
+ <option value="b737">Boeing 737-500</option>
10
+ <option value="b737_900">Boeing 737-900ER</option>
11
+ <option value="b747">Boeing 747-400</option>
12
+ <option value="b777_300er">Boeing 777-300ER</option>
13
+ <option value="b777f">Boeing 777F</option>
14
+ <option value="b787">Boeing 787-10 Dreamliner</option>
15
+ <option value="crj900">Bombardier CRJ-900LR</option>
16
+ <option value="q400">Bombardier Dash-8 Q400</option>
17
+ </optgroup>
18
+ <optgroup label="Helicopters">
19
+ <option value="ec135">Eurocopter EC135-T1</option>
20
+ <option value="r22">Robinson R22 Beta II</option>
21
+ <option value="uh60">Sikorsky UH-60M Black Hawk</option>
22
+ </optgroup>
23
+ <optgroup label="General Aviation">
24
+ <option value="b58">Beechcraft Baron 58</option>
25
+ <option value="c90gtx">Beechcraft King Air C90 GTx</option>
26
+ <option value="lj45">Bombardier Learjet 45</option>
27
+ <option value="c172">Cessna 172 SP Skyhawk</option>
28
+ <option value="dr400">Robin DR400</option>
29
+ </optgroup>
30
+ <optgroup label="Historical Aircraft">
31
+ <option value="concorde">Aérospatiale/BAC Concorde</option>
32
+ <option value="jungmeister">Bücker Bü-133 Jungmeister</option>
33
+ <option value="dr1">Fokker Dr.I</option>
34
+ <option value="ju52">Junkers Ju 52/3m</option>
35
+ <option value="p38">Lockheed P-38 Lightning</option>
36
+ <option value="bf109e">Messerschmitt Bf 109E</option>
37
+ <option value="camel">Sopwith F.1 Camel</option>
38
+ <option value="f4u">Vought F4U Corsair</option>
39
+ </optgroup>
40
+ <optgroup label="Military Aircraft">
41
+ <option value="mb339">Aermacchi MB-339</option>
42
+ <option value="f15e">McDonnell Douglas F-15E Strike Eagle</option>
43
+ <option value="f18">McDonnell Douglas F/A-18C Hornet</option>
44
+ </optgroup>
45
+ <optgroup label="Aerobatic aircraft">
46
+ <option value="extra330">Extra 330 LX</option>
47
+ <option value="pitts">Pitts S-2B</option>
48
+ </optgroup>
49
+ <optgroup label="Gliders">
50
+ <option value="antares">Antares 21 Electro</option>
51
+ <option value="swift">Marganski Swift S1</option>
52
+ <option value="asg29">Schleicher ASG 29-18m</option>
53
+ <option value="ask21">Schleicher ASK 21</option>
54
+ </optgroup>
55
+ </select>
File without changes
File without changes
package/data/aircraft.md CHANGED
@@ -2,42 +2,42 @@
2
2
 
3
3
  | Aircraft Name | ICAO Code | Aerofly FS Code | Approach Speed (kts) | Cruise Altitude (ft) | Cruise Speed (kts) | Maximum Range (nm) |
4
4
  | ------------------------------------ | --------- | --------------- | -------------------: | -------------------: | -----------------: | -----------------: |
5
+ | Aermacchi MB-339 | `M339` | `mb339` | 120 | 25000 | 486 | 1188 |
6
+ | Aérospatiale/BAC Concorde | `CONC` | `concorde` | 165 | 55000 | 1177 | 3900 |
5
7
  | Airbus A319-115 | `A319` | `a319` | 142 | 32000 | 453 | 3747 |
6
8
  | Airbus A320-214 | `A320` | `a320` | 142 | 32000 | 453 | 3321 |
7
9
  | Airbus A321-213 | `A321` | `a321` | 145 | 32000 | 453 | 3186 |
8
10
  | Airbus A350-1000 | `A35K` | `a350_1000` | 151 | 37000 | 488 | 8909 |
9
11
  | Airbus A380-800 | `A388` | `a380` | 142 | 36000 | 517 | 8207 |
10
12
  | Antares 21 Electro | `LAE1` | `antares` | 65 | 8202 | 81 | 313 |
11
- | Schleicher ASG 29-18m | `AS29` | `asg29` | 54 | 8202 | 76 | 0 |
12
- | Schleicher ASK 21 | `AS21` | `ask21` | 57 | 4921 | 59 | 0 |
13
13
  | Beechcraft Baron 58 | `BE58` | `b58` | 90 | 11000 | 202 | 1229 |
14
+ | Beechcraft King Air C90 GTx | `BE9L` | `c90gtx` | 100 | 18000 | 272 | 1192 |
15
+ | Boeing 737 MAX 9 | `B39M` | `b737_max9` | 144 | 37000 | 453 | 3548 |
14
16
  | Boeing 737-500 | `B735` | `b737` | 125 | 33000 | 490 | 2808 |
15
17
  | Boeing 737-900ER | `B739` | `b737_900` | 144 | 37000 | 453 | 2948 |
16
- | Boeing 737 MAX 9 | `B39M` | `b737_max9` | 144 | 37000 | 453 | 3548 |
17
18
  | Boeing 747-400 | `B744` | `b747` | 145 | 34000 | 492 | 7262 |
18
- | Boeing 777F | `B77F` | `b777f` | 147 | 41000 | 482 | 9750 |
19
19
  | Boeing 777-300ER | `B77W` | `b777_300er` | 145 | 41000 | 482 | 7370 |
20
+ | Boeing 777F | `B77F` | `b777f` | 147 | 41000 | 482 | 9750 |
20
21
  | Boeing 787-10 Dreamliner | `B78X` | `b787` | 150 | 40000 | 482 | 6425 |
21
- | Messerschmitt Bf 109E | `ME09` | `bf109e` | 94 | 13123 | 309 | 432 |
22
- | Cessna 172 SP Skyhawk | `C172` | `c172` | 62 | 8000 | 130 | 1031 |
23
- | Beechcraft King Air C90 GTx | `BE9L` | `c90gtx` | 100 | 18000 | 272 | 1192 |
24
- | Sopwith F.1 Camel | `CAML` | `camel` | 45 | 9843 | 100 | 124 |
25
- | Aérospatiale/BAC Concorde | `CONC` | `concorde` | 165 | 55000 | 1177 | 3900 |
26
22
  | Bombardier CRJ-900LR | `CRJ9` | `crj900` | 125 | 38000 | 470 | 1550 |
27
- | Fokker Dr.I | `DR1` | `dr1` | 49 | 9843 | 100 | 162 |
28
- | Robin DR400 | `DR40` | `dr400` | 62 | 5991 | 167 | 586 |
23
+ | Bombardier Dash-8 Q400 | `DH8D` | `q400` | 135 | 24000 | 286 | 2808 |
24
+ | Bombardier Learjet 45 | `LJ45` | `lj45` | 120 | 41000 | 486 | 1710 |
25
+ | Bücker Bü-133 Jungmeister | `BU33` | `jungmeister` | 55 | 13123 | 119 | 270 |
26
+ | Cessna 172 SP Skyhawk | `C172` | `c172` | 62 | 8000 | 130 | 1031 |
29
27
  | Eurocopter EC135-T1 | `EC35` | `ec135` | 60 | 9000 | 135 | 343 |
30
28
  | Extra 330 LX | `E300` | `extra330` | 90 | 9843 | 220 | 459 |
31
- | McDonnell Douglas F-15E Strike Eagle | `F15` | `f15e` | 140 | 40000 | 1458 | 3100 |
32
- | McDonnell Douglas F/A-18C Hornet | `F18` | `f18` | 135 | 40000 | 1034 | 1080 |
33
- | Vought F4U Corsair | `CORS` | `f4u` | 105 | 20000 | 389 | 930 |
29
+ | Fokker Dr.I | `DR1` | `dr1` | 49 | 9843 | 100 | 162 |
34
30
  | Junkers Ju 52/3m | `JU52` | `ju52` | 65 | 19685 | 136 | 1080 |
35
- | Bücker Bü-133 Jungmeister | `BU33` | `jungmeister` | 55 | 13123 | 119 | 270 |
36
- | Bombardier Learjet 45 | `LJ45` | `lj45` | 120 | 41000 | 486 | 1710 |
37
- | Aermacchi MB-339 | `M339` | `mb339` | 120 | 25000 | 486 | 1188 |
38
31
  | Lockheed P-38 Lightning | `P38` | `p38` | 109 | 20000 | 315 | 1031 |
32
+ | Marganski Swift S1 | `` | `swift` | 63 | 6562 | 65 | 0 |
33
+ | McDonnell Douglas F-15E Strike Eagle | `F15` | `f15e` | 140 | 40000 | 1458 | 3100 |
34
+ | McDonnell Douglas F/A-18C Hornet | `F18` | `f18` | 135 | 40000 | 1034 | 1080 |
35
+ | Messerschmitt Bf 109E | `ME09` | `bf109e` | 94 | 13123 | 309 | 432 |
39
36
  | Pitts S-2B | `PTS2` | `pitts` | 90 | 9843 | 132 | 216 |
40
- | Bombardier Dash-8 Q400 | `DH8D` | `q400` | 135 | 24000 | 286 | 2808 |
37
+ | Robin DR400 | `DR40` | `dr400` | 62 | 5991 | 167 | 586 |
41
38
  | Robinson R22 Beta II | `R22` | `r22` | 60 | 6562 | 105 | 208 |
42
- | Marganski Swift S1 | `` | `swift` | 63 | 6562 | 65 | 0 |
43
- | Sikorsky UH-60M Black Hawk | `H60` | `uh60` | 70 | 9843 | 160 | 252 |
39
+ | Schleicher ASG 29-18m | `AS29` | `asg29` | 54 | 8202 | 76 | 0 |
40
+ | Schleicher ASK 21 | `AS21` | `ask21` | 57 | 4921 | 59 | 0 |
41
+ | Sikorsky UH-60M Black Hawk | `H60` | `uh60` | 70 | 9843 | 160 | 252 |
42
+ | Sopwith F.1 Camel | `CAML` | `camel` | 45 | 9843 | 100 | 124 |
43
+ | Vought F4U Corsair | `CORS` | `f4u` | 105 | 20000 | 389 | 930 |
File without changes
File without changes
package/dist/index.html CHANGED
@@ -1,53 +1,170 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Aerofly FS Airport Code Validator</title>
7
- <style>
8
- body {
9
- font-family: Arial, sans-serif;
10
- margin: 20px;
11
- text-align: center;
12
- }
13
- #status {
14
- font-size: 1.5em;
15
- margin-left: 10px;
16
- }
17
- </style>
18
- </head>
19
- <body>
20
- <h1>Aerofly FS Airport Code Validator</h1>
21
- <p>Enter an ICAO airport code:</p>
22
- <input type="text" id="icaoInput" placeholder="Enter ICAO code" pattern="A-Za-z0-9" autocapitalize="on" />
23
- <span id="status">❓</span>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Aerofly FS Airport Code Validator</title>
7
+ <meta
8
+ name="description"
9
+ content="Check if an airport is present in Aerofly FS 4 using its ICAO code."
10
+ />
11
+ <style>
12
+ :root {
13
+ color-scheme: light dark;
14
+ }
15
+ * {
16
+ margin: 0;
17
+ padding: 0;
18
+ }
19
+ body {
20
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
21
+ Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
22
+ "Segoe UI Symbol";
23
+ line-height: 1.5;
24
+ margin: 20px;
25
+ display: flex;
26
+ justify-content: center;
27
+ align-items: center;
28
+ min-height: 100vh;
29
+ }
30
+ main {
31
+ display: flex;
32
+ flex-direction: column;
33
+ align-items: center;
34
+ justify-content: center;
35
+ text-align: center;
36
+ padding-bottom: 2em;
37
+ }
38
+ h1 {
39
+ font-size: 1.8em;
40
+ margin-bottom: 0.75em;
41
+ border-bottom: 1px solid #ccc;
42
+ padding: 0 1em 0.25em;
43
+ }
44
+ label {
45
+ display: block;
46
+ margin-bottom: 1em;
47
+ }
48
+ input {
49
+ font: inherit;
50
+ padding: 0.25em;
51
+ border-radius: 0.25em;
52
+ width: 6em;
53
+ }
54
+ .inputbox {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: 0.5em;
58
+ font-size: 1.25em;
59
+ }
60
+ #status {
61
+ font-size: 1.25em;
62
+ }
63
+ #status2,
64
+ p {
65
+ margin-top: 1em;
66
+ }
67
+ </style>
68
+ </head>
69
+ <body>
70
+ <main>
71
+ <h1>Aerofly FS Airport Code Validator</h1>
72
+ <label for="icaoInput"
73
+ >Check if an airport is present in Aerofly FS 4 using its ICAO
74
+ code:</label
75
+ >
76
+ <div class="inputbox">
77
+ <input
78
+ type="text"
79
+ id="icaoInput"
80
+ list="icaoInputList"
81
+ placeholder="ICAO code"
82
+ pattern="A-Za-z0-9"
83
+ autocapitalize="characters"
84
+ autofocus="autofocus"
85
+ />
86
+ <datalist id="icaoInputList">
87
+ <option value="KATL">KATL</option>
88
+ <option value="KLAX">KLAX</option>
89
+ <option value="EGLL">EGLL</option>
90
+ <option value="OMDB">OMDB</option>
91
+ <option value="RJTT">RJTT</option>
92
+ </datalist>
93
+ <span
94
+ id="status"
95
+ title="Enter an ICAO code to check if the airport is present in Aerofly FS 4"
96
+ aria-hidden="true"
97
+ >❓</span
98
+ >
99
+ </div>
100
+ <div id="status2" role="status" aria-live="polite">
101
+ Enter an ICAO code to check if the airport is present in Aerofly FS 4
102
+ </div>
103
+ <p>Data source: <a href="..">Aerofly FS 4 data repository</a>.</p>
104
+ </main>
24
105
 
25
- <script>
26
- // Load the airport list from the JSON file
27
- let airportList = [];
28
- fetch('./data/airport-list.json')
29
- .then(response => response.json())
30
- .then(data => {
31
- airportList = data;
32
- })
33
- .catch(error => {
34
- console.error('Error loading airport list:', error);
35
- });
106
+ <script>
107
+ // Load the airport list from the JSON file
108
+ let airportList = [];
109
+ fetch("../data/airport-list.json")
110
+ .then((response) => response.json())
111
+ .then((data) => {
112
+ airportList = data;
113
+ })
114
+ .catch((error) => {
115
+ console.error("Error loading airport list:", error);
116
+ });
36
117
 
37
- // Validate the input
38
- const inputField = document.getElementById('icaoInput');
39
- const status = document.getElementById('status');
118
+ // Validate the input
119
+ const inputField = document.getElementById("icaoInput");
120
+ const status = document.getElementById("status");
121
+ const status2 = document.getElementById("status2");
40
122
 
41
- inputField.addEventListener('input', () => {
42
- const inputValue = inputField.value.toUpperCase();
43
- if (airportList.includes(inputValue)) {
44
- status.textContent = '✅'; // Green checkmark
45
- } else if (inputValue.trim() === '') {
46
- status.textContent = '❓'; // Question mark for empty input
47
- } else {
48
- status.textContent = '🚫'; // Red forbidden sign
49
- }
50
- });
51
- </script>
52
- </body>
53
- </html>
123
+ inputField.addEventListener("input", () => {
124
+ const inputValue = inputField.value.toUpperCase();
125
+
126
+ // Autocomplete: Populate datalist if input has 2+ chars
127
+ const datalist = document.getElementById("icaoInputList");
128
+ if (inputValue.length >= 2) {
129
+ // Filter airportList for codes starting with inputValue
130
+ const filtered = airportList.filter((code) =>
131
+ code.startsWith(inputValue)
132
+ );
133
+ // Clear existing options
134
+ datalist.innerHTML = "";
135
+ // Add filtered options
136
+ filtered.forEach((code) => {
137
+ const option = document.createElement("option");
138
+ option.value = code;
139
+ datalist.appendChild(option);
140
+ });
141
+ } else {
142
+ // Restore default options if input < 2 chars
143
+ datalist.innerHTML = `
144
+ <option value="KATL">KATL</option>
145
+ <option value="KLAX">KLAX</option>
146
+ <option value="EGLL">EGLL</option>
147
+ <option value="OMDB">OMDB</option>
148
+ <option value="RJTT">RJTT</option>
149
+ `;
150
+ }
151
+
152
+ if (airportList.includes(inputValue)) {
153
+ status.textContent = "✅"; // Green checkmark
154
+ status.title = "Airport is present in Aerofly FS 4";
155
+ status2.textContent = "Airport is present in Aerofly FS 4";
156
+ } else if (inputValue.trim() === "") {
157
+ status.textContent = "❓"; // Question mark for empty input
158
+ status.title =
159
+ "Enter an ICAO code to check if the airport is present in Aerofly FS 4";
160
+ status2.textContent =
161
+ "Enter an ICAO code to check if the airport is present in Aerofly FS 4";
162
+ } else {
163
+ status.textContent = "🚫"; // Red forbidden sign
164
+ status.title = "Airport is NOT present in Aerofly FS 4";
165
+ status2.textContent = "Airport is NOT present in Aerofly FS 4";
166
+ }
167
+ });
168
+ </script>
169
+ </body>
170
+ </html>
package/get-aircraft.js CHANGED
@@ -4,181 +4,11 @@
4
4
 
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
-
8
- /**
9
- * @typedef AeroflyAircraftParsed
10
- * @type {{
11
- * name: string,
12
- * nameFull: string,
13
- * icaoCode: string,
14
- * tags: string[],
15
- * approachAirspeedKts: number,
16
- * cruiseAltitudeFt: number,
17
- * cruiseSpeedKts: number,
18
- * maximumRangeNm: number,
19
- * }}
20
- */
21
-
22
- /**
23
- * @typedef AeroflyAircraft
24
- * @type {AeroflyAircraftParsed & {
25
- * aeroflyCode: string,
26
- * liveries: {
27
- * aeroflyCode: string,
28
- * name: string,
29
- * }[],
30
- * }}
31
- */
32
-
33
- /**
34
- *
35
- * @param {string} directory
36
- * @returns {AeroflyAircraft[]}
37
- */
38
- const getAeroflyAircraft = (directory) => {
39
- return fs
40
- .readdirSync(directory, { withFileTypes: true })
41
- .filter((dirent) => dirent.isDirectory())
42
- .sort()
43
- .map((dirent) => {
44
- const tmdFileContent = fs.readFileSync(
45
- path.join(dirent.parentPath, dirent.name, dirent.name + ".tmc"),
46
- "utf8"
47
- );
48
-
49
- const tmdOptionFileContent = fs.readFileSync(
50
- path.join(dirent.parentPath, dirent.name, "option.tmc"),
51
- "utf8"
52
- );
53
-
54
- const liveries = [
55
- {
56
- aeroflyCode: "default",
57
- name: parseTmdLine(tmdOptionFileContent, "Description"),
58
- },
59
- ...fs
60
- .readdirSync(path.join(dirent.parentPath, dirent.name), {
61
- withFileTypes: true,
62
- })
63
- .filter((dirent) => dirent.isDirectory())
64
- .filter((dirent) =>
65
- fs.existsSync(
66
- path.join(dirent.parentPath, dirent.name, "preview.ttx")
67
- )
68
- )
69
- .sort()
70
- .map((dirent) => {
71
- const tmdFileContent = fs.readFileSync(
72
- path.join(dirent.parentPath, dirent.name, "option.tmc"),
73
- "utf8"
74
- );
75
-
76
- return {
77
- aeroflyCode: dirent.name,
78
- name: parseTmdLine(tmdFileContent, "Description"),
79
- };
80
- }),
81
- ];
82
-
83
- return {
84
- ...parseAircraft(tmdFileContent),
85
- aeroflyCode: dirent.name,
86
- liveries,
87
- };
88
- });
89
- };
90
-
91
- /**
92
- * @param {string} tmdFileContent
93
- * @returns {AeroflyAircraftParsed}
94
- */
95
- const parseAircraft = (tmdFileContent) => {
96
- const tags = parseTmdLine(tmdFileContent, "Tags").trim().split(" ");
97
-
98
- // type: ;
99
- return {
100
- name: parseTmdLine(tmdFileContent, "DisplayName"),
101
- nameFull: parseTmdLine(tmdFileContent, "DisplayNameFull"),
102
- icaoCode: parseTmdLine(tmdFileContent, "ICAO"),
103
- tags,
104
- /*MinimumAirspeed: convertSpeed(
105
- parseTmdLine(tmdFileContent, "MinimumAirspeed")
106
- ),*/
107
- approachAirspeedKts: convertSpeed(
108
- parseTmdLine(tmdFileContent, "ApproachAirspeed")
109
- ),
110
- /*CruiseAirspeed: convertSpeed(
111
- parseTmdLine(tmdFileContent, "CruiseAirspeed")
112
- ),*/
113
- cruiseAltitudeFt: convertAltitude(
114
- parseTmdLine(tmdFileContent, "CruiseAltitude")
115
- ),
116
- cruiseSpeedKts: convertSpeed(parseTmdLine(tmdFileContent, "CruiseSpeed")),
117
- /*MaximumAirspeed: convertSpeed(
118
- parseTmdLine(tmdFileContent, "MaximumAirspeed")
119
- ),
120
- MaximumAltitude: convertAltitude(
121
- parseTmdLine(tmdFileContent, "MaximumAltitude")
122
- ),
123
- MaximumSpeed: convertSpeed(parseTmdLine(tmdFileContent, "MaximumSpeed")),*/
124
- maximumRangeNm: convertDistance(
125
- parseTmdLine(tmdFileContent, "MaximumRange")
126
- ),
127
- /*FlapAirspeedRange: parseTmdLine(tmdFileContent, "FlapAirspeedRange")
128
- .trim()
129
- .split(" ")
130
- .map((v) => convertSpeed(v)),
131
- NormalAirspeedRange: parseTmdLine(tmdFileContent, "NormalAirspeedRange")
132
- .trim()
133
- .split(" ")
134
- .map((v) => convertSpeed(v)),
135
- CautionAirspeedRange: parseTmdLine(tmdFileContent, "CautionAirspeedRange")
136
- .trim()
137
- .split(" ")
138
- .map((v) => convertSpeed(v)),*/
139
- };
140
- };
141
-
142
- /**
143
- *
144
- * @param {string} tmdFileContent
145
- * @param {string} key
146
- * @returns {string}
147
- */
148
- const parseTmdLine = (tmdFileContent, key) => {
149
- const r = new RegExp("\\[" + key + "\\]\\s*\\[(.+?)\\]");
150
- const match = tmdFileContent.match(r);
151
- return match && match[1] ? match[1] : "";
152
- };
153
-
154
- /**
155
- *
156
- * @param {string} speed in m/s
157
- * @returns {number} in kts
158
- */
159
- const convertSpeed = (speed) => {
160
- return Math.round(Number(speed) * 1.94384449);
161
- };
162
-
163
- /**
164
- *
165
- * @param {string} altitude in m
166
- * @returns {number} in ft
167
- */
168
- const convertAltitude = (altitude) => {
169
- return Math.round(Number(altitude) * 3.28084);
170
- };
171
-
172
- /**
173
- *
174
- * @param {string} range in m
175
- * @returns {number} in kts
176
- */
177
- const convertDistance = (range) => {
178
- return Math.round(Number(range) / 1852);
179
- };
180
-
181
- // -----------------------------------------------------------------------------
7
+ import {
8
+ getAeroflyAircraft,
9
+ getSelectOptgroupOptions,
10
+ getSelectOptions,
11
+ } from "./src/aircraft-functions.js";
182
12
 
183
13
  const inputDirectory = process.argv[2] ?? ".";
184
14
  const aeroflyAircraft = getAeroflyAircraft(inputDirectory);
@@ -223,6 +53,11 @@ process.stderr.write(
223
53
  `Abbreviated aircraft data (without liveries) written to \x1b[92m${outputFilePathWithoutLiveries}\x1b[0m\n`
224
54
  );
225
55
 
56
+ // Sort aeroflyAircraft by nameFull
57
+ const sortedAircraft = aeroflyAircraft.sort((a, b) =>
58
+ a.nameFull.localeCompare(b.nameFull)
59
+ );
60
+
226
61
  // Write summary to aircraft.md
227
62
  const summaryFilePath = path.join(outputDirectory, "aircraft.md");
228
63
  let summaryContent = `\
@@ -231,7 +66,7 @@ let summaryContent = `\
231
66
  | Aircraft Name | ICAO Code | Aerofly FS Code | Approach Speed (kts) | Cruise Altitude (ft) | Cruise Speed (kts) | Maximum Range (nm) |
232
67
  | ------------------------------------ | --------- | --------------- | -------------------: | -------------------: | -----------------: | -----------------: |
233
68
  `;
234
- summaryContent += aeroflyAircraft
69
+ summaryContent += sortedAircraft
235
70
  .map((aircraft) => {
236
71
  return `| ${aircraft.nameFull} | \`${aircraft.icaoCode}\` | \`${aircraft.aeroflyCode}\` | ${aircraft.approachAirspeedKts} | ${aircraft.cruiseAltitudeFt} | ${aircraft.cruiseSpeedKts} | ${aircraft.maximumRangeNm} |`;
237
72
  })
@@ -242,25 +77,33 @@ process.stderr.write(`Summary written to \x1b[92m${summaryFilePath}\x1b[0m\n`);
242
77
 
243
78
  // Write HTML <select> options to aircraft-select.html
244
79
  const selectFilePath = path.join(outputDirectory, "aircraft-select.html");
245
- let selectContent = `\
246
- <select id="aircraft-select">
247
- `;
248
80
 
249
- // Sort aeroflyAircraft by nameFull
250
- const sortedAircraft = aeroflyAircraft.sort((a, b) =>
251
- a.nameFull.localeCompare(b.nameFull)
252
- );
253
81
 
254
- selectContent += sortedAircraft
255
- .map((aircraft) => {
256
- return ` <option value="${aircraft.aeroflyCode}">${aircraft.nameFull}</option>`;
257
- })
258
- .join("\n");
259
- selectContent += `
260
- </select>
261
- `;
82
+ let selectContent = getSelectOptions(sortedAircraft);
262
83
 
263
84
  await fs.promises.writeFile(selectFilePath, selectContent, "utf-8");
264
85
  process.stderr.write(
265
86
  `HTML <select> options written to \x1b[92m${selectFilePath}\x1b[0m\n`
266
87
  );
88
+
89
+ // Write HTML <select> with <optgroup> to aircraft-select-optgroup.html
90
+ const selectOptgroupFilePath = path.join(
91
+ outputDirectory,
92
+ "aircraft-select-optgroup.html"
93
+ );
94
+
95
+ let selectOptgroupContent = getSelectOptgroupOptions(sortedAircraft);
96
+
97
+ await fs.promises.writeFile(
98
+ selectOptgroupFilePath,
99
+ selectOptgroupContent,
100
+ "utf-8"
101
+ );
102
+ process.stderr.write(
103
+ `HTML <select> with <optgroup> options written to \x1b[92m${selectOptgroupFilePath}\x1b[0m\n`
104
+ );
105
+
106
+ process.stderr.write(
107
+ `\nAll aircraft files written to \x1b[92m${path.resolve(outputDirectory)}\x1b[0m\n`
108
+ );
109
+
package/get-airports.js CHANGED
@@ -6,69 +6,7 @@ import GeoJSON from "@fboes/geojson";
6
6
  import * as fs from "node:fs";
7
7
  import * as path from "node:path";
8
8
  import { parse } from "csv-parse/sync";
9
-
10
- /**
11
- *
12
- * @param {string} type
13
- * @param {boolean} isMilitary
14
- * @param {number|undefined} lenght in Bytes
15
- * @returns {string}
16
- */
17
- const geoJsonType = (type, isMilitary, lenght) => {
18
- if (type === "heliport") {
19
- return type;
20
- }
21
-
22
- if (lenght !== undefined) {
23
- let size = "closed";
24
- if (lenght > 1200) {
25
- size = "large";
26
- } else if (lenght > 1050) {
27
- size = "medium";
28
- } else if (lenght > 920) {
29
- size = "small";
30
- }
31
-
32
- if (size === "closed") {
33
- return size;
34
- }
35
-
36
- return size + "_" + (isMilitary ? "airbase" : "airport");
37
- }
38
-
39
- return type;
40
- };
41
-
42
- /**
43
- *
44
- * @param {string} directory
45
- * @param {RegExp?} icaoFilter
46
- * @returns {Map<string,number>}
47
- */
48
- const getAeroflyAirports = (directory, icaoFilter) => {
49
- const aeroflyAirports = new Map();
50
- let maxLength = 0;
51
- let minLength = 10_000;
52
-
53
- const files = fs
54
- .readdirSync(directory)
55
- .filter((fn) => fn.endsWith(".wad"))
56
- .sort();
57
-
58
- for (const file of files) {
59
- const icaoCode = file.replace(/\.wad$/, "").toUpperCase();
60
- if (!icaoFilter || icaoCode.match(icaoFilter)) {
61
- const stats = fs.statSync(path.join(directory, file));
62
- maxLength = Math.max(maxLength, stats.size);
63
- minLength = Math.min(minLength, stats.size);
64
- aeroflyAirports.set(icaoCode, stats.size);
65
- }
66
- }
67
-
68
- return aeroflyAirports;
69
- };
70
-
71
- // -----------------------------------------------------------------------------
9
+ import { geoJsonType, getAeroflyAirports } from "./src/airport-functions.js";
72
10
 
73
11
  const inputDirectory = process.argv[2] ?? ".";
74
12
  const icaoFilterArg = process.argv[3]?.replace(/[^A-Z]/, "").toUpperCase();
@@ -0,0 +1,25 @@
1
+ # Aerofly Data Icons
2
+
3
+ This directory contains SVG icons used in the Aerofly FS data tools. Below is a list of all icons and their purposes:
4
+
5
+ | Icon | File Name | Purpose |
6
+ |:-----:|:----------|:--------|
7
+ | ![af-airfield](af-airfield.svg) | `af-airfield.svg` | Generic airfield |
8
+ | ![af-closed](af-closed.svg) | `af-closed.svg` | Closed airport or airfield |
9
+ | ![af-heliport](af-heliport.svg) | `af-heliport.svg` | Heliport |
10
+ | ![af-large_airbase](af-large_airbase.svg) | `af-large_airbase.svg` | Large airbase |
11
+ | ![af-large_airport](af-large_airport.svg) | `af-large_airport.svg` | Large airport |
12
+ | ![af-medium_airbase](af-medium_airbase.svg) | `af-medium_airbase.svg` | Medium airbase |
13
+ | ![af-medium_airport](af-medium_airport.svg) | `af-medium_airport.svg` | Medium airport |
14
+ | ![af-small_airbase](af-small_airbase.svg) | `af-small_airbase.svg` | Small airbase |
15
+ | ![af-small_airport](af-small_airport.svg) | `af-small_airport.svg` | Small airport |
16
+ | ![navaid-fix](navaid-fix.svg) | `navaid-fix.svg` | Navigation fix |
17
+ | ![navaid-ndb](navaid-ndb.svg) | `navaid-ndb.svg` | Non-directional beacon (NDB) |
18
+ | ![navaid-vor](navaid-vor.svg) | `navaid-vor.svg` | VHF Omnidirectional Range (VOR) |
19
+ | ![navaid-waypoint](navaid-waypoint.svg) | `navaid-waypoint.svg` | Waypoint |
20
+
21
+ Feel free to add or update icons as needed for the project.
22
+
23
+ ---
24
+
25
+ Back to [main documentation](../README.md)
@@ -1,4 +1,3 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
- <svg width="15" height="15" version="1.1" viewBox="0 0 3.9687 3.9688" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="3.9688" height="3.9687" ry=".52917" fill="#fff" fill-rule="evenodd" stroke-width=".26458"/><rect x=".26458" y=".26458" width="3.4396" height="3.4396" ry=".26458" fill="#5f5fd3" fill-rule="evenodd" stroke-width=".26458"/><path d="m3.4396 1.8521v0.32632l-1.2612-0.19403-0.06174 0.92604 0.54681 0.33514v0.19403l-0.6791-0.13229-0.6791 0.13229v-0.19403l0.54681-0.33514-0.06174-0.92604-1.2612 0.19403v-0.32632l1.2612-0.4498v-0.58208s0-0.29104 0.19403-0.29104 0.19403 0.29104 0.19403 0.29104v0.54681z" fill="#f9f9f9" stroke-width=".19403"/></svg>
4
- p
3
+ <svg width="15" height="15" version="1.1" viewBox="0 0 3.9687 3.9688" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="3.9688" height="3.9687" ry=".52917" fill="#fff" fill-rule="evenodd" stroke-width=".26458"/><rect x=".26458" y=".26458" width="3.4396" height="3.4396" ry=".26458" fill="#5f5fd3" fill-rule="evenodd" stroke-width=".26458"/><path d="m3.4396 1.8521v0.32632l-1.2612-0.19403-0.06174 0.92604 0.54681 0.33514v0.19403l-0.6791-0.13229-0.6791 0.13229v-0.19403l0.54681-0.33514-0.06174-0.92604-1.2612 0.19403v-0.32632l1.2612-0.4498v-0.58208s0-0.29104 0.19403-0.29104 0.19403 0.29104 0.19403 0.29104v0.54681z" fill="#f9f9f9" stroke-width=".19403"/></svg>
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@fboes/aerofly-data",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "This project contains data sets for airports and aircraft present in Aerofly FS 4.",
5
5
  "main": "build.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1",
7
+ "test": "echo \"Error: no test specified\"",
8
8
  "fetch-csv": "curl -o tmp/airports.csv https://davidmegginson.github.io/ourairports-data/airports.csv",
9
9
  "eslint": "npx eslint **/*.js --fix",
10
10
  "prettier": "npx prettier --cache --write .",
@@ -0,0 +1,288 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "path";
3
+
4
+ /**
5
+ * @typedef AeroflyAircraftParsed
6
+ * @type {{
7
+ * name: string,
8
+ * nameFull: string,
9
+ * icaoCode: string,
10
+ * tags: string[],
11
+ * approachAirspeedKts: number,
12
+ * cruiseAltitudeFt: number,
13
+ * cruiseSpeedKts: number,
14
+ * maximumRangeNm: number,
15
+ * }}
16
+ */
17
+ /**
18
+ * @typedef AeroflyAircraft
19
+ * @type {AeroflyAircraftParsed & {
20
+ * aeroflyCode: string,
21
+ * liveries: {
22
+ * aeroflyCode: string,
23
+ * name: string,
24
+ * }[],
25
+ * }}
26
+ */
27
+
28
+ /**
29
+ *
30
+ * @param {string} directory
31
+ * @returns {AeroflyAircraft[]}
32
+ */
33
+ export const getAeroflyAircraft = (directory) => {
34
+ return fs
35
+ .readdirSync(directory, { withFileTypes: true })
36
+ .filter((dirent) => dirent.isDirectory())
37
+ .sort()
38
+ .map((dirent) => {
39
+ const tmdFileContent = fs.readFileSync(
40
+ path.join(dirent.parentPath, dirent.name, dirent.name + ".tmc"),
41
+ "utf8"
42
+ );
43
+
44
+ const tmdOptionFileContent = fs.readFileSync(
45
+ path.join(dirent.parentPath, dirent.name, "option.tmc"),
46
+ "utf8"
47
+ );
48
+
49
+ const liveries = [
50
+ {
51
+ aeroflyCode: "default",
52
+ name: parseTmdLine(tmdOptionFileContent, "Description"),
53
+ },
54
+ ...fs
55
+ .readdirSync(path.join(dirent.parentPath, dirent.name), {
56
+ withFileTypes: true,
57
+ })
58
+ .filter((dirent) => dirent.isDirectory())
59
+ .filter((dirent) =>
60
+ fs.existsSync(
61
+ path.join(dirent.parentPath, dirent.name, "preview.ttx")
62
+ )
63
+ )
64
+ .sort()
65
+ .map((dirent) => {
66
+ const tmdFileContent = fs.readFileSync(
67
+ path.join(dirent.parentPath, dirent.name, "option.tmc"),
68
+ "utf8"
69
+ );
70
+
71
+ return {
72
+ aeroflyCode: dirent.name,
73
+ name: parseTmdLine(tmdFileContent, "Description"),
74
+ };
75
+ }),
76
+ ];
77
+
78
+ return {
79
+ ...parseAircraft(tmdFileContent),
80
+ aeroflyCode: dirent.name,
81
+ liveries,
82
+ };
83
+ });
84
+ };
85
+
86
+ /**
87
+ * @param {string} tmdFileContent
88
+ * @returns {AeroflyAircraftParsed}
89
+ */
90
+ export const parseAircraft = (tmdFileContent) => {
91
+ const tags = parseTmdLine(tmdFileContent, "Tags").trim().split(" ");
92
+
93
+ // type: ;
94
+ return {
95
+ name: parseTmdLine(tmdFileContent, "DisplayName"),
96
+ nameFull: parseTmdLine(tmdFileContent, "DisplayNameFull"),
97
+ icaoCode: parseTmdLine(tmdFileContent, "ICAO"),
98
+ tags,
99
+ /*MinimumAirspeed: convertSpeed(
100
+ parseTmdLine(tmdFileContent, "MinimumAirspeed")
101
+ ),*/
102
+ approachAirspeedKts: convertSpeed(
103
+ parseTmdLine(tmdFileContent, "ApproachAirspeed")
104
+ ),
105
+ /*CruiseAirspeed: convertSpeed(
106
+ parseTmdLine(tmdFileContent, "CruiseAirspeed")
107
+ ),*/
108
+ cruiseAltitudeFt: convertAltitude(
109
+ parseTmdLine(tmdFileContent, "CruiseAltitude")
110
+ ),
111
+ cruiseSpeedKts: convertSpeed(parseTmdLine(tmdFileContent, "CruiseSpeed")),
112
+ /*MaximumAirspeed: convertSpeed(
113
+ parseTmdLine(tmdFileContent, "MaximumAirspeed")
114
+ ),
115
+ MaximumAltitude: convertAltitude(
116
+ parseTmdLine(tmdFileContent, "MaximumAltitude")
117
+ ),
118
+ MaximumSpeed: convertSpeed(parseTmdLine(tmdFileContent, "MaximumSpeed")),*/
119
+ maximumRangeNm: convertDistance(
120
+ parseTmdLine(tmdFileContent, "MaximumRange")
121
+ ),
122
+ /*FlapAirspeedRange: parseTmdLine(tmdFileContent, "FlapAirspeedRange")
123
+ .trim()
124
+ .split(" ")
125
+ .map((v) => convertSpeed(v)),
126
+ NormalAirspeedRange: parseTmdLine(tmdFileContent, "NormalAirspeedRange")
127
+ .trim()
128
+ .split(" ")
129
+ .map((v) => convertSpeed(v)),
130
+ CautionAirspeedRange: parseTmdLine(tmdFileContent, "CautionAirspeedRange")
131
+ .trim()
132
+ .split(" ")
133
+ .map((v) => convertSpeed(v)),*/
134
+ };
135
+ };
136
+
137
+ /**
138
+ *
139
+ * @param {string} tmdFileContent
140
+ * @param {string} key
141
+ * @returns {string}
142
+ */
143
+ export const parseTmdLine = (tmdFileContent, key) => {
144
+ const r = new RegExp("\\[" + key + "\\]\\s*\\[(.+?)\\]");
145
+ const match = tmdFileContent.match(r);
146
+ return match && match[1] ? match[1] : "";
147
+ };
148
+
149
+ /**
150
+ *
151
+ * @param {string} speed in m/s
152
+ * @returns {number} in kts
153
+ */
154
+ export const convertSpeed = (speed) => {
155
+ return Math.round(Number(speed) * 1.94384449);
156
+ };
157
+
158
+ /**
159
+ *
160
+ * @param {string} altitude in m
161
+ * @returns {number} in ft
162
+ */
163
+ export const convertAltitude = (altitude) => {
164
+ return Math.round(Number(altitude) * 3.28084);
165
+ };
166
+
167
+ /**
168
+ *
169
+ * @param {string} range in m
170
+ * @returns {number} in kts
171
+ */
172
+ export const convertDistance = (range) => {
173
+ return Math.round(Number(range) / 1852);
174
+ };
175
+
176
+ /**
177
+ *
178
+ * @param {AeroflyAircraft[]} sortedAircraft
179
+ * @returns {string}
180
+ */
181
+ export const getSelectOptions = (sortedAircraft) => {
182
+ const selectContent = sortedAircraft
183
+ .map((aircraft) => {
184
+ return ` <option value="${aircraft.aeroflyCode}">${aircraft.nameFull}</option>`;
185
+ })
186
+ .join("\n");
187
+
188
+ return `\
189
+ <select id="aircraft-select">
190
+ ${selectContent}
191
+ </select>
192
+ `;
193
+ };
194
+
195
+ /**
196
+ *
197
+ * @param {AeroflyAircraft[]} sortedAircraft
198
+ * @returns {string}
199
+ */
200
+ export const getSelectOptgroupOptions = (sortedAircraft) => {
201
+ /**
202
+ * @type {{
203
+ * [key: string]: {
204
+ * label: string,
205
+ * options: { value: string; label: string }[]
206
+ * }
207
+ * }}
208
+ */
209
+ const options = {
210
+ airliner: {
211
+ label: "Airliner",
212
+ options: [],
213
+ },
214
+ helicopter: {
215
+ label: "Helicopters",
216
+ options: [],
217
+ },
218
+ general_aviation: {
219
+ label: "General Aviation",
220
+ options: [],
221
+ },
222
+ historical: {
223
+ label: "Historical Aircraft",
224
+ options: [],
225
+ },
226
+ military: {
227
+ label: "Military Aircraft",
228
+ options: [],
229
+ },
230
+ aerobatic: {
231
+ label: "Aerobatic aircraft",
232
+ options: [],
233
+ },
234
+ glider: {
235
+ label: "Gliders",
236
+ options: [],
237
+ },
238
+ };
239
+
240
+ for (const aircraft of sortedAircraft) {
241
+ const o = {
242
+ value: aircraft.aeroflyCode,
243
+ label: aircraft.nameFull,
244
+ };
245
+
246
+ if (aircraft.tags.includes("historical")) {
247
+ options.historical.options.push(o);
248
+ } else if (aircraft.tags.includes("airliner")) {
249
+ options.airliner.options.push(o);
250
+ } else if (aircraft.tags.includes("helicopter")) {
251
+ options.helicopter.options.push(o);
252
+ } else if (aircraft.tags.includes("military")) {
253
+ options.military.options.push(o);
254
+ } else if (aircraft.tags.includes("glider")) {
255
+ options.glider.options.push(o);
256
+ } else if (aircraft.tags.includes("aerobatic")) {
257
+ options.aerobatic.options.push(o);
258
+ } else {
259
+ options.general_aviation.options.push(o);
260
+ }
261
+ }
262
+
263
+ let html = "";
264
+ for (const optgroup of [
265
+ options.airliner,
266
+ options.general_aviation,
267
+ options.military,
268
+ options.historical,
269
+ options.helicopter,
270
+ options.aerobatic,
271
+ options.glider,
272
+ ]) {
273
+ if (optgroup.options.length === 0) {
274
+ continue;
275
+ }
276
+ html += ` <optgroup label="${optgroup.label}">` + "\n";
277
+ for (const option of optgroup.options) {
278
+ html += ` <option value="${option.value}">${option.label}</option>` + "\n";
279
+ }
280
+ html += " </optgroup>\n";
281
+ }
282
+
283
+ return `\
284
+ <select id="aircraft-select-optgroup">
285
+ ${html}\
286
+ </select>
287
+ `;
288
+ };
@@ -0,0 +1,63 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "path";
3
+
4
+ /**
5
+ *
6
+ * @param {string} type
7
+ * @param {boolean} isMilitary
8
+ * @param {number|undefined} lenght in Bytes
9
+ * @returns {string}
10
+ */
11
+ export const geoJsonType = (type, isMilitary, lenght) => {
12
+ if (type === "heliport") {
13
+ return type;
14
+ }
15
+
16
+ if (lenght !== undefined) {
17
+ let size = "closed";
18
+ if (lenght > 1200) {
19
+ size = "large";
20
+ } else if (lenght > 1050) {
21
+ size = "medium";
22
+ } else if (lenght > 920) {
23
+ size = "small";
24
+ }
25
+
26
+ if (size === "closed") {
27
+ return size;
28
+ }
29
+
30
+ return size + "_" + (isMilitary ? "airbase" : "airport");
31
+ }
32
+
33
+ return type;
34
+ };
35
+
36
+ /**
37
+ *
38
+ * @param {string} directory
39
+ * @param {RegExp?} icaoFilter
40
+ * @returns {Map<string,number>}
41
+ */
42
+ export const getAeroflyAirports = (directory, icaoFilter) => {
43
+ const aeroflyAirports = new Map();
44
+ let maxLength = 0;
45
+ let minLength = 10000;
46
+
47
+ const files = fs
48
+ .readdirSync(directory)
49
+ .filter((fn) => fn.endsWith(".wad"))
50
+ .sort();
51
+
52
+ for (const file of files) {
53
+ const icaoCode = file.replace(/\.wad$/, "").toUpperCase();
54
+ if (!icaoFilter || icaoCode.match(icaoFilter)) {
55
+ const stats = fs.statSync(path.join(directory, file));
56
+ maxLength = Math.max(maxLength, stats.size);
57
+ minLength = Math.min(minLength, stats.size);
58
+ aeroflyAirports.set(icaoCode, stats.size);
59
+ }
60
+ }
61
+
62
+ return aeroflyAirports;
63
+ };