Initial NG
This commit is contained in:
1 parent
e4ed55cac9
commit
f593cbd29a
63 files changed
+929
-455
No files matched your search
@@ -21,6 +21,12 @@ export default tseslint.config([
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
destructuredArrayIgnorePattern: '^_+$',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -4,18 +4,21 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --port 3000 --host",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint . --ext .ts,.tsx --fix",
|
||||
"preview": "vite preview",
|
||||
"parser": "node --experimental-strip-types src/parser/node.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"browser-image-manipulation": "^0.4.0",
|
||||
"geojson": "^0.5.0",
|
||||
"geolib": "^3.3.4",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet-svg-shape-markers": "^1.4.0",
|
||||
"magvar": "^2.0.0",
|
||||
"navigraph": "^1.4.1",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-leaflet": "^5.0.0",
|
||||
@@ -25,7 +28,6 @@
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"@types/node": "^24.0.14",
|
||||
"@types/object-hash": "^3.0.6",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
|
||||
Generated
+137
-4
@@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
browser-image-manipulation:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0
|
||||
geojson:
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0
|
||||
@@ -23,6 +26,12 @@ importers:
|
||||
magvar:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
navigraph:
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1
|
||||
qrcode.react:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0(react@19.1.0)
|
||||
react:
|
||||
specifier: ^19.1.0
|
||||
version: 19.1.0
|
||||
@@ -45,9 +54,6 @@ importers:
|
||||
'@types/leaflet':
|
||||
specifier: ^1.9.20
|
||||
version: 1.9.20
|
||||
'@types/node':
|
||||
specifier: ^24.0.14
|
||||
version: 24.0.14
|
||||
'@types/object-hash':
|
||||
specifier: ^3.0.6
|
||||
version: 3.0.6
|
||||
@@ -331,6 +337,25 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.29':
|
||||
resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
|
||||
|
||||
'@navigraph/amdb@1.1.0':
|
||||
resolution: {integrity: sha512-/LhFxZckcOfreVHDWJh14jp+GUkeN7rIpZmUig5ttrABrvj9kQpMj6I9j1sjA5Rt3qaky2TJeReNKgOKLEHtdg==}
|
||||
|
||||
'@navigraph/app@1.4.0':
|
||||
resolution: {integrity: sha512-TS7G1Qx9Le7RkwkL6CfDVqBjYj+a2m3jOlNrF5yVbXkuo285xoCg4jt1kdueXefolxWS4Q1kP50WNlZu23vGEw==}
|
||||
|
||||
'@navigraph/auth@2.6.0':
|
||||
resolution: {integrity: sha512-/YNckuJeoKvwe/ujLasMbrvVwEYxc/AnOdlb4ioI2Q/f48At3Zp9L5mGtHD7MwpdiFyGbIg8IHnmJkz1pRCh4w==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
'@navigraph/charts@2.1.0':
|
||||
resolution: {integrity: sha512-SOjNjmCe2aqUxA9wz85iVhIdv7qY+gTbrxwkGLVSBFeDTb3aqywesWygpCsb8VsxLIVnP/ePvgEoPRbthZFOAw==}
|
||||
|
||||
'@navigraph/packages@1.1.0':
|
||||
resolution: {integrity: sha512-0rTTs+DuamcyXVKzqCOp+a9KMVhZUwtvGUWqmtC+AfgZRCQ63TvFyjTWaXdyeodbaxf02qpa3I5KK8qGNPWHgg==}
|
||||
|
||||
'@navigraph/weather@2.1.0':
|
||||
resolution: {integrity: sha512-fa5u69o9cJQKEL7AnI+UF7uT67zaLrgF7OpDhWCcQ/+Ax8NxuS9nIjZoyc2VnZ5XIZ2oVcNoKhC3s76NKe83xA==}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -731,6 +756,9 @@ packages:
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
blueimp-canvas-to-blob@3.29.0:
|
||||
resolution: {integrity: sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||
|
||||
@@ -741,6 +769,9 @@ packages:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
browser-image-manipulation@0.4.0:
|
||||
resolution: {integrity: sha512-SYNoVBnbozWA8jIcQJUShYYHpoEimONcoEnYBuDEUvcXgJT6042oTb+zkQry1RN/MmU8jd8ssMOp2RrNPky8VQ==}
|
||||
|
||||
callsites@3.1.0:
|
||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -923,6 +954,9 @@ packages:
|
||||
resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
glur@1.1.2:
|
||||
resolution: {integrity: sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA==}
|
||||
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
@@ -1102,6 +1136,9 @@ packages:
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
multimath@2.0.0:
|
||||
resolution: {integrity: sha512-toRx66cAMJ+Ccz7pMIg38xSIrtnbozk0dchXezwQDMgQmbGpfxjtv68H+L00iFL8hxDaVjrmwAFSb3I6bg8Q2g==}
|
||||
|
||||
nanoid@3.3.11:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
@@ -1110,6 +1147,13 @@ packages:
|
||||
natural-compare@1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
|
||||
navigraph@1.4.1:
|
||||
resolution: {integrity: sha512-cmgDLx12zI6/1jt81NCeqo1II+gqsPTIe3iLQGqj4NNAwzy/7x2C94DcMcq5xWIKL2jmkprD08pnmaKeMCSdkg==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
object-hash@3.0.0:
|
||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -1138,6 +1182,9 @@ packages:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
pica@9.0.1:
|
||||
resolution: {integrity: sha512-v0U4vY6Z3ztz9b4jBIhCD3WYoecGXCQeCsYep+sXRefViL+mVVoTL+wqzdPeE+GpBFsRUtQZb6dltvAt2UkMtQ==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
@@ -1149,6 +1196,9 @@ packages:
|
||||
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
piexifjs@1.0.6:
|
||||
resolution: {integrity: sha512-0wVyH0cKohzBQ5Gi2V1BuxYpxWfxF3cSqfFXfPIpl5tl9XLS5z4ogqhUCD20AbHi0h9aJkqXNJnkVev6gwh2ag==}
|
||||
|
||||
postcss@8.5.6:
|
||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -1237,6 +1287,11 @@ packages:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
qrcode.react@4.2.0:
|
||||
resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
@@ -1292,6 +1347,9 @@ packages:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
stackblur-canvas@2.2.0:
|
||||
resolution: {integrity: sha512-5Gf8dtlf8k6NbLzuly2NkGrkS/Ahh+I5VUjO7TnFizdJtgpfpLLEdQlLe9umbcnZlitU84kfYjXE67xlSXfhfQ==}
|
||||
|
||||
strip-json-comments@3.1.1:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1387,6 +1445,9 @@ packages:
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
webworkify@1.5.0:
|
||||
resolution: {integrity: sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==}
|
||||
|
||||
which@2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -1564,6 +1625,32 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.4
|
||||
|
||||
'@navigraph/amdb@1.1.0':
|
||||
dependencies:
|
||||
'@navigraph/app': 1.4.0
|
||||
'@navigraph/auth': 2.6.0
|
||||
|
||||
'@navigraph/app@1.4.0': {}
|
||||
|
||||
'@navigraph/auth@2.6.0':
|
||||
dependencies:
|
||||
'@navigraph/app': 1.4.0
|
||||
|
||||
'@navigraph/charts@2.1.0':
|
||||
dependencies:
|
||||
'@navigraph/app': 1.4.0
|
||||
'@navigraph/auth': 2.6.0
|
||||
|
||||
'@navigraph/packages@1.1.0':
|
||||
dependencies:
|
||||
'@navigraph/app': 1.4.0
|
||||
'@navigraph/auth': 2.6.0
|
||||
|
||||
'@navigraph/weather@2.1.0':
|
||||
dependencies:
|
||||
'@navigraph/app': 1.4.0
|
||||
'@navigraph/auth': 2.6.0
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
@@ -1780,6 +1867,7 @@ snapshots:
|
||||
'@types/node@24.0.14':
|
||||
dependencies:
|
||||
undici-types: 7.8.0
|
||||
optional: true
|
||||
|
||||
'@types/object-hash@3.0.6': {}
|
||||
|
||||
@@ -1913,6 +2001,8 @@ snapshots:
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
blueimp-canvas-to-blob@3.29.0: {}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
@@ -1926,6 +2016,13 @@ snapshots:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
browser-image-manipulation@0.4.0:
|
||||
dependencies:
|
||||
blueimp-canvas-to-blob: 3.29.0
|
||||
pica: 9.0.1
|
||||
piexifjs: 1.0.6
|
||||
stackblur-canvas: 2.2.0
|
||||
|
||||
callsites@3.1.0: {}
|
||||
|
||||
chalk@4.1.2:
|
||||
@@ -2133,6 +2230,8 @@ snapshots:
|
||||
|
||||
globals@16.3.0: {}
|
||||
|
||||
glur@1.1.2: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
graphemer@1.4.0: {}
|
||||
@@ -2267,10 +2366,26 @@ snapshots:
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
multimath@2.0.0:
|
||||
dependencies:
|
||||
glur: 1.1.2
|
||||
object-assign: 4.1.1
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
navigraph@1.4.1:
|
||||
dependencies:
|
||||
'@navigraph/amdb': 1.1.0
|
||||
'@navigraph/app': 1.4.0
|
||||
'@navigraph/auth': 2.6.0
|
||||
'@navigraph/charts': 2.1.0
|
||||
'@navigraph/packages': 1.1.0
|
||||
'@navigraph/weather': 2.1.0
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-hash@3.0.0: {}
|
||||
|
||||
optionator@0.9.4:
|
||||
@@ -2298,12 +2413,21 @@ snapshots:
|
||||
|
||||
path-key@3.1.1: {}
|
||||
|
||||
pica@9.0.1:
|
||||
dependencies:
|
||||
glur: 1.1.2
|
||||
multimath: 2.0.0
|
||||
object-assign: 4.1.1
|
||||
webworkify: 1.5.0
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
picomatch@4.0.2: {}
|
||||
|
||||
piexifjs@1.0.6: {}
|
||||
|
||||
postcss@8.5.6:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
@@ -2327,6 +2451,10 @@ snapshots:
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
qrcode.react@4.2.0(react@19.1.0):
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
react-dom@19.1.0(react@19.1.0):
|
||||
@@ -2389,6 +2517,8 @@ snapshots:
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
stackblur-canvas@2.2.0: {}
|
||||
|
||||
strip-json-comments@3.1.1: {}
|
||||
|
||||
supports-color@7.2.0:
|
||||
@@ -2438,7 +2568,8 @@ snapshots:
|
||||
|
||||
typescript@5.8.3: {}
|
||||
|
||||
undici-types@7.8.0: {}
|
||||
undici-types@7.8.0:
|
||||
optional: true
|
||||
|
||||
uri-js@4.4.1:
|
||||
dependencies:
|
||||
@@ -2458,6 +2589,8 @@ snapshots:
|
||||
jiti: 2.4.2
|
||||
lightningcss: 1.30.1
|
||||
|
||||
webworkify@1.5.0: {}
|
||||
|
||||
which@2.0.2:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1 @@
|
||||
Copy over contents of `Data/Primary` retaining the structure.
|
||||
+65
-32
@@ -1,44 +1,77 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Map } from './components/map/Map';
|
||||
import type { DeviceFlowParams } from 'navigraph/auth';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import { useState } from 'react';
|
||||
import { ProcedureSelect } from './components//ProcedureSelect';
|
||||
import { Map } from './components/Map';
|
||||
import { useNavigraphAuth } from './hooks/useNavigraphAuth';
|
||||
import Parser from './parser/parser';
|
||||
|
||||
const parser = await Parser.instance();
|
||||
|
||||
const terminals = [10394, 10395, 10475, 10480, 10482, 10485, 10653, 10654, 10657, 10659, 10679, 11798, 11909, 12765];
|
||||
|
||||
function App() {
|
||||
const [selectedTerminal, setSelectedTerminal] = useState(terminals[0]);
|
||||
const [procedures, setProcedures] = useState<object[]>([]);
|
||||
const [selectedAirport, setSelectedAirport] = useState<Airport>();
|
||||
const [selectedRunway, setSelectedRunway] = useState<Runway>();
|
||||
const [selectedTerminal, setSelectedTerminal] = useState<Terminal>();
|
||||
const [procedures, setProcedures] = useState<{ name: string; data: object }[]>([]);
|
||||
const [params, setParams] = useState<DeviceFlowParams | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setProcedures(await parser.parse(selectedTerminal));
|
||||
})();
|
||||
}, [selectedTerminal]);
|
||||
const { user, signIn, initialized } = useNavigraphAuth();
|
||||
|
||||
const handleSignIn = () => signIn((p) => setParams(p));
|
||||
|
||||
return (
|
||||
<div className="flex h-dvh w-dvw">
|
||||
<Map procedures={procedures} />
|
||||
<div className="w-[200px] overflow-y-auto">
|
||||
<div className="flex flex-col gap-1 p-1">
|
||||
{terminals.map((terminal) => (
|
||||
<div
|
||||
key={terminal}
|
||||
className={`flex cursor-pointer flex-col border bg-gray-300 p-1 ${selectedTerminal === terminal ? 'border-black' : 'border-gray-300'}`}
|
||||
onClick={() => setSelectedTerminal(terminal)}
|
||||
>
|
||||
<span className="whitespace-nowrap">
|
||||
{(() => {
|
||||
const t = parser.terminals.find(({ ID }) => ID === terminal);
|
||||
return `${t?.ICAO} - ${t?.FullName}`;
|
||||
})()}
|
||||
</span>
|
||||
<pre>({terminal})</pre>
|
||||
</div>
|
||||
))}
|
||||
<>
|
||||
{procedures.length === 0 ? (
|
||||
<div className="flex min-h-dvh w-full">
|
||||
{!initialized && <div>Loading...</div>}
|
||||
|
||||
{initialized && !params && !user && <button onClick={handleSignIn}>Sign in</button>}
|
||||
|
||||
{params?.verification_uri_complete && !user && (
|
||||
<>
|
||||
<QRCodeSVG value={params.verification_uri_complete} size={250} />
|
||||
<a href={params.verification_uri_complete} target="_blank" rel="noreferrer">
|
||||
Open sign in page
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
{user && (
|
||||
<ProcedureSelect
|
||||
selectedAirport={selectedAirport}
|
||||
selectedRunway={selectedRunway}
|
||||
selectedTerminal={selectedTerminal}
|
||||
setSelectedAirport={setSelectedAirport}
|
||||
setSelectedRunway={setSelectedRunway}
|
||||
setSelectedTerminal={setSelectedTerminal}
|
||||
handleSelection={(selectedTransitions) =>
|
||||
setProcedures(
|
||||
selectedTransitions.map((transition) => ({
|
||||
name: transition,
|
||||
data: parser.parse(selectedRunway!, transition),
|
||||
}))
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-dvh w-dvw">
|
||||
{procedures.length > 0 && selectedAirport && selectedTerminal ? (
|
||||
<Map
|
||||
airport={selectedAirport}
|
||||
terminal={selectedTerminal}
|
||||
procedures={procedures}
|
||||
backAction={() => {
|
||||
setSelectedTerminal(undefined);
|
||||
setProcedures([]);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<h1 className="text-center text-3xl">Error</h1>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import BrowserImageManipulation from 'browser-image-manipulation';
|
||||
import { default as L, type LatLngBoundsExpression } from 'leaflet';
|
||||
import 'leaflet-svg-shape-markers';
|
||||
import { type Chart } from 'navigraph/charts';
|
||||
import { createRef, useEffect, useState, type FC } from 'react';
|
||||
import { GeoJSON, ImageOverlay, MapContainer, TileLayer } from 'react-leaflet';
|
||||
import { charts } from '../lib/navigraph';
|
||||
|
||||
interface MapProps {
|
||||
airport: Airport;
|
||||
terminal: Terminal;
|
||||
procedures: { name: string; data: object }[];
|
||||
backAction: () => void;
|
||||
}
|
||||
|
||||
export const Map: FC<MapProps> = ({ airport, terminal, procedures, backAction }) => {
|
||||
const [selectedProcedure, setSelectedProcedure] = useState(procedures[0]);
|
||||
const [chartIndex, setChartIndex] = useState<Chart[]>([]);
|
||||
const [selectedChart, setSelectedChart] = useState<{
|
||||
data: string;
|
||||
index_number: string;
|
||||
bounds: LatLngBoundsExpression;
|
||||
}>();
|
||||
|
||||
const mapRef = createRef<L.Map>();
|
||||
const imageRef = createRef<L.ImageOverlay>();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setChartIndex((await charts.getChartsIndex({ icao: airport.ICAO, version: 'STD' })) ?? []);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className="fixed top-2 left-2 z-[5000] cursor-pointer rounded border border-red-500 bg-red-500 px-2 py-1 font-semibold text-stone-50 focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black"
|
||||
onClick={backAction}
|
||||
>
|
||||
Go back
|
||||
</button>
|
||||
|
||||
<MapContainer
|
||||
center={[airport.Latitude, airport.Longitude]}
|
||||
zoom={13}
|
||||
zoomSnap={0}
|
||||
className="h-full w-full"
|
||||
ref={(_mapRef) => {
|
||||
_mapRef?.attributionControl.setPosition('topright');
|
||||
_mapRef?.zoomControl.setPosition('topright');
|
||||
mapRef.current = _mapRef;
|
||||
}}
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
|
||||
{selectedChart && selectedChart.bounds && (
|
||||
<ImageOverlay
|
||||
url={selectedChart.data}
|
||||
bounds={selectedChart.bounds}
|
||||
opacity={0.75}
|
||||
ref={(_imageRef) => {
|
||||
if (_imageRef) {
|
||||
mapRef.current?.fitBounds(_imageRef.getBounds(), {
|
||||
padding: [-50, -50],
|
||||
});
|
||||
}
|
||||
imageRef.current = _imageRef;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<GeoJSON
|
||||
key={`${selectedProcedure.name}-lines`}
|
||||
data={selectedProcedure.data}
|
||||
style={({ properties }) => ({
|
||||
color: '#ff00ff',
|
||||
stroke: true,
|
||||
weight: 5,
|
||||
opacity: 1,
|
||||
dashArray: properties.isManual ? '20, 20' : undefined,
|
||||
})}
|
||||
filter={(feature) => feature.geometry.type !== 'Point'}
|
||||
/>
|
||||
<GeoJSON
|
||||
key={`${selectedProcedure.name}-points`}
|
||||
data={selectedProcedure.data}
|
||||
style={{
|
||||
color: 'black',
|
||||
fill: true,
|
||||
fillColor: 'transparent',
|
||||
stroke: true,
|
||||
weight: 3,
|
||||
}}
|
||||
pointToLayer={({ properties }, latlng) => {
|
||||
if (properties.isFlyOver)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'triangle',
|
||||
radius: 6,
|
||||
});
|
||||
if (properties.isIntersection) return L.circleMarker(latlng, { radius: 6 });
|
||||
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'star-4',
|
||||
radius: 10,
|
||||
rotation: 45,
|
||||
});
|
||||
}}
|
||||
onEachFeature={({ geometry, properties }, layer) => {
|
||||
if (geometry.type === 'Point') {
|
||||
layer.bindPopup(
|
||||
`${properties.name}<br>
|
||||
${properties.altitude} ft<br>
|
||||
${properties.speed} kts<br>
|
||||
CNSTR:
|
||||
${properties.altitudeConstraint ?? ''}
|
||||
${properties.speedConstraint ?? ''}<br>`
|
||||
);
|
||||
}
|
||||
}}
|
||||
filter={(feature) => feature.geometry.type === 'Point'}
|
||||
/>
|
||||
</MapContainer>
|
||||
|
||||
<div className="absolute right-0 bottom-0 left-0 z-[5000] bg-[#ffffff77] bg-blend-color backdrop-blur-xs">
|
||||
<div className="flex items-center gap-2 overflow-x-auto p-2">
|
||||
<span className="text-lg font-semibold">Procedures:</span>
|
||||
{procedures.map((procedure) => (
|
||||
<button
|
||||
key={procedure.name}
|
||||
className={`cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${selectedProcedure.name === procedure.name ? 'outline-2' : ''}`}
|
||||
onClick={() => {
|
||||
if (selectedProcedure.name === procedure.name) return;
|
||||
|
||||
setSelectedProcedure(procedure);
|
||||
}}
|
||||
>
|
||||
{procedure.name ? procedure.name : 'ZZZZ'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-lg font-semibold">Charts:</span>
|
||||
<div className="flex items-center gap-2 overflow-x-auto p-2">
|
||||
{chartIndex
|
||||
.filter((chart) => chart.is_georeferenced)
|
||||
.map((chart) => (
|
||||
<button
|
||||
key={chart.index_number}
|
||||
className={`cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold whitespace-nowrap focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${selectedChart?.index_number === chart.index_number ? 'outline-2' : ''}`}
|
||||
onClick={async () => {
|
||||
if (selectedChart?.index_number === chart.index_number) return;
|
||||
|
||||
if (!mapRef.current) return;
|
||||
|
||||
if (!chart.bounding_boxes) return;
|
||||
|
||||
const planView = chart.bounding_boxes.planview;
|
||||
|
||||
const chartImage = await charts.getChartImage({ chart, theme: 'light' });
|
||||
if (!chartImage) return;
|
||||
// Crop
|
||||
const dataURL = await new BrowserImageManipulation()
|
||||
.loadBlob(chartImage)
|
||||
.crop(
|
||||
planView.pixels.x2 - planView.pixels.x1,
|
||||
planView.pixels.y1 - planView.pixels.y2,
|
||||
planView.pixels.x1,
|
||||
planView.pixels.y2
|
||||
)
|
||||
.saveAsImage();
|
||||
|
||||
const bounds = new L.LatLngBounds(
|
||||
[planView.latlng.lat1, planView.latlng.lng1],
|
||||
[planView.latlng.lat2, planView.latlng.lng2]
|
||||
);
|
||||
|
||||
setSelectedChart({ data: dataURL, index_number: chart.index_number, bounds });
|
||||
|
||||
if (imageRef.current) {
|
||||
mapRef.current?.fitBounds(imageRef.current.getBounds(), {
|
||||
padding: [-50, -50],
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{chart.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
import { createRef, useMemo, useState, type Dispatch, type FC, type SetStateAction } from 'react';
|
||||
import Parser from '../parser/parser';
|
||||
|
||||
const parser = await Parser.instance();
|
||||
|
||||
interface ProcedureSelectProps {
|
||||
selectedAirport: Airport | undefined;
|
||||
selectedRunway: Runway | undefined;
|
||||
selectedTerminal: Terminal | undefined;
|
||||
setSelectedAirport: Dispatch<SetStateAction<Airport | undefined>>;
|
||||
setSelectedRunway: Dispatch<SetStateAction<Runway | undefined>>;
|
||||
setSelectedTerminal: Dispatch<SetStateAction<Terminal | undefined>>;
|
||||
handleSelection: (transitions: string[]) => void;
|
||||
}
|
||||
|
||||
export const ProcedureSelect: FC<ProcedureSelectProps> = ({
|
||||
selectedAirport,
|
||||
selectedRunway,
|
||||
selectedTerminal,
|
||||
setSelectedAirport,
|
||||
setSelectedRunway,
|
||||
setSelectedTerminal,
|
||||
handleSelection,
|
||||
}) => {
|
||||
const inputRef = createRef<HTMLInputElement>();
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
const runways = useMemo(
|
||||
() => parser.runways.filter(({ AirportID }) => AirportID === selectedAirport?.ID),
|
||||
[selectedAirport]
|
||||
);
|
||||
const terminals = useMemo(
|
||||
() =>
|
||||
parser.terminals.filter(
|
||||
({ AirportID, RwyID }) => AirportID === selectedAirport?.ID && (!RwyID || RwyID === selectedRunway?.ID)
|
||||
),
|
||||
[selectedAirport, selectedRunway]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col items-center justify-center gap-2 p-2">
|
||||
{selectedAirport && (
|
||||
<button
|
||||
className="fixed top-2 left-2 cursor-pointer rounded border border-red-500 bg-red-500 px-2 py-1 font-semibold text-stone-50 focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black"
|
||||
onClick={() => {
|
||||
setError(undefined);
|
||||
|
||||
if (selectedTerminal) {
|
||||
setSelectedTerminal(undefined);
|
||||
} else if (selectedRunway) {
|
||||
setSelectedRunway(undefined);
|
||||
} else if (selectedAirport) {
|
||||
setSelectedAirport(undefined);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Go back
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!selectedAirport && (
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<h1 className="text-center text-3xl">Enter Airport ICAO</h1>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="rounded border border-black px-2 py-1 focus:outline-2 focus-visible:outline-2"
|
||||
onChange={(e) => {
|
||||
if (e.target.value.length <= 4) e.target.value = e.target.value.toUpperCase();
|
||||
else e.target.value = e.target.value.slice(0, 4);
|
||||
}}
|
||||
></input>
|
||||
<button
|
||||
className="w-full cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2"
|
||||
onClick={() => {
|
||||
const airport = parser.airports.find(({ ICAO }) => ICAO === inputRef.current?.value.toUpperCase());
|
||||
if (!airport) {
|
||||
setError('Airport not found');
|
||||
return;
|
||||
}
|
||||
setSelectedAirport(airport);
|
||||
setError(undefined);
|
||||
}}
|
||||
>
|
||||
Select Airport
|
||||
</button>
|
||||
{error && <span className="text-center text-red-700">{error}</span>}
|
||||
</div>
|
||||
)}
|
||||
{selectedAirport && !selectedRunway && (
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<h1 className="text-center text-3xl">Select Runway</h1>
|
||||
{runways.map((runway) => (
|
||||
<button
|
||||
key={runway.ID}
|
||||
className="w-full cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2"
|
||||
onClick={() => setSelectedRunway(runway)}
|
||||
>
|
||||
Runway {runway.Ident}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{selectedAirport && selectedRunway && !selectedTerminal && (
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<h1 className="text-center text-3xl">Select Procedure</h1>
|
||||
{terminals.map((terminal) => (
|
||||
<button
|
||||
key={terminal.ID}
|
||||
className="cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2"
|
||||
onClick={() => {
|
||||
parser.loadTerminal(terminal.ID).then(() => {
|
||||
setSelectedTerminal(terminal);
|
||||
const transitions = new Set(parser.procedures.map((proc) => proc.Transition));
|
||||
handleSelection(Array.from(transitions));
|
||||
});
|
||||
}}
|
||||
>
|
||||
{terminal.FullName}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
import { default as L } from 'leaflet';
|
||||
import 'leaflet-svg-shape-markers';
|
||||
import hash from 'object-hash';
|
||||
import { createRef, Fragment, useEffect, type FC } from 'react';
|
||||
import { GeoJSON, MapContainer, TileLayer } from 'react-leaflet';
|
||||
|
||||
interface MapProps {
|
||||
procedures: object[];
|
||||
}
|
||||
|
||||
export const Map: FC<MapProps> = ({ procedures }) => {
|
||||
const mapRef = createRef<L.Map>();
|
||||
const layerRef = createRef<L.GeoJSON>();
|
||||
|
||||
useEffect(() => {
|
||||
if (layerRef.current && mapRef.current) {
|
||||
mapRef.current.flyToBounds(layerRef.current.getBounds(), {
|
||||
animate: false,
|
||||
padding: [50, 50],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<MapContainer
|
||||
center={[51.505, -0.09]}
|
||||
zoom={13}
|
||||
zoomSnap={0}
|
||||
zoomDelta={0.1}
|
||||
wheelPxPerZoomLevel={1000}
|
||||
ref={mapRef}
|
||||
className="h-full w-full"
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
/>
|
||||
{procedures.map((procedure) => (
|
||||
<Fragment key={hash(procedure ?? '')}>
|
||||
<GeoJSON
|
||||
key={hash(procedure ?? '') + 'lines'}
|
||||
data={procedure}
|
||||
style={({ properties }) => ({
|
||||
color: '#ff00ff',
|
||||
stroke: true,
|
||||
weight: 5,
|
||||
opacity: 1,
|
||||
dashArray: properties.isManual ? '20, 20' : undefined,
|
||||
})}
|
||||
filter={(feature) => feature.geometry.type !== 'Point'}
|
||||
ref={layerRef}
|
||||
/>
|
||||
<GeoJSON
|
||||
key={hash(procedure ?? '') + 'points'}
|
||||
data={procedure}
|
||||
style={{
|
||||
color: 'black',
|
||||
fill: true,
|
||||
fillColor: 'transparent',
|
||||
stroke: true,
|
||||
weight: 3,
|
||||
}}
|
||||
pointToLayer={({ properties }, latlng) => {
|
||||
if (properties.isFlyOver)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'triangle',
|
||||
radius: 6,
|
||||
});
|
||||
if (properties.isIntersection) return L.circleMarker(latlng, { radius: 6 });
|
||||
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'star-4',
|
||||
radius: 10,
|
||||
rotation: 45,
|
||||
});
|
||||
}}
|
||||
onEachFeature={({ geometry, properties }, layer) => {
|
||||
if (geometry.type === 'Point') {
|
||||
layer.bindPopup(
|
||||
`${properties.name}<br>
|
||||
${properties.altitude} ft<br>
|
||||
${properties.speed} kts<br>
|
||||
CNSTR:
|
||||
${properties.altitudeConstraint ?? ''}
|
||||
${properties.speedConstraint ?? ''}<br>`
|
||||
);
|
||||
}
|
||||
}}
|
||||
filter={(feature) => feature.geometry.type === 'Point'}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</MapContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import { type User } from 'navigraph/auth';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { auth } from '../lib/navigraph';
|
||||
|
||||
interface NavigraphAuthContext {
|
||||
initialized: boolean;
|
||||
user: User | null;
|
||||
signIn: typeof auth.signInWithDeviceFlow;
|
||||
}
|
||||
|
||||
const authContext = createContext<NavigraphAuthContext>({
|
||||
initialized: false,
|
||||
user: null,
|
||||
signIn: () => Promise.reject('Not initialized'),
|
||||
});
|
||||
|
||||
// Provider hook that creates auth object and handles state
|
||||
function useProvideAuth() {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [initialized, setinitialized] = useState(false);
|
||||
|
||||
// Subscribe to user on mount
|
||||
// Because this sets state in the callback it will cause any
|
||||
// component that utilizes this hook to re-render with the latest auth object.
|
||||
useEffect(() => {
|
||||
const unsubscribe = auth.onAuthStateChanged((u) => {
|
||||
if (!initialized) setinitialized(true);
|
||||
setUser(u);
|
||||
});
|
||||
// Cleanup subscription on unmount
|
||||
return () => unsubscribe();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return {
|
||||
user,
|
||||
initialized,
|
||||
signIn: auth.signInWithDeviceFlow,
|
||||
};
|
||||
}
|
||||
|
||||
// Provider component that wraps your app and makes auth object
|
||||
// available to any child component that calls useAuth().
|
||||
export function NavigraphAuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const auth = useProvideAuth();
|
||||
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
|
||||
}
|
||||
|
||||
// Hook for child components to get the auth object
|
||||
// and re-render when it changes.
|
||||
export const useNavigraphAuth = () => {
|
||||
return useContext(authContext);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { initializeApp, Scope, type NavigraphApp } from 'navigraph/app';
|
||||
import { getAuth } from 'navigraph/auth';
|
||||
import { getChartsAPI } from 'navigraph/charts';
|
||||
|
||||
const config: NavigraphApp = {
|
||||
clientId: import.meta.env.VITE_NG_CLIENT_ID,
|
||||
clientSecret: import.meta.env.VITE_NG_CLIENT_SECRET,
|
||||
scopes: [Scope.CHARTS],
|
||||
};
|
||||
|
||||
initializeApp(config);
|
||||
|
||||
export const auth = getAuth({
|
||||
storage: {
|
||||
// Optional
|
||||
getItem: (key) => localStorage.getItem('NG' + key),
|
||||
setItem: (key, value) => localStorage.setItem('NG' + key, value),
|
||||
},
|
||||
});
|
||||
|
||||
export const charts = getChartsAPI();
|
||||
@@ -3,9 +3,12 @@ import { createRoot } from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import { NavigraphAuthProvider } from './hooks/useNavigraphAuth.tsx';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
<NavigraphAuthProvider>
|
||||
<App />
|
||||
</NavigraphAuthProvider>
|
||||
</StrictMode>
|
||||
);
|
||||
@@ -1,17 +0,0 @@
|
||||
import Parser from './parser.ts';
|
||||
import { prompting } from './utils/prompting.ts';
|
||||
|
||||
// mutate fetch to be local
|
||||
|
||||
// @ts-expect-error Global override
|
||||
// eslint-disable-next-line no-global-assign
|
||||
fetch = async (path: string) => {
|
||||
const fs = await import('fs');
|
||||
return {
|
||||
json: () => JSON.parse(fs.readFileSync(`public/${path}`) as unknown as string),
|
||||
};
|
||||
};
|
||||
|
||||
const parser = await Parser.instance();
|
||||
|
||||
console.log(JSON.stringify(await parser.parse(Number.parseInt((await prompting('Terminal ID')) ?? ''))));
|
||||
+214
-220
@@ -1,26 +1,25 @@
|
||||
import geojson from 'geojson';
|
||||
import * as geolib from 'geolib';
|
||||
import { TerminatorsAF } from './terminators/AF.ts';
|
||||
import { TerminatorsCA } from './terminators/CA.ts';
|
||||
import { TerminatorsCD } from './terminators/CD.ts';
|
||||
import { TerminatorsCF } from './terminators/CF.ts';
|
||||
import { TerminatorsCI } from './terminators/CI.ts';
|
||||
import { TerminatorsCR } from './terminators/CR.ts';
|
||||
import { TerminatorsDF } from './terminators/DF.ts';
|
||||
import { TerminatorsFA } from './terminators/FA.ts';
|
||||
import { TerminatorsFC } from './terminators/FC.ts';
|
||||
import { TerminatorsFD } from './terminators/FD.ts';
|
||||
import { TerminatorsFM } from './terminators/FM.ts';
|
||||
import { TerminatorsIF } from './terminators/IF.ts';
|
||||
import { TerminatorsRF } from './terminators/RF.ts';
|
||||
import { TerminatorsTF } from './terminators/TF.ts';
|
||||
import { TerminatorsVA } from './terminators/VA.ts';
|
||||
import { TerminatorsVD } from './terminators/VD.ts';
|
||||
import { TerminatorsVI } from './terminators/VI.ts';
|
||||
import { TerminatorsVM } from './terminators/VM.ts';
|
||||
import { TerminatorsVR } from './terminators/VR.ts';
|
||||
import './utils/extensions.ts';
|
||||
import { prompting } from './utils/prompting.ts';
|
||||
import { TerminatorsAF } from './terminators/AF';
|
||||
import { TerminatorsCA } from './terminators/CA';
|
||||
import { TerminatorsCD } from './terminators/CD';
|
||||
import { TerminatorsCF } from './terminators/CF';
|
||||
import { TerminatorsCI } from './terminators/CI';
|
||||
import { TerminatorsCR } from './terminators/CR';
|
||||
import { TerminatorsDF } from './terminators/DF';
|
||||
import { TerminatorsFA } from './terminators/FA';
|
||||
import { TerminatorsFC } from './terminators/FC';
|
||||
import { TerminatorsFD } from './terminators/FD';
|
||||
import { TerminatorsFM } from './terminators/FM';
|
||||
import { TerminatorsIF } from './terminators/IF';
|
||||
import { TerminatorsRF } from './terminators/RF';
|
||||
import { TerminatorsTF } from './terminators/TF';
|
||||
import { TerminatorsVA } from './terminators/VA';
|
||||
import { TerminatorsVD } from './terminators/VD';
|
||||
import { TerminatorsVI } from './terminators/VI';
|
||||
import { TerminatorsVM } from './terminators/VM';
|
||||
import { TerminatorsVR } from './terminators/VR';
|
||||
import './utils/extensions';
|
||||
|
||||
/*
|
||||
Runway IDs for LIED
|
||||
@@ -31,15 +30,19 @@ Runway IDs for LIED
|
||||
class Parser {
|
||||
private static _instance: Parser;
|
||||
|
||||
private _airports: Airport[];
|
||||
private _waypoints: Waypoint[];
|
||||
private _runways: Runway[];
|
||||
private _terminals: Terminal[];
|
||||
|
||||
private _procedures: TerminalEntry[] = [];
|
||||
|
||||
public static AC_SPEED = 250;
|
||||
public static AC_BANK = 30;
|
||||
public static AC_VS = 1400;
|
||||
|
||||
private constructor(waypoints: Waypoint[], runways: Runway[], terminals: Terminal[]) {
|
||||
private constructor(airports: Airport[], waypoints: Waypoint[], runways: Runway[], terminals: Terminal[]) {
|
||||
this._airports = airports;
|
||||
this._waypoints = waypoints;
|
||||
this._runways = runways;
|
||||
this._terminals = terminals;
|
||||
@@ -47,16 +50,20 @@ class Parser {
|
||||
|
||||
public static instance = async () => {
|
||||
if (!Parser._instance) {
|
||||
const airports = await (await fetch('NavData/Airports.json')).json();
|
||||
const waypoints = await (await fetch('NavData/Waypoints.json')).json();
|
||||
const runways = await (await fetch('NavData/Runways.json')).json();
|
||||
const terminals = await (await fetch('NavData/Terminals.json')).json();
|
||||
|
||||
Parser._instance = new Parser(waypoints, runways, terminals);
|
||||
Parser._instance = new Parser(airports, waypoints, runways, terminals);
|
||||
}
|
||||
|
||||
return Parser._instance;
|
||||
};
|
||||
|
||||
public get airports() {
|
||||
return this._airports;
|
||||
}
|
||||
public get terminals() {
|
||||
return this._terminals;
|
||||
}
|
||||
@@ -67,212 +74,199 @@ class Parser {
|
||||
return this._runways;
|
||||
}
|
||||
|
||||
public parse = async (terminalID: number) => {
|
||||
// Get Procedure main
|
||||
const terminal = this.terminals.find(({ ID }) => ID === terminalID);
|
||||
if (!terminal) throw new Error('Procedure does not exists');
|
||||
// Get runway this procedure is for
|
||||
let runway = this.runways.find(({ ID }) => ID === terminal.RwyID);
|
||||
if (!runway) {
|
||||
const id = Number.parseInt((await prompting('Runway ID')) ?? '');
|
||||
runway = this.runways.find(({ ID }) => ID === id);
|
||||
if (!runway) throw new Error('Procedure links to non existent Runway');
|
||||
}
|
||||
// Load procedure
|
||||
const procedures = (await (await fetch(`NavData/TermID_${terminalID}.json`)).json()) as TerminalEntry[];
|
||||
// Split into transitions
|
||||
const transitions = new Set(procedures.map((proc) => proc.Transition));
|
||||
public get procedures() {
|
||||
return this._procedures;
|
||||
}
|
||||
|
||||
const output: object[] = [];
|
||||
public loadTerminal = async (terminalID: number) => {
|
||||
this._procedures = (await (
|
||||
await fetch(`NavData/ProcedureLegs/TermID_${terminalID}.json`)
|
||||
).json()) as TerminalEntry[];
|
||||
};
|
||||
|
||||
public parse = (runway: Runway, transition: string) => {
|
||||
// Private functions
|
||||
/**
|
||||
* @param line Line segments
|
||||
*/
|
||||
const updateLastCourse = (line: LineSegment[]) => {
|
||||
lastCourse = geolib.getGreatCircleBearing(
|
||||
{
|
||||
latitude: line.at(-2)![1],
|
||||
longitude: line.at(-2)![0],
|
||||
},
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param fix New fix
|
||||
* @param line New line
|
||||
* @param options Options for line rendering
|
||||
*/
|
||||
const update = (fix?: NavFix, line?: LineSegment[], options?: Record<string, unknown>) => {
|
||||
if (fix) navFixes.push(fix);
|
||||
if (line) {
|
||||
lineSegments.push({ line, ...options });
|
||||
updateLastCourse(line);
|
||||
}
|
||||
};
|
||||
|
||||
// Output variables
|
||||
const navFixes: NavFix[] = [];
|
||||
const lineSegments: { line: LineSegment[]; [x: string]: unknown }[] = [];
|
||||
|
||||
// Initials
|
||||
navFixes.push({
|
||||
latitude: runway.Latitude,
|
||||
longitude: runway.Longitude,
|
||||
altitude: runway.Elevation,
|
||||
speed: 0,
|
||||
name: runway.Ident,
|
||||
});
|
||||
let lastCourse = runway.TrueHeading;
|
||||
const procedure = this._procedures.filter(({ Transition }) => !Transition || Transition === transition);
|
||||
|
||||
// Main
|
||||
transitions.forEach((transition) => {
|
||||
// Private functions
|
||||
/**
|
||||
* @param line Line segments
|
||||
*/
|
||||
const updateLastCourse = (line: LineSegment[]) => {
|
||||
lastCourse = geolib.getGreatCircleBearing(
|
||||
{
|
||||
latitude: line.at(-2)![1],
|
||||
longitude: line.at(-2)![0],
|
||||
},
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
}
|
||||
);
|
||||
};
|
||||
for (let index = 0; index < procedure.length; index++) {
|
||||
const leg = procedure[index];
|
||||
const previousFix = navFixes.at(-1)!;
|
||||
const waypoint = this.waypoints.filter(({ ID }) => ID === leg.WptID)[0];
|
||||
|
||||
/**
|
||||
* @param fix New fix
|
||||
* @param line New line
|
||||
* @param options Options for line rendering
|
||||
*/
|
||||
const update = (fix?: NavFix, line?: LineSegment[], options?: Record<string, unknown>) => {
|
||||
if (fix) navFixes.push(fix);
|
||||
if (line) {
|
||||
lineSegments.push({ line, ...options });
|
||||
updateLastCourse(line);
|
||||
switch (leg.TrackCode) {
|
||||
case 'AF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsAF(leg as AFTerminalEntry, previousFix, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Output variables
|
||||
const navFixes: NavFix[] = [];
|
||||
const lineSegments: { line: LineSegment[]; [x: string]: unknown }[] = [];
|
||||
|
||||
// Initials
|
||||
navFixes.push({
|
||||
latitude: runway.Latitude,
|
||||
longitude: runway.Longitude,
|
||||
altitude: runway.Elevation,
|
||||
speed: 0,
|
||||
name: runway.Ident,
|
||||
});
|
||||
let lastCourse = runway.TrueHeading;
|
||||
|
||||
const procedure = procedures.filter((proc) => proc.Transition === transition);
|
||||
for (let index = 0; index < procedure.length; index++) {
|
||||
const leg = procedure[index];
|
||||
const previousFix = navFixes.at(-1)!;
|
||||
const waypoint = this.waypoints.filter(({ ID }) => ID === leg.WptID)[0];
|
||||
|
||||
switch (leg.TrackCode) {
|
||||
case 'AF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsAF(leg as AFTerminalEntry, previousFix, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCA(leg as CATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCD(leg as CDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCF(leg as CFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CI': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCI(
|
||||
leg as CITerminalEntry,
|
||||
procedure[index + 1],
|
||||
previousFix,
|
||||
lastCourse
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CR': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCR(leg as CRTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'DF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsDF(leg as DFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFA(leg as FATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FC': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFC(leg as FCTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFD(leg as FDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFM(leg as FMTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd, { isManual: true });
|
||||
break;
|
||||
}
|
||||
case 'HA':
|
||||
case 'HF':
|
||||
case 'HM':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
break;
|
||||
case 'IF': {
|
||||
const fixToAdd = TerminatorsIF(leg as RFTerminalEntry, waypoint);
|
||||
navFixes.length = 0;
|
||||
navFixes.push(fixToAdd);
|
||||
break;
|
||||
}
|
||||
case 'PI':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
break;
|
||||
case 'RF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsRF(
|
||||
leg as RFTerminalEntry,
|
||||
procedure[index + 1],
|
||||
previousFix,
|
||||
lastCourse,
|
||||
waypoint
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'TF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsTF(leg as TFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVA(leg as VATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVD(leg as VDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VI': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVI(
|
||||
leg as VITerminalEntry,
|
||||
procedure[index + 1],
|
||||
previousFix,
|
||||
lastCourse
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVM(leg as VMTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd, { isManual: true });
|
||||
break;
|
||||
}
|
||||
case 'VR': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVR(leg as VRTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
break;
|
||||
case 'CA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCA(leg as CATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCD(leg as CDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCF(leg as CFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CI': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCI(
|
||||
leg as CITerminalEntry,
|
||||
procedure[index + 1],
|
||||
previousFix,
|
||||
lastCourse
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'CR': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCR(leg as CRTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'DF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsDF(leg as DFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFA(leg as FATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FC': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFC(leg as FCTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFD(leg as FDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'FM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFM(leg as FMTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd, { isManual: true });
|
||||
break;
|
||||
}
|
||||
case 'HA':
|
||||
case 'HF':
|
||||
case 'HM':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
break;
|
||||
case 'IF': {
|
||||
const fixToAdd = TerminatorsIF(leg as RFTerminalEntry, waypoint);
|
||||
navFixes.length = 0;
|
||||
navFixes.push(fixToAdd);
|
||||
break;
|
||||
}
|
||||
case 'PI':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
break;
|
||||
case 'RF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsRF(
|
||||
leg as RFTerminalEntry,
|
||||
procedure[index + 1],
|
||||
previousFix,
|
||||
lastCourse,
|
||||
waypoint
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'TF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsTF(leg as TFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVA(leg as VATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVD(leg as VDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VI': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVI(
|
||||
leg as VITerminalEntry,
|
||||
procedure[index + 1],
|
||||
previousFix,
|
||||
lastCourse
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
case 'VM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVM(leg as VMTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd, { isManual: true });
|
||||
break;
|
||||
}
|
||||
case 'VR': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVR(leg as VRTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output.push(
|
||||
geojson.parse([...navFixes, ...lineSegments], {
|
||||
LineString: 'line',
|
||||
Point: ['latitude', 'longitude'],
|
||||
})
|
||||
);
|
||||
return geojson.parse([...navFixes, ...lineSegments], {
|
||||
LineString: 'line',
|
||||
Point: ['latitude', 'longitude'],
|
||||
});
|
||||
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { generatePerformanceArc } from './generatePerformanceArc.ts';
|
||||
import { generatePerformanceArc } from './generatePerformanceArc';
|
||||
|
||||
/**
|
||||
* @param crsIntoEndpoint Course into arc endpoint
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import Parser from '../parser.ts';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate.ts';
|
||||
import Parser from '../parser';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
/**
|
||||
* @param crsIntoEndpoint Course into arc endpoint
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { computeIntersection } from '../utils/computeIntersection.ts';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
|
||||
/**
|
||||
* @param crsIntoEndpoint Course into arc endpoint
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { generatePerformanceArc } from './generatePerformanceArc.ts';
|
||||
import { generateTangentArc } from './generateTangentArc.ts';
|
||||
import { generatePerformanceArc } from './generatePerformanceArc';
|
||||
import { generateTangentArc } from './generateTangentArc';
|
||||
|
||||
/**
|
||||
* @param crsIntoEndpoint Course into endpoint
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { generateAFArc } from '../pathGenerators/generateAFArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { generateAFArc } from '../pathGenerators/generateAFArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsAF = (
|
||||
leg: AFTerminalEntry,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as geolib from 'geolib';
|
||||
import Parser from '../parser.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import Parser from '../parser';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsCA = (
|
||||
leg: CATerminalEntry,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
// NOTE: Distance not adjusted for altitude in this demo
|
||||
export const TerminatorsCD = (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsCF = (
|
||||
leg: CFTerminalEntry,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix.ts';
|
||||
import { computeIntersection } from '../utils/computeIntersection.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts.ts';
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';
|
||||
|
||||
export const TerminatorsCI = (
|
||||
leg: CITerminalEntry,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeIntersection } from '../utils/computeIntersection.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsCR = (
|
||||
leg: CRTerminalEntry,
|
||||
@@ -28,6 +28,7 @@ export const TerminatorsCR = (
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
};
|
||||
|
||||
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||
|
||||
return [interceptFix, line];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsDF = (
|
||||
leg: DFTerminalEntry,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as geolib from 'geolib';
|
||||
import Parser from '../parser.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import Parser from '../parser';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsFA = (
|
||||
leg: FATerminalEntry,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as geolib from 'geolib';
|
||||
import Parser from '../parser.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate.ts';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
// NOTE: Distance not adjusted for altitude in this demo
|
||||
export const TerminatorsFC = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
// NOTE: Distance not adjusted for altitude in this demo
|
||||
export const TerminatorsFD = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsFM = (
|
||||
leg: FMTerminalEntry,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Parser from '../parser.ts';
|
||||
import Parser from '../parser';
|
||||
|
||||
export const TerminatorsIF = (leg: IFTerminalEntry, waypoint?: Waypoint): NavFix => {
|
||||
const targetFix: NavFix = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { generateRFArc } from '../pathGenerators/generateRFArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts.ts';
|
||||
import { generateRFArc } from '../pathGenerators/generateRFArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';
|
||||
|
||||
export const TerminatorsRF = (
|
||||
leg: RFTerminalEntry,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import Parser from '../parser.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsTF = (
|
||||
leg: TFTerminalEntry,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as geolib from 'geolib';
|
||||
import Parser from '../parser.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import Parser from '../parser';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
||||
export const TerminatorsVA = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
||||
// NOTE: Distance not adjusted for altitude in this demo
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix.ts';
|
||||
import { computeIntersection } from '../utils/computeIntersection.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts.ts';
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';
|
||||
|
||||
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
||||
export const TerminatorsVI = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as geolib from 'geolib';
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
||||
export const TerminatorsVM = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc.ts';
|
||||
import { computeIntersection } from '../utils/computeIntersection.ts';
|
||||
import { computeSpeed } from '../utils/computeSpeed.ts';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
||||
export const TerminatorsVR = (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Parser from '../parser.ts';
|
||||
import Parser from '../parser';
|
||||
|
||||
export const computeSpeed = (leg: TerminalEntry, previousFix: NavFix) => {
|
||||
if (leg.SpeedLimit) return leg.SpeedLimit;
|
||||
|
||||
@@ -32,6 +32,11 @@ export const getCourseAndFixForIntercepts = (leg: TerminalEntry, origin: NavFix)
|
||||
const fix = { latitude: _leg.WptLat, longitude: _leg.WptLon };
|
||||
return [_leg.Course.reciprocalCourse().toTrue(fix), fix];
|
||||
}
|
||||
case 'DF': {
|
||||
const _leg = leg as FMTerminalEntry;
|
||||
const fix = { latitude: _leg.WptLat, longitude: _leg.WptLon };
|
||||
return [-1, fix];
|
||||
}
|
||||
default: {
|
||||
return [-1, origin];
|
||||
}
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'leaflet';
|
||||
|
||||
declare module 'leaflet' {
|
||||
|
||||
Vendored
+10
-1
@@ -6,6 +6,7 @@ export declare global {
|
||||
|
||||
type Runway = {
|
||||
ID: number;
|
||||
AirportID: number;
|
||||
Latitude: number;
|
||||
Longitude: number;
|
||||
Elevation: number;
|
||||
@@ -70,8 +71,9 @@ export declare global {
|
||||
|
||||
type Terminal = {
|
||||
ID: number;
|
||||
FullName: string;
|
||||
AirportID: number;
|
||||
ICAO: string;
|
||||
FullName: string;
|
||||
RwyID?: number;
|
||||
};
|
||||
|
||||
@@ -90,4 +92,11 @@ export declare global {
|
||||
};
|
||||
|
||||
type LineSegment = [number, number];
|
||||
|
||||
type Airport = {
|
||||
ID: number;
|
||||
ICAO: string;
|
||||
Latitude: number;
|
||||
Longitude: number;
|
||||
};
|
||||
}
|
||||
Vendored
+13
@@ -1 +1,14 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ViteTypeOptions {
|
||||
strictImportMetaEnv: unknown;
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_NG_CLIENT_ID: string;
|
||||
readonly VITE_NG_CLIENT_SECRET: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"sourceMap": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"sourceMap": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
|
||||
@@ -5,4 +5,7 @@ import { defineConfig } from 'vite';
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), react()],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user