@fboes/aerofly-data 1.0.0 → 1.1.0
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/README.md +1 -1
- package/data/aircraft-liveries.json +0 -0
- package/data/aircraft-select-optgroup.html +55 -0
- package/data/aircraft-select.html +0 -0
- package/data/aircraft.json +0 -0
- package/data/aircraft.md +20 -20
- package/data/airport-list.json +0 -0
- package/data/airports.geojson +0 -0
- package/dist/index.html +161 -49
- package/get-aircraft.js +34 -191
- package/get-airports.js +1 -63
- package/package.json +2 -2
- package/src/aircraft-functions.js +288 -0
- package/src/airport-functions.js +63 -0
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ The `data` directory contains the following files:
|
|
|
17
17
|
| [`airport-list.json`](./data/airport-list.json) | JSON file containing an array of all ICAO codes. |
|
|
18
18
|
| [`airports.geojson`](./data/airports.geojson) | GeoJSON file containing the location of all airports in Aerofly FS 4. |
|
|
19
19
|
|
|
20
|
-
[](https://fboes.github.io/aerofly-data/dist/)
|
|
20
|
+
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.
|
|
21
21
|
|
|
22
22
|
## Building
|
|
23
23
|
|
|
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
|
package/data/aircraft.json
CHANGED
|
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
|
-
|
|
|
28
|
-
|
|
|
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
|
-
|
|
|
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
|
-
|
|
|
37
|
+
| Robin DR400 | `DR40` | `dr400` | 62 | 5991 | 167 | 586 |
|
|
41
38
|
| Robinson R22 Beta II | `R22` | `r22` | 60 | 6562 | 105 | 208 |
|
|
42
|
-
|
|
|
43
|
-
|
|
|
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 |
|
package/data/airport-list.json
CHANGED
|
File without changes
|
package/data/airports.geojson
CHANGED
|
File without changes
|
package/dist/index.html
CHANGED
|
@@ -1,53 +1,165 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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: Arial, sans-serif;
|
|
21
|
+
margin: 20px;
|
|
22
|
+
display: flex;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
align-items: center;
|
|
25
|
+
min-height: 100vh;
|
|
26
|
+
}
|
|
27
|
+
main {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
text-align: center;
|
|
33
|
+
padding-bottom: 2em;
|
|
34
|
+
}
|
|
35
|
+
h1 {
|
|
36
|
+
font-size: 1.8em;
|
|
37
|
+
margin-bottom: 0.75em;
|
|
38
|
+
border-bottom: 1px solid #ccc;
|
|
39
|
+
padding: 0 1em 0.25em;
|
|
40
|
+
}
|
|
41
|
+
label {
|
|
42
|
+
display: block;
|
|
43
|
+
margin-bottom: 1em;
|
|
44
|
+
}
|
|
45
|
+
input {
|
|
46
|
+
font: inherit;
|
|
47
|
+
padding: 0.25em;
|
|
48
|
+
border-radius: 0.25em;
|
|
49
|
+
width: 6em;
|
|
50
|
+
}
|
|
51
|
+
.inputbox {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
gap: 0.5em;
|
|
55
|
+
font-size: 1.25em;
|
|
56
|
+
}
|
|
57
|
+
#status {
|
|
58
|
+
font-size: 1.25em;
|
|
59
|
+
}
|
|
60
|
+
#status2, p {
|
|
61
|
+
margin-top: 1em;
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
</head>
|
|
65
|
+
<body>
|
|
66
|
+
<main>
|
|
67
|
+
<h1>Aerofly FS Airport Code Validator</h1>
|
|
68
|
+
<label for="icaoInput"
|
|
69
|
+
>Check if an airport is present in Aerofly FS 4 using its ICAO
|
|
70
|
+
code:</label
|
|
71
|
+
>
|
|
72
|
+
<div class="inputbox">
|
|
73
|
+
<input
|
|
74
|
+
type="text"
|
|
75
|
+
id="icaoInput"
|
|
76
|
+
list="icaoInputList"
|
|
77
|
+
placeholder="ICAO code"
|
|
78
|
+
pattern="A-Za-z0-9"
|
|
79
|
+
autocapitalize="characters"
|
|
80
|
+
autofocus="autofocus"
|
|
81
|
+
/>
|
|
82
|
+
<datalist id="icaoInputList">
|
|
83
|
+
<option value="KATL">KATL</option>
|
|
84
|
+
<option value="KLAX">KLAX</option>
|
|
85
|
+
<option value="EGLL">EGLL</option>
|
|
86
|
+
<option value="OMDB">OMDB</option>
|
|
87
|
+
<option value="RJTT">RJTT</option>
|
|
88
|
+
</datalist>
|
|
89
|
+
<span
|
|
90
|
+
id="status"
|
|
91
|
+
title="Enter an ICAO code to check if the airport is present in Aerofly FS 4"
|
|
92
|
+
>❓</span
|
|
93
|
+
>
|
|
94
|
+
</div>
|
|
95
|
+
<div id="status2">
|
|
96
|
+
Enter an ICAO code to check if the airport is present in Aerofly FS 4
|
|
97
|
+
</div>
|
|
98
|
+
<p>Data source: <a href="..">Aerofly FS 4 data repository</a>.</p>
|
|
99
|
+
</main>
|
|
24
100
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
101
|
+
<script>
|
|
102
|
+
// Load the airport list from the JSON file
|
|
103
|
+
let airportList = [];
|
|
104
|
+
fetch("../data/airport-list.json")
|
|
105
|
+
.then((response) => response.json())
|
|
106
|
+
.then((data) => {
|
|
107
|
+
airportList = data;
|
|
108
|
+
})
|
|
109
|
+
.catch((error) => {
|
|
110
|
+
console.error("Error loading airport list:", error);
|
|
111
|
+
});
|
|
36
112
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
113
|
+
// Validate the input
|
|
114
|
+
const inputField = document.getElementById("icaoInput");
|
|
115
|
+
const status = document.getElementById("status");
|
|
116
|
+
const status2 = document.getElementById("status2");
|
|
40
117
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
118
|
+
inputField.addEventListener("input", () => {
|
|
119
|
+
const inputValue = inputField.value.toUpperCase();
|
|
120
|
+
|
|
121
|
+
// Autocomplete: Populate datalist if input has 2+ chars
|
|
122
|
+
const datalist = document.getElementById("icaoInputList");
|
|
123
|
+
if (inputValue.length >= 2) {
|
|
124
|
+
// Filter airportList for codes starting with inputValue
|
|
125
|
+
const filtered = airportList.filter((code) =>
|
|
126
|
+
code.startsWith(inputValue)
|
|
127
|
+
);
|
|
128
|
+
// Clear existing options
|
|
129
|
+
datalist.innerHTML = "";
|
|
130
|
+
// Add filtered options
|
|
131
|
+
filtered.forEach((code) => {
|
|
132
|
+
const option = document.createElement("option");
|
|
133
|
+
option.value = code;
|
|
134
|
+
datalist.appendChild(option);
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
// Restore default options if input < 2 chars
|
|
138
|
+
datalist.innerHTML = `
|
|
139
|
+
<option value="KATL">KATL</option>
|
|
140
|
+
<option value="KLAX">KLAX</option>
|
|
141
|
+
<option value="EGLL">EGLL</option>
|
|
142
|
+
<option value="OMDB">OMDB</option>
|
|
143
|
+
<option value="RJTT">RJTT</option>
|
|
144
|
+
`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (airportList.includes(inputValue)) {
|
|
148
|
+
status.textContent = "✅"; // Green checkmark
|
|
149
|
+
status.title = "Airport is present in Aerofly FS 4";
|
|
150
|
+
status2.textContent = "Airport is present in Aerofly FS 4";
|
|
151
|
+
} else if (inputValue.trim() === "") {
|
|
152
|
+
status.textContent = "❓"; // Question mark for empty input
|
|
153
|
+
status.title =
|
|
154
|
+
"Enter an ICAO code to check if the airport is present in Aerofly FS 4";
|
|
155
|
+
status2.textContent =
|
|
156
|
+
"Enter an ICAO code to check if the airport is present in Aerofly FS 4";
|
|
157
|
+
} else {
|
|
158
|
+
status.textContent = "🚫"; // Red forbidden sign
|
|
159
|
+
status.title = "Airport is NOT present in Aerofly FS 4";
|
|
160
|
+
status2.textContent = "Airport is NOT present in Aerofly FS 4";
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
</script>
|
|
164
|
+
</body>
|
|
165
|
+
</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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 +=
|
|
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
|
|
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();
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fboes/aerofly-data",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
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\"
|
|
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.helicopter,
|
|
267
|
+
options.general_aviation,
|
|
268
|
+
options.historical,
|
|
269
|
+
options.military,
|
|
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
|
+
};
|