Compare commits
14 Commits
e4adf30632
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 266606d9ab | |||
| 900babf4cc | |||
| 048d5129aa | |||
| 73373420d3 | |||
| e9bab0306f | |||
| 4e4b921f79 | |||
| d102ec4834 | |||
| e91323c79b | |||
| bbda565272 | |||
| 56ecfe38d1 | |||
| e0ee6d11dd | |||
| 2bdb7e78c4 | |||
| c5cd3c7a0e | |||
| 779a78649e |
Vendored
+1
-1
@@ -9,7 +9,7 @@
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}/browser",
|
||||
"webRoot": "${workspaceFolder}/md11-nav-data",
|
||||
"sourceMaps": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
### Example
|
||||
|
||||
LGAV BIBE1K SID (Cycle 2507, ID 10653)
|
||||
LGAV 03L BIBE1K SID (Cycle 2507, ID 10653)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -40,12 +40,14 @@ LGAV BIBE1K SID (Cycle 2507, ID 10653)
|
||||
|
||||
While similar to an RF, the center point is coded differently.
|
||||
|
||||
Calculate distance for FMS based on `2 * π * NavDist * abs(NavBear - Course) 360`.
|
||||
|
||||
|
||||
## Course to Altitude (CA)
|
||||
|
||||
### Example
|
||||
|
||||
LGAV BIBE1L SID (Cycle 2507, ID 10654)
|
||||
LGAV 03L BIBE1L SID (Cycle 2507, ID 10654)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -74,7 +76,7 @@ This new origin is an implicit overfly.
|
||||
|
||||
### Example
|
||||
|
||||
LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
||||
LGAV 21L BIBE2F SID (Cycle 2507, ID 10657)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -98,14 +100,13 @@ LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
||||
|
||||
The leg terminates upon reaching `Distance`.
|
||||
This intercept point then becomes the origin fix of the succeeding leg.
|
||||
This new origin is an implicit overfly.
|
||||
|
||||
|
||||
## Course to Fix (CF)
|
||||
|
||||
### Example
|
||||
|
||||
LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
||||
LGAV 21L BIBE2F SID (Cycle 2507, ID 10657)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -145,7 +146,7 @@ shall be `TurnDir`.
|
||||
|
||||
### Example
|
||||
|
||||
LGAV BIBE1L SID (Cycle 2507, ID 10654)
|
||||
LGAV 03L BIBE1L SID (Cycle 2507, ID 10654)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -170,7 +171,7 @@ This new origin can never be an overfly due to the intercept nature.
|
||||
|
||||
### Example
|
||||
|
||||
LGAV KOR1D SID (Cycle 2507, ID 10679)
|
||||
LGAV 03L KOR1D SID (Cycle 2507, ID 10679)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -197,7 +198,7 @@ This intercept point then becomes the origin fix of the succeeding leg.
|
||||
|
||||
### Example
|
||||
|
||||
LGAV KOR1D SID (Cycle 2507, ID 10679)
|
||||
LGAV 03L KOR1D SID (Cycle 2507, ID 10679)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -219,7 +220,7 @@ LGAV KOR1D SID (Cycle 2507, ID 10679)
|
||||
|
||||
### Example
|
||||
|
||||
LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
||||
LGAV 21L BIBE2F SID (Cycle 2507, ID 10657)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -254,7 +255,7 @@ This new origin is an implicit overfly.
|
||||
|
||||
### Example
|
||||
|
||||
LIED CAR6F SID (Cycle 2507, ID 11798)
|
||||
LIED 34L/R CAR6F SID (Cycle 2507, ID 11798)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -290,7 +291,7 @@ This intercept point then becomes the origin fix of the succeeding leg.
|
||||
|
||||
### Example
|
||||
|
||||
LGAV BIBE2T SID (Cycle 2507, ID 10659)
|
||||
LGAV 03R BIBE2T SID (Cycle 2507, ID 10659)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -318,14 +319,13 @@ LGAV BIBE2T SID (Cycle 2507, ID 10659)
|
||||
|
||||
The leg terminates upon reaching `Distance`.
|
||||
This intercept point then becomes the origin fix of the succeeding leg.
|
||||
This new origin is an implicit overfly.
|
||||
|
||||
|
||||
## Track from Fix to Manual Termination (FM)
|
||||
|
||||
### Example
|
||||
|
||||
LFPV PB2V SID (Cycle 2507, ID 10395)
|
||||
LFPV 27 PB2V SID (Cycle 2507, ID 10395)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -346,7 +346,7 @@ LFPV PB2V SID (Cycle 2507, ID 10395)
|
||||
- `NavDist` is in **nmi**. `NavDist` is geodesic.
|
||||
|
||||
|
||||
## Holding mandatory (HA)
|
||||
## Holding mandatory (altitude end) (HA)
|
||||
|
||||
### Example
|
||||
|
||||
@@ -420,7 +420,7 @@ My guess is that `Distance` always is a distance. This would match with the exam
|
||||
The Leg terminates after the hold is exited ath the hold entry fix.
|
||||
|
||||
|
||||
## Holding mandatory (HM)
|
||||
## Holding mandatory (manual end) (HM)
|
||||
|
||||
### Example
|
||||
|
||||
@@ -457,7 +457,7 @@ My guess as for the missing time/distance decider field, assume it to be distanc
|
||||
|
||||
### Example
|
||||
|
||||
FAWB VDM29 APP (Cycle 2507, ID 67794), Missed approach procedure
|
||||
FAWB 29 VDM29 APP (Cycle 2507, ID 67794), Missed approach procedure
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -493,8 +493,11 @@ FAUP VDM35 APP (Cycle 2507, ID 67790)
|
||||
### Instructions
|
||||
|
||||
- From the fix identified by (`WptID`, `WptLat`, `WptLon`) *or*
|
||||
(`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly `Course` for `Distance`.
|
||||
- Turn `TurnDir` until aligned with succeeding leg course.
|
||||
(`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly direct to succeeding leg fix.
|
||||
- Turn `TurnDir` onto `Course`.
|
||||
- Fly for 1 minute.
|
||||
- Turn 180°.
|
||||
- Fly until intercepting course of succeeding leg.
|
||||
- `Alt` shall be the altitude during the procedure.
|
||||
|
||||
### Units
|
||||
@@ -505,15 +508,17 @@ FAUP VDM35 APP (Cycle 2507, ID 67790)
|
||||
|
||||
### Notes
|
||||
|
||||
This needs to look into the succeeding leg in order to calculate the intercept point.
|
||||
This intercept point then becomes the origin fix of the succeeding leg.
|
||||
This needs to look into the succeeding leg in order to fly the first segment and get the course
|
||||
to intercept after the reversal.
|
||||
The database does not specify how long the outbound leg shall be.
|
||||
1 minute is assumed here (googled it, seemed a common one).
|
||||
|
||||
|
||||
## Radius to Fix (RF)
|
||||
|
||||
### Example
|
||||
|
||||
LFRN GODA5R SID (cycle 2507, ID 10485)
|
||||
LFRN 10 GODA5R SID (cycle 2507, ID 10485)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -545,12 +550,14 @@ No radius is specified, but can be inferred based on center point, both endpoint
|
||||
|
||||
Example has `NavBear` set to `null`, significance of the inbound tangential track is unknown.
|
||||
|
||||
Use `Distance` for calculations in FMS.
|
||||
|
||||
|
||||
## Track to Fix (TF)
|
||||
|
||||
### Example
|
||||
|
||||
LFRN GODA5R SID (cycle 2507, ID 10485)
|
||||
LFRN 10 GODA5R SID (cycle 2507, ID 10485)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -571,7 +578,7 @@ LFRN GODA5R SID (cycle 2507, ID 10485)
|
||||
|
||||
### Example
|
||||
|
||||
LFRK LGL4X SID (Cycle 2507, ID 10475)
|
||||
LFRK 10 LGL4X SID (Cycle 2507, ID 10475)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -599,7 +606,7 @@ This new origin is an implicit overfly.
|
||||
|
||||
### Example
|
||||
|
||||
LFRK NEVI4Y SID (Cycle 2507, ID 10482)
|
||||
LFRK 31 NEVI4Y SID (Cycle 2507, ID 10482)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -622,14 +629,13 @@ LFRK NEVI4Y SID (Cycle 2507, ID 10482)
|
||||
|
||||
The leg terminates upon reaching `Distance`.
|
||||
This intercept point then becomes the origin fix of the succeeding leg.
|
||||
This new origin is an implicit overfly.
|
||||
|
||||
|
||||
## Heading to Intercept (VI)
|
||||
|
||||
### Example
|
||||
|
||||
LFRK LUSI4Y SID (Cycle 2507, ID 10480)
|
||||
LFRK 31 LUSI4Y SID (Cycle 2507, ID 10480)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -656,7 +662,7 @@ This new origin can never be an overfly due to the intercept nature.
|
||||
|
||||
### Example
|
||||
|
||||
LFPV PB2P SID (Cycle 2507, ID 10394)
|
||||
LFPV 27 PB2P SID (Cycle 2507, ID 10394)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -676,7 +682,7 @@ LFPV PB2P SID (Cycle 2507, ID 10394)
|
||||
|
||||
### Example
|
||||
|
||||
LIMC MMP8G SID (Cycle 2507, ID 11909)
|
||||
LIMC 35R MMP8G SID (Cycle 2507, ID 11909)
|
||||
|
||||
### Minimum Required Fields
|
||||
|
||||
@@ -696,4 +702,28 @@ LIMC MMP8G SID (Cycle 2507, ID 11909)
|
||||
### Notes
|
||||
|
||||
The leg terminates at the intercept point.
|
||||
This intercept point then becomes the origin fix of the succeeding leg.
|
||||
This intercept point then becomes the origin fix of the succeeding leg.
|
||||
|
||||
|
||||
# Other Fields
|
||||
|
||||
- `Vnav`: Angle from IAF to MAP. Useful for RNAV.
|
||||
|
||||
|
||||
# How to run
|
||||
|
||||
- `git clone https://git.hofmannnet.myhome-server.de/TFDi_MD-11_QA/NavDataExplorer.git`.
|
||||
- `cd md11-nav-data`.
|
||||
- `pnpm install`
|
||||
|
||||
For dev:
|
||||
|
||||
- Copy contents of `tfdidesign-aircraft-md11/Design/Primary` into ? `md11-nav-data/public/NavData`
|
||||
- `cd md11-nav-data`.
|
||||
- `pnpm dev`
|
||||
|
||||
For prod:
|
||||
|
||||
- `cd md11-nav-data`.
|
||||
- `pnpm build`
|
||||
- Verify NavData was copied into `dist`
|
||||
File diff suppressed because one or more lines are too long
@@ -1,137 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 84218,
|
||||
"TerminalID": 10394,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 165609,
|
||||
"WptLat": 48.77185278,
|
||||
"WptLon": 2.09752222,
|
||||
"TurnDir": "",
|
||||
"NavID": 3827,
|
||||
"NavLat": 48.97466667,
|
||||
"NavLon": 2.45516667,
|
||||
"NavBear": 228.5,
|
||||
"NavDist": 18.7,
|
||||
"Course": 267,
|
||||
"Distance": 4,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84219,
|
||||
"TerminalID": 10394,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 247786,
|
||||
"WptLat": 48.73058333,
|
||||
"WptLon": 1.8205,
|
||||
"TurnDir": "",
|
||||
"NavID": 3827,
|
||||
"NavLat": 48.97466667,
|
||||
"NavLon": 2.45516667,
|
||||
"NavBear": 239,
|
||||
"NavDist": 29.1,
|
||||
"Course": 256,
|
||||
"Distance": 11,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84220,
|
||||
"TerminalID": 10394,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 253072,
|
||||
"WptLat": 48.75372222,
|
||||
"WptLon": 2.10236111,
|
||||
"TurnDir": "R",
|
||||
"NavID": 3827,
|
||||
"NavLat": 48.97466667,
|
||||
"NavLon": 2.45516667,
|
||||
"NavBear": 225.6,
|
||||
"NavDist": 19.3,
|
||||
"Course": 107,
|
||||
"Distance": 11,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84221,
|
||||
"TerminalID": 10394,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CR",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": 3827,
|
||||
"NavLat": 48.97466667,
|
||||
"NavLon": 2.45516667,
|
||||
"NavBear": 137,
|
||||
"NavDist": null,
|
||||
"Course": 73,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84222,
|
||||
"TerminalID": 10394,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "VM",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 73,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,110 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 84223,
|
||||
"TerminalID": 10395,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 165609,
|
||||
"WptLat": 48.77185278,
|
||||
"WptLon": 2.09752222,
|
||||
"TurnDir": "",
|
||||
"NavID": 3827,
|
||||
"NavLat": 48.97466667,
|
||||
"NavLon": 2.45516667,
|
||||
"NavBear": 228.5,
|
||||
"NavDist": 18.7,
|
||||
"Course": 267,
|
||||
"Distance": 4,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84224,
|
||||
"TerminalID": 10395,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 165604,
|
||||
"WptLat": 48.80511389,
|
||||
"WptLon": 2.01279722,
|
||||
"TurnDir": "",
|
||||
"NavID": 3827,
|
||||
"NavLat": 48.97466667,
|
||||
"NavLon": 2.45516667,
|
||||
"NavBear": 239,
|
||||
"NavDist": 20.3,
|
||||
"Course": 310,
|
||||
"Distance": 3,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84225,
|
||||
"TerminalID": 10395,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CI",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 22,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84226,
|
||||
"TerminalID": 10395,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "FM",
|
||||
"WptID": 165605,
|
||||
"WptLat": 48.83954444,
|
||||
"WptLon": 1.95262222,
|
||||
"TurnDir": "",
|
||||
"NavID": 3827,
|
||||
"NavLat": 48.97466667,
|
||||
"NavLon": 2.45516667,
|
||||
"NavBear": 247,
|
||||
"NavDist": 21.5,
|
||||
"Course": 67,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,83 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 84536,
|
||||
"TerminalID": 10475,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "VA",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 125,
|
||||
"Distance": null,
|
||||
"Alt": "00660A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84537,
|
||||
"TerminalID": 10475,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 247774,
|
||||
"WptLat": 49.11566667,
|
||||
"WptLon": -0.32125,
|
||||
"TurnDir": "",
|
||||
"NavID": 3836,
|
||||
"NavLat": 49.17319444,
|
||||
"NavLon": -0.45527778,
|
||||
"NavBear": 123.1,
|
||||
"NavDist": 6.3,
|
||||
"Course": 125,
|
||||
"Distance": 6,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84538,
|
||||
"TerminalID": 10475,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 252987,
|
||||
"WptLat": 48.79061111,
|
||||
"WptLon": 0.53027778,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,83 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 84551,
|
||||
"TerminalID": 10480,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "VD",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": 3851,
|
||||
"NavLat": 49.17333333,
|
||||
"NavLon": -0.44711111,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 305,
|
||||
"Distance": 5,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84552,
|
||||
"TerminalID": 10480,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "VI",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 243,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84553,
|
||||
"TerminalID": 10480,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 40633,
|
||||
"WptLat": 49.21472222,
|
||||
"WptLon": -1.79555556,
|
||||
"TurnDir": "",
|
||||
"NavID": 3836,
|
||||
"NavLat": 49.17319444,
|
||||
"NavLon": -0.45527778,
|
||||
"NavBear": 273.2,
|
||||
"NavDist": 52.8,
|
||||
"Course": 273,
|
||||
"Distance": 44,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,83 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 84557,
|
||||
"TerminalID": 10482,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "VD",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": 3851,
|
||||
"NavLat": 49.17333333,
|
||||
"NavLon": -0.44711111,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 305,
|
||||
"Distance": 5,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84558,
|
||||
"TerminalID": 10482,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CI",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "R",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 50,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84559,
|
||||
"TerminalID": 10482,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 8516,
|
||||
"WptLat": 50,
|
||||
"WptLon": -0.36823333,
|
||||
"TurnDir": "",
|
||||
"NavID": 3836,
|
||||
"NavLat": 49.17319444,
|
||||
"NavLon": -0.45527778,
|
||||
"NavBear": 3.9,
|
||||
"NavDist": 49.8,
|
||||
"Course": 4,
|
||||
"Distance": 42,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,110 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 84567,
|
||||
"TerminalID": 10485,
|
||||
"Type": "5",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 166062,
|
||||
"WptLat": 48.06525,
|
||||
"WptLon": -1.67930556,
|
||||
"TurnDir": "",
|
||||
"NavID": 3946,
|
||||
"NavLat": 48.06925,
|
||||
"NavLon": -1.74166667,
|
||||
"NavBear": 95.4,
|
||||
"NavDist": 2.5,
|
||||
"Course": 101,
|
||||
"Distance": 2,
|
||||
"Alt": "00530A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 195,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84568,
|
||||
"TerminalID": 10485,
|
||||
"Type": "5",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "RF",
|
||||
"WptID": 166064,
|
||||
"WptLat": 48.03483333,
|
||||
"WptLon": -1.63894444,
|
||||
"TurnDir": "R",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 175.3,
|
||||
"Distance": 2.6,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": 166063,
|
||||
"CenterLat": 48.03252778,
|
||||
"CenterLon": -1.6885,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 195,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84569,
|
||||
"TerminalID": 10485,
|
||||
"Type": "5",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 166061,
|
||||
"WptLat": 47.91030556,
|
||||
"WptLon": -1.62611111,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 84570,
|
||||
"TerminalID": 10485,
|
||||
"Type": "5",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 40368,
|
||||
"WptLat": 47.64194444,
|
||||
"WptLon": -1.96,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,110 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 85228,
|
||||
"TerminalID": 10653,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 166553,
|
||||
"WptLat": 38.04930556,
|
||||
"WptLon": 24.04694722,
|
||||
"TurnDir": "",
|
||||
"NavID": 3985,
|
||||
"NavLat": 37.88867778,
|
||||
"NavLon": 23.80449722,
|
||||
"NavBear": 45,
|
||||
"NavDist": 15,
|
||||
"Course": 32,
|
||||
"Distance": 9,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85229,
|
||||
"TerminalID": 10653,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "AF",
|
||||
"WptID": 166645,
|
||||
"WptLat": 37.75945833,
|
||||
"WptLon": 24.07472778,
|
||||
"TurnDir": "R",
|
||||
"NavID": 3985,
|
||||
"NavLat": 37.88867778,
|
||||
"NavLon": 23.80449722,
|
||||
"NavBear": 116,
|
||||
"NavDist": 15,
|
||||
"Course": 45,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85230,
|
||||
"TerminalID": 10653,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "AF",
|
||||
"WptID": 166646,
|
||||
"WptLat": 37.73090833,
|
||||
"WptLon": 24.04940556,
|
||||
"TurnDir": "R",
|
||||
"NavID": 3985,
|
||||
"NavLat": 37.88867778,
|
||||
"NavLon": 23.80449722,
|
||||
"NavBear": 124,
|
||||
"NavDist": 15,
|
||||
"Course": 116,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 220,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85231,
|
||||
"TerminalID": 10653,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 166620,
|
||||
"WptLat": 37.20805556,
|
||||
"WptLon": 24.83444444,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,164 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 85232,
|
||||
"TerminalID": 10654,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CA",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 32,
|
||||
"Distance": null,
|
||||
"Alt": "00710A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85233,
|
||||
"TerminalID": 10654,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 166631,
|
||||
"WptLat": 38.07855556,
|
||||
"WptLon": 24.06305,
|
||||
"TurnDir": "",
|
||||
"NavID": 4032,
|
||||
"NavLat": 37.91683889,
|
||||
"NavLon": 23.91443889,
|
||||
"NavBear": 31,
|
||||
"NavDist": 12,
|
||||
"Course": 31,
|
||||
"Distance": 10,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85234,
|
||||
"TerminalID": 10654,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CI",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 31,
|
||||
"Distance": null,
|
||||
"Alt": "04000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 210,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85235,
|
||||
"TerminalID": 10654,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 166663,
|
||||
"WptLat": 38.04289167,
|
||||
"WptLon": 24.31536944,
|
||||
"TurnDir": "",
|
||||
"NavID": 4015,
|
||||
"NavLat": 37.99413889,
|
||||
"NavLon": 24.49490833,
|
||||
"NavBear": 284,
|
||||
"NavDist": 9,
|
||||
"Course": 104,
|
||||
"Distance": 11,
|
||||
"Alt": "07000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85236,
|
||||
"TerminalID": 10654,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 253105,
|
||||
"WptLat": 37.55716389,
|
||||
"WptLon": 24.2987,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "24000B",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85237,
|
||||
"TerminalID": 10654,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 166620,
|
||||
"WptLat": 37.20805556,
|
||||
"WptLon": 24.83444444,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,137 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 85247,
|
||||
"TerminalID": 10657,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 253138,
|
||||
"WptLat": 37.91801944,
|
||||
"WptLon": 23.93798333,
|
||||
"TurnDir": "",
|
||||
"NavID": 4040,
|
||||
"NavLat": 37.91801944,
|
||||
"NavLon": 23.93798333,
|
||||
"NavBear": 0,
|
||||
"NavDist": 0,
|
||||
"Course": 212,
|
||||
"Distance": 2,
|
||||
"Alt": "00700A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85248,
|
||||
"TerminalID": 10657,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "FA",
|
||||
"WptID": 253138,
|
||||
"WptLat": 37.91801944,
|
||||
"WptLon": 23.93798333,
|
||||
"TurnDir": "",
|
||||
"NavID": 4040,
|
||||
"NavLat": 37.91801944,
|
||||
"NavLon": 23.93798333,
|
||||
"NavBear": 0,
|
||||
"NavDist": 0,
|
||||
"Course": 203,
|
||||
"Distance": null,
|
||||
"Alt": "04000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85249,
|
||||
"TerminalID": 10657,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CD",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": 4007,
|
||||
"NavLat": 37.55716389,
|
||||
"NavLon": 24.2987,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 123,
|
||||
"Distance": 17,
|
||||
"Alt": "06000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 210,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85250,
|
||||
"TerminalID": 10657,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 253105,
|
||||
"WptLat": 37.55716389,
|
||||
"WptLon": 24.2987,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "24000B",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85251,
|
||||
"TerminalID": 10657,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 166620,
|
||||
"WptLat": 37.20805556,
|
||||
"WptLon": 24.83444444,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,110 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 85257,
|
||||
"TerminalID": 10659,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CA",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 32,
|
||||
"Distance": null,
|
||||
"Alt": "00710A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85258,
|
||||
"TerminalID": 10659,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "FD",
|
||||
"WptID": 253138,
|
||||
"WptLat": 37.91801944,
|
||||
"WptLon": 23.93798333,
|
||||
"TurnDir": "",
|
||||
"NavID": 4040,
|
||||
"NavLat": 37.91801944,
|
||||
"NavLon": 23.93798333,
|
||||
"NavBear": 0,
|
||||
"NavDist": 0,
|
||||
"Course": 32,
|
||||
"Distance": 12,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85259,
|
||||
"TerminalID": 10659,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 253105,
|
||||
"WptLat": 37.55716389,
|
||||
"WptLon": 24.2987,
|
||||
"TurnDir": "R",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "24000B06000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 210,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85260,
|
||||
"TerminalID": 10659,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 166620,
|
||||
"WptLat": 37.20805556,
|
||||
"WptLon": 24.83444444,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,110 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 85331,
|
||||
"TerminalID": 10679,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CA",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 32,
|
||||
"Distance": null,
|
||||
"Alt": "00710A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85332,
|
||||
"TerminalID": 10679,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 166630,
|
||||
"WptLat": 38.06507222,
|
||||
"WptLon": 24.05061944,
|
||||
"TurnDir": "",
|
||||
"NavID": 4032,
|
||||
"NavLat": 37.91683889,
|
||||
"NavLon": 23.91443889,
|
||||
"NavBear": 31,
|
||||
"NavDist": 11,
|
||||
"Course": 31,
|
||||
"Distance": 10,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85333,
|
||||
"TerminalID": 10679,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CR",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "L",
|
||||
"NavID": 3985,
|
||||
"NavLat": 37.88867778,
|
||||
"NavLon": 23.80449722,
|
||||
"NavBear": 25,
|
||||
"NavDist": null,
|
||||
"Course": 253,
|
||||
"Distance": null,
|
||||
"Alt": "06000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 240,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 85334,
|
||||
"TerminalID": 10679,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 247838,
|
||||
"WptLat": 37.93041111,
|
||||
"WptLon": 22.9359,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,218 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 90645,
|
||||
"TerminalID": 11798,
|
||||
"Type": "1",
|
||||
"Transition": "RW34L",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 169637,
|
||||
"WptLat": 39.36545,
|
||||
"WptLon": 8.96781667,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 90646,
|
||||
"TerminalID": 11798,
|
||||
"Type": "1",
|
||||
"Transition": "RW34L",
|
||||
"TrackCode": "FC",
|
||||
"WptID": 169637,
|
||||
"WptLat": 39.36545,
|
||||
"WptLon": 8.96781667,
|
||||
"TurnDir": "",
|
||||
"NavID": 4103,
|
||||
"NavLat": 39.38108333,
|
||||
"NavLon": 8.96566667,
|
||||
"NavBear": 170.9,
|
||||
"NavDist": 0.9,
|
||||
"Course": 344,
|
||||
"Distance": 12,
|
||||
"Alt": "05000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 90647,
|
||||
"TerminalID": 11798,
|
||||
"Type": "1",
|
||||
"Transition": "RW34L",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 247859,
|
||||
"WptLat": 39.36372222,
|
||||
"WptLon": 8.97402778,
|
||||
"TurnDir": "L",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 270,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 90648,
|
||||
"TerminalID": 11798,
|
||||
"Type": "1",
|
||||
"Transition": "RW34R",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 169638,
|
||||
"WptLat": 39.36699722,
|
||||
"WptLon": 8.96985833,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 90649,
|
||||
"TerminalID": 11798,
|
||||
"Type": "1",
|
||||
"Transition": "RW34R",
|
||||
"TrackCode": "FC",
|
||||
"WptID": 169638,
|
||||
"WptLat": 39.36699722,
|
||||
"WptLon": 8.96985833,
|
||||
"TurnDir": "",
|
||||
"NavID": 4103,
|
||||
"NavLat": 39.38108333,
|
||||
"NavLon": 8.96566667,
|
||||
"NavBear": 164,
|
||||
"NavDist": 0.9,
|
||||
"Course": 344,
|
||||
"Distance": 12,
|
||||
"Alt": "05000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 90650,
|
||||
"TerminalID": 11798,
|
||||
"Type": "1",
|
||||
"Transition": "RW34R",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 247859,
|
||||
"WptLat": 39.36372222,
|
||||
"WptLon": 8.97402778,
|
||||
"TurnDir": "L",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 270,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 90651,
|
||||
"TerminalID": 11798,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 247859,
|
||||
"WptLat": 39.36372222,
|
||||
"WptLon": 8.97402778,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 90652,
|
||||
"TerminalID": 11798,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 253189,
|
||||
"WptLat": 39.11136111,
|
||||
"WptLon": 9.50802778,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,623 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 91603,
|
||||
"TerminalID": 11909,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CA",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 346,
|
||||
"Distance": null,
|
||||
"Alt": "01170A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91604,
|
||||
"TerminalID": 11909,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CD",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": 4169,
|
||||
"NavLat": 45.64077778,
|
||||
"NavLon": 8.73483333,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 356,
|
||||
"Distance": 4.1,
|
||||
"Alt": "02200A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91605,
|
||||
"TerminalID": 11909,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "VR",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": 4169,
|
||||
"NavLat": 45.64077778,
|
||||
"NavLon": 8.73483333,
|
||||
"NavBear": 9,
|
||||
"NavDist": null,
|
||||
"Course": 41,
|
||||
"Distance": null,
|
||||
"Alt": "03000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 220,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91606,
|
||||
"TerminalID": 11909,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 169914,
|
||||
"WptLat": 45.72388889,
|
||||
"WptLon": 8.86777778,
|
||||
"TurnDir": "R",
|
||||
"NavID": 4204,
|
||||
"NavLat": 45.64597222,
|
||||
"NavLon": 9.02158333,
|
||||
"NavBear": 302.9,
|
||||
"NavDist": 8,
|
||||
"Course": 123,
|
||||
"Distance": 2,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91607,
|
||||
"TerminalID": 11909,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CA",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 173,
|
||||
"Distance": null,
|
||||
"Alt": "04000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 230,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91608,
|
||||
"TerminalID": 11909,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 253267,
|
||||
"WptLat": 45.64077778,
|
||||
"WptLon": 8.73483333,
|
||||
"TurnDir": "R",
|
||||
"NavID": 4204,
|
||||
"NavLat": 45.64597222,
|
||||
"NavLon": 9.02158333,
|
||||
"NavBear": 265.6,
|
||||
"NavDist": 12.1,
|
||||
"Course": 266,
|
||||
"Distance": 5,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 230,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91609,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "AOS5X",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 253267,
|
||||
"WptLat": 45.64077778,
|
||||
"WptLon": 8.73483333,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91610,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "AOS5X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 169921,
|
||||
"WptLat": 45.57482778,
|
||||
"WptLon": 8.31563056,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91611,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "AOS5X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 169922,
|
||||
"WptLat": 45.59140556,
|
||||
"WptLon": 8.24456111,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "13500A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91612,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "AOS5X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 169932,
|
||||
"WptLat": 45.66769722,
|
||||
"WptLon": 7.91445,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "16000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91613,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "AOS5X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42141,
|
||||
"WptLat": 45.79638889,
|
||||
"WptLon": 7.34583333,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "19000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91614,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "LAG9X",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 253267,
|
||||
"WptLat": 45.64077778,
|
||||
"WptLon": 8.73483333,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91615,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "LAG9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42354,
|
||||
"WptLat": 45.55416667,
|
||||
"WptLon": 8.44916667,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91616,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "LAG9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42823,
|
||||
"WptLat": 45.10916667,
|
||||
"WptLon": 8.22972222,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91617,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "LAG9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42487,
|
||||
"WptLat": 44.39416667,
|
||||
"WptLon": 8.49805556,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "11000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91618,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "NED9X",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 253267,
|
||||
"WptLat": 45.64077778,
|
||||
"WptLon": 8.73483333,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91619,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "NED9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42354,
|
||||
"WptLat": 45.55416667,
|
||||
"WptLon": 8.44916667,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91620,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "NED9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42823,
|
||||
"WptLat": 45.10916667,
|
||||
"WptLon": 8.22972222,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91621,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "NED9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42584,
|
||||
"WptLat": 44.69388889,
|
||||
"WptLon": 8.14055556,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "20000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91622,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "TOP9X",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 253267,
|
||||
"WptLat": 45.64077778,
|
||||
"WptLon": 8.73483333,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91623,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "TOP9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42354,
|
||||
"WptLat": 45.55416667,
|
||||
"WptLon": 8.44916667,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91624,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "TOP9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 42823,
|
||||
"WptLat": 45.10916667,
|
||||
"WptLon": 8.22972222,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 91625,
|
||||
"TerminalID": 11909,
|
||||
"Type": "3",
|
||||
"Transition": "TOP9X",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 253309,
|
||||
"WptLat": 44.92541667,
|
||||
"WptLon": 7.86166667,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,83 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 97919,
|
||||
"TerminalID": 12765,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 253373,
|
||||
"WptLat": 46.95531111,
|
||||
"WptLon": 15.44944444,
|
||||
"TurnDir": "",
|
||||
"NavID": 4275,
|
||||
"NavLat": 46.95531111,
|
||||
"NavLon": 15.44944444,
|
||||
"NavBear": 0,
|
||||
"NavDist": 0,
|
||||
"Course": 164,
|
||||
"Distance": 2,
|
||||
"Alt": "02200A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 97920,
|
||||
"TerminalID": 12765,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 253373,
|
||||
"WptLat": 46.95531111,
|
||||
"WptLon": 15.44944444,
|
||||
"TurnDir": "L",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "04000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 97921,
|
||||
"TerminalID": 12765,
|
||||
"Type": "2",
|
||||
"Transition": "ALL",
|
||||
"TrackCode": "HA",
|
||||
"WptID": 253373,
|
||||
"WptLat": 46.95531111,
|
||||
"WptLon": 15.44944444,
|
||||
"TurnDir": "R",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 344,
|
||||
"Distance": 4,
|
||||
"Alt": "04000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,272 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 458535,
|
||||
"TerminalID": 67790,
|
||||
"Type": "A",
|
||||
"Transition": "UPV1",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 250431,
|
||||
"WptLat": -28.40175278,
|
||||
"WptLon": 21.26041111,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "05800A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458536,
|
||||
"TerminalID": 67790,
|
||||
"Type": "A",
|
||||
"Transition": "UPV1",
|
||||
"TrackCode": "PI",
|
||||
"WptID": 250431,
|
||||
"WptLat": -28.40175278,
|
||||
"WptLon": 21.26041111,
|
||||
"TurnDir": "L",
|
||||
"NavID": 1333,
|
||||
"NavLat": -28.40175278,
|
||||
"NavLon": 21.26041111,
|
||||
"NavBear": 0,
|
||||
"NavDist": 0,
|
||||
"Course": 213,
|
||||
"Distance": 10,
|
||||
"Alt": "04500A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458537,
|
||||
"TerminalID": 67790,
|
||||
"Type": "A",
|
||||
"Transition": "UPV1",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 113102,
|
||||
"WptLat": -28.51632222,
|
||||
"WptLon": 21.33836389,
|
||||
"TurnDir": "",
|
||||
"NavID": 1333,
|
||||
"NavLat": -28.40175278,
|
||||
"NavLon": 21.26041111,
|
||||
"NavBear": 168,
|
||||
"NavDist": 8,
|
||||
"Course": 348,
|
||||
"Distance": 2,
|
||||
"Alt": "04500A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458538,
|
||||
"TerminalID": 67790,
|
||||
"Type": "A",
|
||||
"Transition": "UPV2",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 250431,
|
||||
"WptLat": -28.40175278,
|
||||
"WptLon": 21.26041111,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "05800A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458539,
|
||||
"TerminalID": 67790,
|
||||
"Type": "A",
|
||||
"Transition": "UPV2",
|
||||
"TrackCode": "HF",
|
||||
"WptID": 250431,
|
||||
"WptLat": -28.40175278,
|
||||
"WptLon": 21.26041111,
|
||||
"TurnDir": "L",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 348,
|
||||
"Distance": 8,
|
||||
"Alt": "04500A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458540,
|
||||
"TerminalID": 67790,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 113102,
|
||||
"WptLat": -28.51632222,
|
||||
"WptLon": 21.33836389,
|
||||
"TurnDir": "",
|
||||
"NavID": 1333,
|
||||
"NavLat": -28.40175278,
|
||||
"NavLon": 21.26041111,
|
||||
"NavBear": 168,
|
||||
"NavDist": 8,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "04500A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": -1,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458541,
|
||||
"TerminalID": 67790,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "CF",
|
||||
"WptID": null,
|
||||
"WptLat": -28.41938889,
|
||||
"WptLon": 21.27444444,
|
||||
"TurnDir": "",
|
||||
"NavID": 1333,
|
||||
"NavLat": -28.40175278,
|
||||
"NavLon": 21.26041111,
|
||||
"NavBear": 163.9,
|
||||
"NavDist": 1.3,
|
||||
"Course": 348,
|
||||
"Distance": 6.7,
|
||||
"Alt": "MAP",
|
||||
"Vnav": 2.45,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": -1
|
||||
},
|
||||
{
|
||||
"ID": 458542,
|
||||
"TerminalID": 67790,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 250431,
|
||||
"WptLat": -28.40175278,
|
||||
"WptLon": 21.26041111,
|
||||
"TurnDir": "",
|
||||
"NavID": 1333,
|
||||
"NavLat": -28.40175278,
|
||||
"NavLon": 21.26041111,
|
||||
"NavBear": 0,
|
||||
"NavDist": 0,
|
||||
"Course": 348,
|
||||
"Distance": 1.3,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458543,
|
||||
"TerminalID": 67790,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "CA",
|
||||
"WptID": null,
|
||||
"WptLat": null,
|
||||
"WptLon": null,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 348,
|
||||
"Distance": null,
|
||||
"Alt": "05500A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458544,
|
||||
"TerminalID": 67790,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 250431,
|
||||
"WptLat": -28.40175278,
|
||||
"WptLon": 21.26041111,
|
||||
"TurnDir": "L",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "05800",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
@@ -1,353 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ID": 458580,
|
||||
"TerminalID": 67794,
|
||||
"Type": "A",
|
||||
"Transition": "FD291",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 113125,
|
||||
"WptLat": -25.65088056,
|
||||
"WptLon": 28.29919167,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "08000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458581,
|
||||
"TerminalID": 67794,
|
||||
"Type": "A",
|
||||
"Transition": "FD291",
|
||||
"TrackCode": "HF",
|
||||
"WptID": 113125,
|
||||
"WptLat": -25.65088056,
|
||||
"WptLon": 28.29919167,
|
||||
"TurnDir": "R",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 284,
|
||||
"Distance": 5.5,
|
||||
"Alt": "07000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 140,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458582,
|
||||
"TerminalID": 67794,
|
||||
"Type": "A",
|
||||
"Transition": "FD292",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 113125,
|
||||
"WptLat": -25.65088056,
|
||||
"WptLon": 28.29919167,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "07000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458583,
|
||||
"TerminalID": 67794,
|
||||
"Type": "A",
|
||||
"Transition": "FD292",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 113123,
|
||||
"WptLat": -25.64774167,
|
||||
"WptLon": 28.34813056,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "07000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458584,
|
||||
"TerminalID": 67794,
|
||||
"Type": "A",
|
||||
"Transition": "FD292",
|
||||
"TrackCode": "FC",
|
||||
"WptID": 113123,
|
||||
"WptLat": -25.64774167,
|
||||
"WptLon": 28.34813056,
|
||||
"TurnDir": "",
|
||||
"NavID": 1283,
|
||||
"NavLat": -25.65591667,
|
||||
"NavLon": 28.220075,
|
||||
"NavBear": 104,
|
||||
"NavDist": 7,
|
||||
"Course": 59,
|
||||
"Distance": 2.7,
|
||||
"Alt": "07000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": -1,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458585,
|
||||
"TerminalID": 67794,
|
||||
"Type": "A",
|
||||
"Transition": "FD292",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 113123,
|
||||
"WptLat": -25.64774167,
|
||||
"WptLon": 28.34813056,
|
||||
"TurnDir": "R",
|
||||
"NavID": 1283,
|
||||
"NavLat": -25.65591667,
|
||||
"NavLon": 28.220075,
|
||||
"NavBear": 104,
|
||||
"NavDist": 7,
|
||||
"Course": 284,
|
||||
"Distance": 3,
|
||||
"Alt": "07000A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458586,
|
||||
"TerminalID": 67794,
|
||||
"Type": "A",
|
||||
"Transition": "FD292",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 113125,
|
||||
"WptLat": -25.65088056,
|
||||
"WptLon": 28.29919167,
|
||||
"TurnDir": "",
|
||||
"NavID": 1283,
|
||||
"NavLat": -25.65591667,
|
||||
"NavLon": 28.220075,
|
||||
"NavBear": 104,
|
||||
"NavDist": 4.3,
|
||||
"Course": 284,
|
||||
"Distance": 2.7,
|
||||
"Alt": "05300A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458587,
|
||||
"TerminalID": 67794,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "IF",
|
||||
"WptID": 113125,
|
||||
"WptLat": -25.65088056,
|
||||
"WptLon": 28.29919167,
|
||||
"TurnDir": "",
|
||||
"NavID": 1283,
|
||||
"NavLat": -25.65591667,
|
||||
"NavLon": 28.220075,
|
||||
"NavBear": 104,
|
||||
"NavDist": 4.3,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "05300A",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": -1,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458588,
|
||||
"TerminalID": 67794,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "CF",
|
||||
"WptID": 113127,
|
||||
"WptLat": -25.65404722,
|
||||
"WptLon": 28.24951389,
|
||||
"TurnDir": "",
|
||||
"NavID": 1283,
|
||||
"NavLat": -25.65591667,
|
||||
"NavLon": 28.220075,
|
||||
"NavBear": 104,
|
||||
"NavDist": 1.6,
|
||||
"Course": 284,
|
||||
"Distance": 2.7,
|
||||
"Alt": "MAP",
|
||||
"Vnav": 3,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": -1
|
||||
},
|
||||
{
|
||||
"ID": 458589,
|
||||
"TerminalID": 67794,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "FD",
|
||||
"WptID": 113127,
|
||||
"WptLat": -25.65404722,
|
||||
"WptLon": 28.24951389,
|
||||
"TurnDir": "",
|
||||
"NavID": 1283,
|
||||
"NavLat": -25.65591667,
|
||||
"NavLon": 28.220075,
|
||||
"NavBear": 104,
|
||||
"NavDist": 1.6,
|
||||
"Course": 284,
|
||||
"Distance": 2.2,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458590,
|
||||
"TerminalID": 67794,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "DF",
|
||||
"WptID": 250381,
|
||||
"WptLat": -25.65591667,
|
||||
"WptLon": 28.220075,
|
||||
"TurnDir": "R",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458591,
|
||||
"TerminalID": 67794,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "TF",
|
||||
"WptID": 113125,
|
||||
"WptLat": -25.65088056,
|
||||
"WptLon": 28.29919167,
|
||||
"TurnDir": "",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": null,
|
||||
"Distance": null,
|
||||
"Alt": "07000",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": null,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
},
|
||||
{
|
||||
"ID": 458592,
|
||||
"TerminalID": 67794,
|
||||
"Type": "D",
|
||||
"Transition": "",
|
||||
"TrackCode": "HM",
|
||||
"WptID": 113125,
|
||||
"WptLat": -25.65088056,
|
||||
"WptLon": 28.29919167,
|
||||
"TurnDir": "R",
|
||||
"NavID": null,
|
||||
"NavLat": null,
|
||||
"NavLon": null,
|
||||
"NavBear": null,
|
||||
"NavDist": null,
|
||||
"Course": 284,
|
||||
"Distance": 4,
|
||||
"Alt": "",
|
||||
"Vnav": null,
|
||||
"CenterID": null,
|
||||
"CenterLat": null,
|
||||
"CenterLon": null,
|
||||
"IsFlyOver": 0,
|
||||
"SpeedLimit": 140,
|
||||
"IsFAF": 0,
|
||||
"IsMAP": 0
|
||||
}
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,89 +0,0 @@
|
||||
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 { Sidebar } from './components/Sidebar';
|
||||
import { useNavigraphAuth } from './contexts/NavigraphAuth/NavigraphAuthContext';
|
||||
import Parser from './parser/parser';
|
||||
|
||||
const parser = await Parser.instance();
|
||||
|
||||
function App() {
|
||||
const [selectedAirport, setSelectedAirport] = useState<Airport>();
|
||||
const [selectedRunway, setSelectedRunway] = useState<Runway>();
|
||||
const [selectedTerminal, setSelectedTerminal] = useState<Terminal>();
|
||||
const [transitions, setTransitions] = useState<Procedure[]>([]);
|
||||
const [params, setParams] = useState<DeviceFlowParams | null>(null);
|
||||
const [selectedTransition, setSelectedTransition] = useState<Procedure>();
|
||||
const [selectedChart, setSelectedChart] = useState<Chart>();
|
||||
|
||||
const { user, signIn, initialized } = useNavigraphAuth();
|
||||
|
||||
const handleSignIn = () => signIn((p) => setParams(p));
|
||||
|
||||
return (
|
||||
<>
|
||||
{transitions.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) => {
|
||||
const _transitions = selectedTransitions.map((transition) => ({
|
||||
name: transition,
|
||||
data: parser.parse(selectedRunway!, transition),
|
||||
}));
|
||||
setTransitions(_transitions);
|
||||
setSelectedTransition(_transitions[0]);
|
||||
setSelectedChart(undefined);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-dvh w-dvw">
|
||||
{transitions.length > 0 && selectedAirport && selectedTerminal ? (
|
||||
<>
|
||||
<Sidebar
|
||||
airport={selectedAirport}
|
||||
terminal={selectedTerminal}
|
||||
transitions={transitions}
|
||||
transition={selectedTransition}
|
||||
chart={selectedChart}
|
||||
setTransition={setSelectedTransition}
|
||||
setChart={setSelectedChart}
|
||||
backAction={() => {
|
||||
setSelectedTerminal(undefined);
|
||||
setTransitions([]);
|
||||
}}
|
||||
/>
|
||||
<Map airport={selectedAirport} chart={selectedChart} procedure={selectedTransition} />
|
||||
</>
|
||||
) : (
|
||||
<h1 className="text-center text-3xl">Error</h1>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,125 +0,0 @@
|
||||
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,109 +0,0 @@
|
||||
import BrowserImageManipulation from 'browser-image-manipulation';
|
||||
import L from 'leaflet';
|
||||
import type { Chart as NGChart } from 'navigraph/charts';
|
||||
import { useEffect, useState, type Dispatch, type FC, type SetStateAction } from 'react';
|
||||
import { charts } from '../lib/navigraph';
|
||||
|
||||
interface SidebarProps {
|
||||
airport: Airport;
|
||||
terminal: Terminal;
|
||||
transitions: Procedure[];
|
||||
transition: Procedure | undefined;
|
||||
chart: Chart | undefined;
|
||||
setTransition: Dispatch<SetStateAction<SidebarProps['transition']>>;
|
||||
setChart: Dispatch<SetStateAction<SidebarProps['chart']>>;
|
||||
backAction: () => void;
|
||||
}
|
||||
|
||||
export const Sidebar: FC<SidebarProps> = ({
|
||||
airport,
|
||||
terminal,
|
||||
transitions,
|
||||
transition,
|
||||
chart,
|
||||
setTransition,
|
||||
setChart,
|
||||
backAction,
|
||||
}) => {
|
||||
const [chartIndex, setChartIndex] = useState<NGChart[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setChartIndex((await charts.getChartsIndex({ icao: airport.ICAO, version: 'STD' })) ?? []);
|
||||
})();
|
||||
}, [airport.ICAO]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-[300px] shrink-0 flex-col overflow-hidden">
|
||||
<button
|
||||
className="sticky top-0 m-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={backAction}
|
||||
>
|
||||
Go back
|
||||
</button>
|
||||
|
||||
<div className="flex h-[calc(100%-50px)] flex-col gap-2 overflow-y-auto px-2 pb-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-0 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">
|
||||
Transitions for <span className="font-bold">{terminal.FullName}</span>
|
||||
</div>
|
||||
{transitions.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 ${_procedure.name === transition?.name ? 'outline-2' : ''}`}
|
||||
onClick={() => {
|
||||
if (_procedure.name === transition?.name) return;
|
||||
|
||||
setTransition(_procedure);
|
||||
}}
|
||||
>
|
||||
{_procedure.name ? _procedure.name : 'ZZZZ'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-0 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">Charts</div>
|
||||
{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 text-left font-semibold focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${chart?.index_number === _chart.index_number ? 'outline-2' : ''}`}
|
||||
onClick={async () => {
|
||||
if (chart?.index_number === _chart.index_number) return;
|
||||
if (!_chart.bounding_boxes) return;
|
||||
|
||||
const planView = _chart.bounding_boxes.planview;
|
||||
|
||||
const chartImage = await charts.getChartImage({ chart: _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]
|
||||
);
|
||||
|
||||
setChart({ data: dataURL, index_number: _chart.index_number, bounds });
|
||||
}}
|
||||
>
|
||||
<span className="font-bold">{_chart.index_number}</span>
|
||||
<br />
|
||||
{_chart.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,94 +0,0 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import Parser from '../parser';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
export const TerminatorsCF = (
|
||||
leg: CFTerminalEntry,
|
||||
previousFix: NavFix,
|
||||
lastCourse: number,
|
||||
waypoint?: Waypoint
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const crsIntoEndpoint = leg.Course.toTrue(previousFix);
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
const targetFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoint?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
};
|
||||
const crsToIntercept = leg.Course.toTrue(targetFix);
|
||||
|
||||
// Compute overfly arc
|
||||
if (previousFix.isFlyOver && !lastCourse.equal(crsIntoEndpoint)) {
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
let updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
// Turn Dir
|
||||
if (!leg.TurnDir || leg.TurnDir === 'E') {
|
||||
let prov = lastCourse - crsIntoEndpoint;
|
||||
prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
|
||||
leg.TurnDir = prov > 0 ? 'L' : 'R';
|
||||
}
|
||||
|
||||
// Generate arc
|
||||
while (!updatedCrsToIntercept.equal(crsToIntercept)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
//const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||
const increment = 1; //delta < 1 ? delta : 1;
|
||||
lastCourse = (lastCourse + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
//const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = 1; //delta < 1 ? delta : 1;
|
||||
lastCourse = (lastCourse - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
lastCourse
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
|
||||
// Update previousFix
|
||||
previousFix.latitude = arcFix.latitude;
|
||||
previousFix.longitude = arcFix.longitude;
|
||||
updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
let interceptAngle = 0;
|
||||
if (leg.TurnDir === 'R') Math.abs((interceptAngle = lastCourse - crsToIntercept));
|
||||
else interceptAngle = Math.abs(crsToIntercept - lastCourse);
|
||||
|
||||
if (interceptAngle >= 45) break;
|
||||
}
|
||||
}
|
||||
|
||||
const interceptFix: NavFix = {
|
||||
...computeIntersection(previousFix, leg.Course.toTrue(previousFix), targetFix, crsToIntercept.reciprocalCourse())!,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
};
|
||||
if (interceptFix.latitude) line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
return [targetFix, line];
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsDF = (
|
||||
leg: DFTerminalEntry,
|
||||
previousFix: NavFix,
|
||||
lastCourse: number,
|
||||
waypoint?: Waypoint
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
|
||||
const targetFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoint?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
};
|
||||
|
||||
const crsIntoEndpoint = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
// Compute overfly
|
||||
const [line, _, _lastCourse] = generateOverflyArc(
|
||||
crsIntoEndpoint,
|
||||
lastCourse,
|
||||
previousFix,
|
||||
speed,
|
||||
leg.TurnDir,
|
||||
previousFix.latitude.equal(targetFix.latitude) && previousFix.longitude.equal(targetFix.longitude)
|
||||
);
|
||||
lastCourse = _lastCourse;
|
||||
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
return [targetFix, line];
|
||||
};
|
||||
@@ -1,77 +0,0 @@
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
|
||||
export const TerminatorsTF = (
|
||||
leg: TFTerminalEntry,
|
||||
previousFix: NavFix,
|
||||
lastCourse: number,
|
||||
waypoint?: Waypoint
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const targetFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoint?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: computeSpeed(leg, previousFix),
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
};
|
||||
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
const trackIntoEndpoint = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
if (previousFix.isFlyOver) {
|
||||
let crsIntoEndpoint = trackIntoEndpoint;
|
||||
|
||||
// Check if there even is an arc
|
||||
if (crsIntoEndpoint !== lastCourse) {
|
||||
// Turn Dir
|
||||
if (!leg.TurnDir || leg.TurnDir === 'E') {
|
||||
let prov = lastCourse - crsIntoEndpoint;
|
||||
prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
|
||||
leg.TurnDir = prov > 0 ? 'L' : 'R';
|
||||
}
|
||||
|
||||
// Generate arc
|
||||
let condition = false;
|
||||
do {
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||
lastCourse += delta < 1 ? delta : 1;
|
||||
lastCourse = lastCourse.normaliseDegrees();
|
||||
} else {
|
||||
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||
lastCourse -= delta < 1 ? delta : 1;
|
||||
lastCourse = lastCourse.normaliseDegrees();
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 3600).toMetre(),
|
||||
lastCourse
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
|
||||
crsIntoEndpoint = getGreatCircleBearing(arcFix, targetFix);
|
||||
|
||||
if (leg.TurnDir === 'R') {
|
||||
condition = crsIntoEndpoint > trackIntoEndpoint;
|
||||
} else {
|
||||
condition = crsIntoEndpoint < trackIntoEndpoint;
|
||||
}
|
||||
} while (condition);
|
||||
}
|
||||
}
|
||||
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
return [targetFix, line];
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
import { magvar } from 'magvar';
|
||||
|
||||
Number.prototype.toRadians = function () {
|
||||
return ((this as number) * Math.PI) / 180;
|
||||
};
|
||||
Number.prototype.toDegrees = function () {
|
||||
return ((this as number) * 180) / Math.PI;
|
||||
};
|
||||
Number.prototype.reciprocalCourse = function () {
|
||||
let inv = (this as number) + 180;
|
||||
inv = inv >= 360 ? inv - 360 : inv;
|
||||
return inv;
|
||||
};
|
||||
Number.prototype.normaliseDegrees = function () {
|
||||
return (this as number) >= 360
|
||||
? (this as number) - 360
|
||||
: (this as number) < 0
|
||||
? (this as number) + 360
|
||||
: (this as number);
|
||||
};
|
||||
Number.prototype.toTrue = function (fix) {
|
||||
const _magvar = magvar(fix.latitude, fix.longitude); //Magvar is returned + for East
|
||||
return ((this as number) + _magvar).normaliseDegrees();
|
||||
};
|
||||
Number.prototype.toMetre = function () {
|
||||
return (this as number) * 1852.0;
|
||||
};
|
||||
Number.prototype.equal = function (other: number) {
|
||||
return Math.abs((this as number) - other) < 0.1;
|
||||
};
|
||||
|
||||
String.prototype.parseAltitude = function () {
|
||||
switch (this.length) {
|
||||
case 5:
|
||||
return Number.parseInt(this as string);
|
||||
case 6:
|
||||
return Number.parseInt(this.substring(0, 5));
|
||||
case 12: {
|
||||
const upper = Number.parseInt(this.substring(0, 5));
|
||||
const lower = Number.parseInt(this.substring(6, 12));
|
||||
return lower + (upper - lower) / 2;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
import readline from 'readline';
|
||||
|
||||
export const prompting = (message: string) => {
|
||||
return new Promise<string | null>((resolve) => {
|
||||
if (typeof prompt !== 'undefined') {
|
||||
return resolve(prompt(message));
|
||||
}
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
rl.question(`${message}: `, (id) => {
|
||||
rl.close();
|
||||
|
||||
resolve(id);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
dist
|
||||
@@ -0,0 +1 @@
|
||||
v22
|
||||
@@ -0,0 +1,19 @@
|
||||
FROM node:lts-alpine AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS prod-deps
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
|
||||
|
||||
FROM base AS build
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
RUN pnpm run build
|
||||
|
||||
FROM base
|
||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
||||
COPY --from=build /app/dist /app/dist
|
||||
EXPOSE 3003
|
||||
ENTRYPOINT [ "pnpm", "serve" ]
|
||||
@@ -0,0 +1,27 @@
|
||||
networks:
|
||||
traefik:
|
||||
name: traefik
|
||||
|
||||
services:
|
||||
md11-nav-data:
|
||||
build: .
|
||||
image: md11-nav-data
|
||||
container_name: md11-nav-data-server
|
||||
volumes:
|
||||
- /Volumes/Data/DockerMD11NavData/NavData:/app/dist/NavData
|
||||
environment:
|
||||
PORT: 3003
|
||||
env_file: ".env"
|
||||
restart: always
|
||||
networks:
|
||||
- traefik
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.md11-nav-data-router.rule=Host(`md11-nav-data.hofmannnet.myhome-server.de`)"
|
||||
- "traefik.http.routers.md11-nav-data-router.service=md11-nav-data-service"
|
||||
- "traefik.http.services.md11-nav-data-service.loadbalancer.server.port=3003"
|
||||
- "traefik.http.routers.md11-nav-data-router.entrypoints=websecure"
|
||||
- "traefik.http.routers.md11-nav-data-router.tls=true"
|
||||
- "traefik.http.routers.md11-nav-data-router.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.md11-nav-data-router.middlewares=md11-nav-data-auth"
|
||||
- "traefik.http.middlewares.md11-nav-data-auth.basicauth.users=tfdi:$$apr1$$gN9W1XPy$$hucbFbJGcMv/GaUgiZ/mX."
|
||||
@@ -8,7 +8,7 @@
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint . --ext .ts,.tsx --fix",
|
||||
"preview": "vite preview",
|
||||
"parser": "node --experimental-strip-types src/parser/node.ts"
|
||||
"serve": "pnpm npx http-server dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"browser-image-manipulation": "^0.4.0",
|
||||
@@ -16,7 +16,6 @@
|
||||
"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",
|
||||
@@ -23,9 +23,6 @@ importers:
|
||||
leaflet-svg-shape-markers:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
magvar:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
navigraph:
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1
|
||||
@@ -1102,9 +1099,6 @@ packages:
|
||||
magic-string@0.30.17:
|
||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||
|
||||
magvar@2.0.0:
|
||||
resolution: {integrity: sha512-00LpSwEJZcnyX3VsdCM2CHSCvB+M6sVQTawLCB3J9oK7eEueNltNx9GFL4YR+/HIkLM+l8rFKiY77JTKnOf0jw==}
|
||||
|
||||
merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -2339,8 +2333,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.4
|
||||
|
||||
magvar@2.0.0: {}
|
||||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
micromatch@4.0.8:
|
||||
Binary file not shown.
@@ -0,0 +1,138 @@
|
||||
import type { DeviceFlowParams } from 'navigraph/auth';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ProcedureSelect } from './components//ProcedureSelect';
|
||||
import { Loader } from './components/Loader';
|
||||
import { Map } from './components/Map';
|
||||
import { Sidebar } from './components/Sidebar';
|
||||
import { useNavigraphAuth } from './contexts/NavigraphAuth/NavigraphAuthContext';
|
||||
import Parser from './parser/parser';
|
||||
|
||||
function App() {
|
||||
const [parser, setParser] = useState<Parser>();
|
||||
const [selectedAirport, setSelectedAirport] = useState<Airport>();
|
||||
const [selectedRunway, setSelectedRunway] = useState<Runway>();
|
||||
const [selectedTerminal, setSelectedTerminal] = useState<Terminal>();
|
||||
const [transitions, setTransitions] = useState<Procedure[]>([]);
|
||||
const [params, setParams] = useState<DeviceFlowParams | null>(null);
|
||||
const [selectedTransition, setSelectedTransition] = useState<Procedure>();
|
||||
const [selectedChart, setSelectedChart] = useState<Chart>();
|
||||
const [skippedNG, setSkippedNG] = useState(false);
|
||||
|
||||
const { user, signIn, initialized } = useNavigraphAuth();
|
||||
|
||||
const handleSignIn = () => signIn((p) => setParams(p));
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setParser(await Parser.instance());
|
||||
})();
|
||||
}, []);
|
||||
|
||||
if (!parser || !initialized) {
|
||||
return (
|
||||
<div className="flex min-h-dvh w-full">
|
||||
<div className="flex h-dvh w-dvw flex-col items-center justify-center gap-2">
|
||||
<Loader size={3} />
|
||||
{!parser && <span className="text-3xl">Initialising Parser...</span>}
|
||||
{!initialized && <span className="text-3xl">Initialising Navigraph SDK...</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{!user && !skippedNG ? (
|
||||
<div className="flex min-h-dvh w-full">
|
||||
{initialized && !params && (
|
||||
<div className="flex h-dvh w-dvw flex-col items-center justify-center gap-2">
|
||||
<button
|
||||
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={handleSignIn}
|
||||
>
|
||||
Sign in to NG
|
||||
</button>
|
||||
<button
|
||||
className="cursor-pointer rounded border border-gray-600 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2"
|
||||
onClick={() => setSkippedNG(true)}
|
||||
>
|
||||
Skip NG
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{params?.verification_uri_complete && (
|
||||
<div className="flex h-dvh w-dvw flex-col items-center justify-center">
|
||||
<QRCodeSVG value={params.verification_uri_complete} size={250} />
|
||||
|
||||
<div className="flex w-100 items-center text-center before:mr-1 before:flex-1 before:border-b-1 before:content-[''] after:ml-1 after:flex-1 after:border-b-1 after:content-['']">
|
||||
or
|
||||
</div>
|
||||
|
||||
<a className="text-3xl" href={params.verification_uri_complete} target="_blank" rel="noreferrer">
|
||||
Open sign in page
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-dvh w-dvw">
|
||||
{selectedAirport && selectedRunway && selectedTerminal ? (
|
||||
<Sidebar
|
||||
airport={selectedAirport}
|
||||
runway={selectedRunway}
|
||||
terminal={selectedTerminal}
|
||||
transitions={transitions}
|
||||
transition={selectedTransition}
|
||||
chart={selectedChart}
|
||||
skippedNG={skippedNG}
|
||||
setTransition={setSelectedTransition}
|
||||
setChart={setSelectedChart}
|
||||
backAction={() => {
|
||||
setSelectedTerminal(undefined);
|
||||
setSelectedChart(undefined);
|
||||
setTransitions([]);
|
||||
setSelectedTransition(undefined);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ProcedureSelect
|
||||
parser={parser}
|
||||
selectedAirport={selectedAirport}
|
||||
selectedRunway={selectedRunway}
|
||||
selectedTerminal={selectedTerminal}
|
||||
setSelectedAirport={setSelectedAirport}
|
||||
setSelectedRunway={setSelectedRunway}
|
||||
setSelectedTerminal={setSelectedTerminal}
|
||||
handleSelection={(selectedTransitions, _terminal) => {
|
||||
let _transitions = selectedTransitions
|
||||
.map((transition) => ({
|
||||
name: transition,
|
||||
data: parser.parse(selectedRunway!, _terminal, transition),
|
||||
}))
|
||||
.filter(
|
||||
(transition) =>
|
||||
!transition.name.startsWith('RW') || transition.name === `RW${selectedRunway?.Ident}`
|
||||
);
|
||||
|
||||
if (_transitions.length > 1)
|
||||
_transitions = _transitions.filter((transition) => transition.name !== 'ALL');
|
||||
setTransitions(_transitions);
|
||||
setSelectedTransition(_transitions[0]);
|
||||
setSelectedChart(undefined);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Map
|
||||
airport={selectedAirport}
|
||||
chart={selectedChart}
|
||||
procedures={selectedTransition ? [selectedTransition] : []}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { FC } from 'react';
|
||||
|
||||
interface LoaderProps {
|
||||
size?: 0.5 | 1 | 2 | 3;
|
||||
}
|
||||
|
||||
export const Loader: FC<LoaderProps> = ({ size = 2 }) => {
|
||||
const variants = {
|
||||
0.5: 'size-6 border-1 after:top-1 after:left-0.5 after:size-1.5 after:border-1',
|
||||
1: 'size-12 border-2 after:top-2 after:left-1 after:size-3 after:border-2',
|
||||
2: 'size-24 border-4 after:top-3 after:left-2 after:size-6 after:border-4',
|
||||
3: 'size-36 border-6 after:top-4 after:left-3 after:size-9 after:border-6',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<span
|
||||
className={`relative inline-block ${variants[size]} animate-spin rounded-[50%] border-red-500 after:absolute after:rounded-[50%] after:border-black after:content-['']`}
|
||||
></span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,22 +1,26 @@
|
||||
import { default as L } from 'leaflet';
|
||||
import 'leaflet-svg-shape-markers';
|
||||
import { createRef, type FC } from 'react';
|
||||
import { createRef, Fragment, useEffect, type FC } from 'react';
|
||||
import { GeoJSON, ImageOverlay, MapContainer, TileLayer } from 'react-leaflet';
|
||||
|
||||
interface MapProps {
|
||||
airport: Airport;
|
||||
airport: Airport | undefined;
|
||||
chart: Chart | undefined;
|
||||
procedure: Procedure | undefined;
|
||||
procedures: Procedure[];
|
||||
}
|
||||
|
||||
export const Map: FC<MapProps> = ({ airport, chart, procedure }) => {
|
||||
export const Map: FC<MapProps> = ({ airport, chart, procedures }) => {
|
||||
const mapRef = createRef<L.Map>();
|
||||
const imageRef = createRef<L.ImageOverlay>();
|
||||
|
||||
useEffect(() => {
|
||||
if (airport) mapRef.current?.flyTo([airport?.Latitude, airport?.Longitude], 10, { animate: false });
|
||||
}, [airport]);
|
||||
|
||||
return (
|
||||
<MapContainer
|
||||
center={[airport.Latitude, airport.Longitude]}
|
||||
zoom={13}
|
||||
center={[0, 0]}
|
||||
zoom={5}
|
||||
zoomSnap={0}
|
||||
className="h-full w-full"
|
||||
ref={(_mapRef) => {
|
||||
@@ -46,15 +50,15 @@ export const Map: FC<MapProps> = ({ airport, chart, procedure }) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{procedure && (
|
||||
<>
|
||||
{procedures.map((procedure) => (
|
||||
<Fragment key={procedure.name}>
|
||||
<GeoJSON
|
||||
key={`${procedure.name}-lines`}
|
||||
data={procedure.data}
|
||||
style={({ properties }) => ({
|
||||
color: '#ff00ff',
|
||||
color: properties.isMAP ? '#00ffff' : '#ff00ff',
|
||||
stroke: true,
|
||||
weight: 5,
|
||||
weight: properties.isMAP ? 2.5 : 5,
|
||||
opacity: 1,
|
||||
dashArray: properties.isManual ? '20, 20' : undefined,
|
||||
})}
|
||||
@@ -71,12 +75,27 @@ export const Map: FC<MapProps> = ({ airport, chart, procedure }) => {
|
||||
weight: 3,
|
||||
}}
|
||||
pointToLayer={({ properties }, latlng) => {
|
||||
if (properties.isFlyOver)
|
||||
if (properties.isIntersection) return L.circleMarker(latlng, { radius: 6 });
|
||||
else if (properties.IsMAP)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'square',
|
||||
radius: 6,
|
||||
});
|
||||
else if (properties.IsFAF)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'diamond',
|
||||
radius: 6,
|
||||
});
|
||||
else if (properties.isFlyOver)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'triangle',
|
||||
radius: 6,
|
||||
});
|
||||
if (properties.isIntersection) return L.circleMarker(latlng, { radius: 6 });
|
||||
else if (properties.isInitial)
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'arrowhead',
|
||||
radius: 6,
|
||||
});
|
||||
|
||||
return L.shapeMarker(latlng, {
|
||||
shape: 'star-4',
|
||||
@@ -98,8 +117,8 @@ export const Map: FC<MapProps> = ({ airport, chart, procedure }) => {
|
||||
}}
|
||||
filter={(feature) => feature.geometry.type === 'Point'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</MapContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
import { createRef, useMemo, useState, type Dispatch, type FC, type SetStateAction } from 'react';
|
||||
import Parser from '../parser/parser';
|
||||
import { Loader } from './Loader';
|
||||
|
||||
interface ProcedureSelectProps {
|
||||
parser: Parser;
|
||||
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[], terminal: Terminal) => void;
|
||||
}
|
||||
|
||||
export const ProcedureSelect: FC<ProcedureSelectProps> = ({
|
||||
parser,
|
||||
selectedAirport,
|
||||
selectedRunway,
|
||||
selectedTerminal,
|
||||
setSelectedAirport,
|
||||
setSelectedRunway,
|
||||
setSelectedTerminal,
|
||||
handleSelection,
|
||||
}) => {
|
||||
const inputRef = createRef<HTMLInputElement>();
|
||||
const [error, setError] = useState<string>();
|
||||
const [inFlight, setInFlight] = useState<number>();
|
||||
|
||||
const runways = useMemo(
|
||||
() => parser.runways.filter(({ AirportID }) => AirportID === selectedAirport?.ID),
|
||||
[selectedAirport]
|
||||
);
|
||||
const terminals = useMemo(() => {
|
||||
const _terminals = parser.terminals.filter(
|
||||
({ AirportID, RwyID }) => AirportID === selectedAirport?.ID && (!RwyID || RwyID === selectedRunway?.ID)
|
||||
);
|
||||
|
||||
return {
|
||||
star: _terminals.filter((terminal) => terminal.Proc === 1),
|
||||
sid: _terminals.filter((terminal) => terminal.Proc === 2),
|
||||
iap: _terminals.filter((terminal) => terminal.Proc === 3),
|
||||
};
|
||||
}, [selectedAirport, selectedRunway]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-[300px] shrink-0 flex-col overflow-hidden">
|
||||
{selectedAirport && (
|
||||
<button
|
||||
className="sticky top-0 m-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);
|
||||
setInFlight(undefined);
|
||||
|
||||
if (selectedTerminal) {
|
||||
setSelectedTerminal(undefined);
|
||||
} else if (selectedRunway) {
|
||||
setSelectedRunway(undefined);
|
||||
} else if (selectedAirport) {
|
||||
setSelectedAirport(undefined);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Go back
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="flex h-[calc(100%-50px)] flex-col gap-2 overflow-y-auto px-2 pb-2">
|
||||
{!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>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-0 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">Arrivals</div>
|
||||
{terminals.star.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 disabled:bg-gray-100"
|
||||
onClick={() => {
|
||||
setInFlight(terminal.ID);
|
||||
parser.loadTerminal(terminal.ID).then(() => {
|
||||
setSelectedTerminal(terminal);
|
||||
const transitions = new Set(parser.procedures.map((proc) => proc.Transition));
|
||||
handleSelection(Array.from(transitions), terminal);
|
||||
setInFlight(undefined);
|
||||
});
|
||||
}}
|
||||
disabled={!!inFlight}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
{terminal.FullName}
|
||||
{inFlight === terminal.ID && <Loader size={0.5} />}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-0 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">Departures</div>
|
||||
{terminals.sid.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 disabled:bg-gray-100"
|
||||
onClick={() => {
|
||||
setInFlight(terminal.ID);
|
||||
parser.loadTerminal(terminal.ID).then(() => {
|
||||
setSelectedTerminal(terminal);
|
||||
const transitions = new Set(parser.procedures.map((proc) => proc.Transition));
|
||||
handleSelection(Array.from(transitions), terminal);
|
||||
setInFlight(undefined);
|
||||
});
|
||||
}}
|
||||
disabled={!!inFlight}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
{terminal.FullName}
|
||||
{inFlight === terminal.ID && <Loader size={0.5} />}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-0 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">Approaches</div>
|
||||
{terminals.iap.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 disabled:bg-gray-100"
|
||||
onClick={() => {
|
||||
setInFlight(terminal.ID);
|
||||
parser.loadTerminal(terminal.ID).then(() => {
|
||||
setSelectedTerminal(terminal);
|
||||
const transitions = new Set(parser.procedures.map((proc) => proc.Transition));
|
||||
handleSelection(Array.from(transitions), terminal);
|
||||
setInFlight(undefined);
|
||||
});
|
||||
}}
|
||||
disabled={!!inFlight}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
{terminal.FullName}
|
||||
{inFlight === terminal.ID && <Loader size={0.5} />}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,201 @@
|
||||
import BrowserImageManipulation from 'browser-image-manipulation';
|
||||
import L from 'leaflet';
|
||||
import type { Chart as NGChart } from 'navigraph/charts';
|
||||
import { useCallback, useEffect, useState, type Dispatch, type FC, type SetStateAction } from 'react';
|
||||
import { charts } from '../lib/navigraph';
|
||||
import { Loader } from './Loader';
|
||||
|
||||
interface SidebarProps {
|
||||
airport: Airport;
|
||||
runway: Runway;
|
||||
terminal: Terminal;
|
||||
transitions: Procedure[];
|
||||
transition: Procedure | undefined;
|
||||
chart: Chart | undefined;
|
||||
skippedNG?: boolean;
|
||||
setTransition: Dispatch<SetStateAction<SidebarProps['transition']>>;
|
||||
setChart: Dispatch<SetStateAction<SidebarProps['chart']>>;
|
||||
backAction: () => void;
|
||||
}
|
||||
|
||||
export const Sidebar: FC<SidebarProps> = ({
|
||||
airport,
|
||||
runway,
|
||||
terminal,
|
||||
transitions,
|
||||
transition,
|
||||
chart,
|
||||
skippedNG,
|
||||
setTransition,
|
||||
setChart,
|
||||
backAction,
|
||||
}) => {
|
||||
const [inFlight, setInFlight] = useState(false);
|
||||
const [chartIndex, setChartIndex] = useState<{
|
||||
sid: NGChart[];
|
||||
star: NGChart[];
|
||||
iap: NGChart[];
|
||||
}>({ star: [], sid: [], iap: [] });
|
||||
|
||||
useEffect(() => {
|
||||
if (skippedNG) return;
|
||||
|
||||
setInFlight(true);
|
||||
(async () => {
|
||||
const _chartIndex = await charts.getChartsIndex({ icao: airport.ICAO, version: 'STD' });
|
||||
|
||||
const newChartIndex = {
|
||||
star:
|
||||
_chartIndex?.filter(
|
||||
(_chart) =>
|
||||
_chart.category === 'ARR' && (_chart.runways.length === 0 || _chart.runways.includes(runway.Ident))
|
||||
) ?? [],
|
||||
sid:
|
||||
_chartIndex?.filter(
|
||||
(_chart) =>
|
||||
_chart.category === 'DEP' && (_chart.runways.length === 0 || _chart.runways.includes(runway.Ident))
|
||||
) ?? [],
|
||||
iap:
|
||||
_chartIndex?.filter(
|
||||
(_chart) =>
|
||||
_chart.category === 'APP' && (_chart.runways.length === 0 || _chart.runways.includes(runway.Ident))
|
||||
) ?? [],
|
||||
};
|
||||
|
||||
_chartIndex?.filter((_chart) => ['ARR', 'DEP', 'APP'].includes(_chart.category));
|
||||
setChartIndex(newChartIndex);
|
||||
setInFlight(false);
|
||||
})();
|
||||
}, [airport.ICAO]);
|
||||
|
||||
useEffect(() => {
|
||||
if (terminal) {
|
||||
const star = chartIndex.star.find((_chart) => _chart.procedures.includes(terminal.FullName));
|
||||
const sid = chartIndex.sid.find((_chart) => _chart.procedures.includes(terminal.FullName));
|
||||
const iap = chartIndex.iap.find((_chart) => _chart.procedures.includes(terminal.FullName));
|
||||
if (star) findChart(star);
|
||||
if (sid) findChart(sid);
|
||||
if (iap) findChart(iap);
|
||||
}
|
||||
}, [terminal, chartIndex]);
|
||||
|
||||
const findChart = useCallback(
|
||||
async (_chart: NGChart) => {
|
||||
if (chart?.index_number === _chart.index_number) return;
|
||||
if (!_chart.bounding_boxes) return;
|
||||
|
||||
const planView = _chart.bounding_boxes.planview;
|
||||
|
||||
const chartImage = await charts.getChartImage({ chart: _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]
|
||||
);
|
||||
|
||||
setChart({ data: dataURL, index_number: _chart.index_number, bounds });
|
||||
},
|
||||
[chart]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-[300px] shrink-0 flex-col overflow-hidden">
|
||||
<button
|
||||
className="sticky top-0 m-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={backAction}
|
||||
>
|
||||
Go back
|
||||
</button>
|
||||
|
||||
<div className="flex h-[calc(100%-50px)] flex-col gap-2 overflow-y-auto px-2 pb-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-0 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">
|
||||
Transitions for <span className="font-bold">{terminal.FullName}</span>
|
||||
</div>
|
||||
{transitions.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 ${_procedure.name === transition?.name ? 'outline-2' : ''}`}
|
||||
onClick={() => {
|
||||
if (_procedure.name === transition?.name) return;
|
||||
|
||||
setTransition(_procedure);
|
||||
}}
|
||||
>
|
||||
{_procedure.name ? _procedure.name : 'NONE'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{!skippedNG && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-0 z-10 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">Charts</div>
|
||||
{inFlight && <Loader size={3} />}
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-7 -mx-2 -mt-2 bg-gray-500 px-2 text-lg font-semibold text-white">Arrivals</div>
|
||||
{chartIndex.star
|
||||
.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 text-left font-semibold focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${chart?.index_number === _chart.index_number ? 'outline-2' : ''}`}
|
||||
onClick={() => findChart(_chart)}
|
||||
>
|
||||
<span className="font-bold">{_chart.index_number}</span>
|
||||
<br />
|
||||
{_chart.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-7 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">Departures</div>
|
||||
{chartIndex.sid
|
||||
.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 text-left font-semibold focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${chart?.index_number === _chart.index_number ? 'outline-2' : ''}`}
|
||||
onClick={() => findChart(_chart)}
|
||||
>
|
||||
<span className="font-bold">{_chart.index_number}</span>
|
||||
<br />
|
||||
{_chart.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="sticky top-7 -mx-2 bg-gray-500 px-2 text-lg font-semibold text-white">Approaches</div>
|
||||
{chartIndex.iap
|
||||
.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 text-left font-semibold focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${chart?.index_number === _chart.index_number ? 'outline-2' : ''}`}
|
||||
onClick={() => findChart(_chart)}
|
||||
>
|
||||
<span className="font-bold">{_chart.index_number}</span>
|
||||
<br />
|
||||
{_chart.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,7 +11,11 @@ import { TerminatorsFA } from './terminators/FA';
|
||||
import { TerminatorsFC } from './terminators/FC';
|
||||
import { TerminatorsFD } from './terminators/FD';
|
||||
import { TerminatorsFM } from './terminators/FM';
|
||||
import { TerminatorsHA } from './terminators/HA';
|
||||
import { TerminatorsHF } from './terminators/HF';
|
||||
import { TerminatorsHM } from './terminators/HM';
|
||||
import { TerminatorsIF } from './terminators/IF';
|
||||
import { TerminatorsPI } from './terminators/PI';
|
||||
import { TerminatorsRF } from './terminators/RF';
|
||||
import { TerminatorsTF } from './terminators/TF';
|
||||
import { TerminatorsVA } from './terminators/VA';
|
||||
@@ -21,12 +25,6 @@ import { TerminatorsVM } from './terminators/VM';
|
||||
import { TerminatorsVR } from './terminators/VR';
|
||||
import './utils/extensions';
|
||||
|
||||
/*
|
||||
Runway IDs for LIED
|
||||
26156 - 34L
|
||||
26157 - 34R
|
||||
*/
|
||||
|
||||
class Parser {
|
||||
private static _instance: Parser;
|
||||
|
||||
@@ -37,11 +35,19 @@ class Parser {
|
||||
|
||||
private _procedures: TerminalEntry[] = [];
|
||||
|
||||
public static MAGVAR: Uint8Array;
|
||||
public static AC_SPEED = 250;
|
||||
public static AC_BANK = 30;
|
||||
public static AC_VS = 1400;
|
||||
|
||||
private constructor(airports: Airport[], waypoints: Waypoint[], runways: Runway[], terminals: Terminal[]) {
|
||||
private constructor(
|
||||
magVar: Uint8Array,
|
||||
airports: Airport[],
|
||||
waypoints: Waypoint[],
|
||||
runways: Runway[],
|
||||
terminals: Terminal[]
|
||||
) {
|
||||
Parser.MAGVAR = magVar;
|
||||
this._airports = airports;
|
||||
this._waypoints = waypoints;
|
||||
this._runways = runways;
|
||||
@@ -50,12 +56,13 @@ class Parser {
|
||||
|
||||
public static instance = async () => {
|
||||
if (!Parser._instance) {
|
||||
const magVar = await (await fetch('NavData/magdec.bgl')).bytes();
|
||||
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(airports, waypoints, runways, terminals);
|
||||
Parser._instance = new Parser(magVar, airports, waypoints, runways, terminals);
|
||||
}
|
||||
|
||||
return Parser._instance;
|
||||
@@ -84,7 +91,7 @@ class Parser {
|
||||
).json()) as TerminalEntry[];
|
||||
};
|
||||
|
||||
public parse = (runway: Runway, transition: string) => {
|
||||
public parse = (runway: Runway, terminal: Terminal, transition: string) => {
|
||||
// Private functions
|
||||
/**
|
||||
* @param line Line segments
|
||||
@@ -119,37 +126,67 @@ class Parser {
|
||||
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);
|
||||
let lastCourse = 0;
|
||||
|
||||
// RWY as origin for Departures
|
||||
if (terminal.Proc === 2) {
|
||||
navFixes.push({
|
||||
latitude: runway.Latitude,
|
||||
longitude: runway.Longitude,
|
||||
altitude: runway.Elevation,
|
||||
speed: 0,
|
||||
name: runway.Ident,
|
||||
});
|
||||
lastCourse = runway.TrueHeading;
|
||||
}
|
||||
const legOptions = { isMAP: false };
|
||||
const procedure = this._procedures.filter(
|
||||
({ Transition }) => !Transition || Transition === transition || Transition === 'ALL'
|
||||
);
|
||||
|
||||
// Main
|
||||
for (let index = 0; index < procedure.length; index++) {
|
||||
console.log('Processing leg with ID', procedure[index].ID);
|
||||
|
||||
const leg = procedure[index];
|
||||
leg.Alt = leg.IsMAP ? String(runway.Elevation + 200).padStart(5, '0') : leg.Alt;
|
||||
|
||||
const waypoint = this.waypoints.find(({ ID }) => ID === leg.WptID);
|
||||
|
||||
if (terminal.Proc !== 2 && navFixes.length === 0) {
|
||||
if (!leg.WptLat || !leg.WptLon)
|
||||
throw new Error('Non departure procedure has first leg that has no defined starting point. Cannot render.');
|
||||
|
||||
navFixes.push({
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : runway.Elevation,
|
||||
speed: leg.SpeedLimit ? leg.SpeedLimit : Parser.AC_SPEED,
|
||||
name: waypoint?.Ident,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
});
|
||||
}
|
||||
|
||||
const previousFix = navFixes.at(-1)!;
|
||||
const waypoint = this.waypoints.filter(({ ID }) => ID === leg.WptID)[0];
|
||||
legOptions.isMAP ||= previousFix.IsMAP ?? false;
|
||||
|
||||
switch (leg.TrackCode) {
|
||||
case 'AF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsAF(leg as AFTerminalEntry, previousFix, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'CA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCA(leg as CATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'CD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCD(leg as CDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'CF': {
|
||||
@@ -159,7 +196,7 @@ class Parser {
|
||||
lastCourse,
|
||||
waypoint
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'CI': {
|
||||
@@ -169,53 +206,75 @@ class Parser {
|
||||
{ ...previousFix }, // COPY
|
||||
lastCourse
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'CR': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsCR(leg as CRTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'DF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsDF(leg as DFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'FA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFA(leg as FATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'FC': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFC(leg as FCTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'FD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFD(leg as FDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'FM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsFM(leg as FMTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd, { isManual: true });
|
||||
update(fixToAdd, lineToAdd, { isManual: true, ...legOptions });
|
||||
// Make overfly
|
||||
navFixes.at(-1)!.isFlyOver = true;
|
||||
break;
|
||||
}
|
||||
case 'HA':
|
||||
case 'HF':
|
||||
case 'HM':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
case 'HA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsHA(leg as HATerminalEntry, previousFix);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'HF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsHF(leg as HFTerminalEntry, previousFix);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'HM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsHM(leg as HMTerminalEntry, previousFix);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'IF': {
|
||||
const fixToAdd = TerminatorsIF(leg as RFTerminalEntry, waypoint);
|
||||
navFixes.length = 0;
|
||||
navFixes.push(fixToAdd);
|
||||
// Only Runway, replace
|
||||
if (navFixes.length <= 1) {
|
||||
navFixes.push(fixToAdd);
|
||||
lastCourse = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'PI':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
case 'PI': {
|
||||
const nextLeg = procedure[index + 1] as CFTerminalEntry;
|
||||
const _waypoint = this.waypoints.find(({ ID }) => ID === nextLeg.WptID);
|
||||
const [fixToAdd, lineToAdd] = TerminatorsPI(leg as PITerminalEntry, nextLeg, previousFix, [
|
||||
waypoint,
|
||||
_waypoint,
|
||||
]);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'RF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsRF(leg as RFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
if (fixToAdd) {
|
||||
@@ -223,23 +282,28 @@ class Parser {
|
||||
lastCourse = (leg as RFTerminalEntry).Course?.toTrue(fixToAdd);
|
||||
}
|
||||
if (lineToAdd) {
|
||||
lineSegments.push({ line: lineToAdd });
|
||||
lineSegments.push({ line: lineToAdd, ...legOptions });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'TF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsTF(leg as TFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
update(fixToAdd, lineToAdd);
|
||||
const [fixToAdd, lineToAdd] = TerminatorsTF(
|
||||
leg as TFTerminalEntry,
|
||||
{ ...previousFix }, // COPY
|
||||
lastCourse,
|
||||
waypoint
|
||||
);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'VA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVA(leg as VATerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'VD': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVD(leg as VDTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'VI': {
|
||||
@@ -249,17 +313,19 @@ class Parser {
|
||||
{ ...previousFix }, // COPY
|
||||
lastCourse
|
||||
);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
case 'VM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVM(leg as VMTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd, { isManual: true });
|
||||
update(fixToAdd, lineToAdd, { isManual: true, ...legOptions });
|
||||
// Make overfly
|
||||
navFixes.at(-1)!.isFlyOver = true;
|
||||
break;
|
||||
}
|
||||
case 'VR': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsVR(leg as VRTerminalEntry, previousFix, lastCourse);
|
||||
update(fixToAdd, lineToAdd);
|
||||
update(fixToAdd, lineToAdd, legOptions);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
+3
-3
@@ -6,7 +6,7 @@ import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
* @param start Arc origin point
|
||||
* @param center Arc center point
|
||||
* @param radius Arc radius in nmi
|
||||
* @param turnDir
|
||||
* @param turnDir Turn direction
|
||||
* @returns Line segments
|
||||
*/
|
||||
export const generateAFArc = (
|
||||
@@ -30,11 +30,11 @@ export const generateAFArc = (
|
||||
while (crsFromOrigin !== crsIntoEndpoint) {
|
||||
if (turnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
||||
crsFromOrigin += delta < 1 ? delta : 1;
|
||||
crsFromOrigin += delta < 0.1 ? delta : 0.1;
|
||||
crsFromOrigin = crsFromOrigin.normaliseDegrees();
|
||||
} else {
|
||||
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
||||
crsFromOrigin -= delta < 1 ? delta : 1;
|
||||
crsFromOrigin -= delta < 0.1 ? delta : 0.1;
|
||||
crsFromOrigin = crsFromOrigin.normaliseDegrees();
|
||||
}
|
||||
if (crsFromOrigin === crsIntoEndpoint) break;
|
||||
+4
-4
@@ -38,12 +38,12 @@ export const generatePerformanceArc = (
|
||||
let time = 0;
|
||||
if (turnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
crsFromOrigin = (crsFromOrigin + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
crsFromOrigin = (crsFromOrigin - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
@@ -70,12 +70,12 @@ export const generatePerformanceArc = (
|
||||
let time = 0;
|
||||
if (turnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
crsFromOrigin = (crsFromOrigin + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
crsFromOrigin = (crsFromOrigin - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
+10
-17
@@ -1,12 +1,14 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getDistance from 'geolib/es/getDistance';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
|
||||
/**
|
||||
* @param crsIntoEndpoint Course into arc endpoint
|
||||
* @param crsIntoOrigin Course into arc origin point
|
||||
* @param start Arc origin point
|
||||
* @param center Arc center point
|
||||
* @param turnDir
|
||||
* @param end Arc end point
|
||||
* @param turnDir Turn Direction
|
||||
* @returns Line segments
|
||||
*/
|
||||
export const generateRFArc = (
|
||||
@@ -14,6 +16,7 @@ export const generateRFArc = (
|
||||
crsIntoOrigin: number,
|
||||
start: NavFix,
|
||||
center: NavFix,
|
||||
end: NavFix,
|
||||
turnDir: TurnDirection
|
||||
) => {
|
||||
const line: LineSegment[] = [[start.longitude, start.latitude]];
|
||||
@@ -26,20 +29,10 @@ export const generateRFArc = (
|
||||
turnDir = prov > 0 ? 'L' : 'R';
|
||||
}
|
||||
|
||||
let crsOrthogonalOnOrigin;
|
||||
let crsOrthogonalOnEndpoint;
|
||||
if (turnDir === 'R') {
|
||||
crsOrthogonalOnOrigin = (crsIntoOrigin + 90).normaliseDegrees();
|
||||
crsOrthogonalOnEndpoint = (crsIntoEndpoint + 90).normaliseDegrees();
|
||||
} else {
|
||||
crsOrthogonalOnOrigin = (crsIntoOrigin - 90).normaliseDegrees();
|
||||
crsOrthogonalOnEndpoint = (crsIntoEndpoint - 90).normaliseDegrees();
|
||||
}
|
||||
let crsOrthogonalOnOrigin = getGreatCircleBearing(center, start);
|
||||
const crsOrthogonalOnEndpoint = getGreatCircleBearing(center, end);
|
||||
const arcRad = getPreciseDistance(center, start);
|
||||
|
||||
const arcRad = getDistance(center, start);
|
||||
|
||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
|
||||
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
|
||||
// Start turn immediately
|
||||
if (turnDir === 'R') {
|
||||
crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
||||
@@ -50,11 +43,11 @@ export const generateRFArc = (
|
||||
while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
|
||||
if (turnDir === 'R') {
|
||||
const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
|
||||
crsOrthogonalOnOrigin += delta < 1 ? delta : 1;
|
||||
crsOrthogonalOnOrigin += delta < 0.1 ? delta : 0.1;
|
||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||
} else {
|
||||
const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
|
||||
crsOrthogonalOnOrigin -= delta < 1 ? delta : 1;
|
||||
crsOrthogonalOnOrigin -= delta < 0.1 ? delta : 0.1;
|
||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||
}
|
||||
|
||||
+16
-6
@@ -1,5 +1,5 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getDistance from 'geolib/es/getDistance';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
|
||||
/**
|
||||
@@ -56,14 +56,19 @@ export const generateTangentArc = (
|
||||
}
|
||||
|
||||
// Generate arc
|
||||
const arcCenter = computeIntersection(
|
||||
let arcRad = 0;
|
||||
let arcCenter = computeIntersection(
|
||||
start,
|
||||
crsOrthogonalOnOrigin,
|
||||
intcArcOnCrsIntoEndpoint,
|
||||
crsOrthogonalOnEndpoint
|
||||
);
|
||||
if (!arcCenter) return null;
|
||||
const arcRad = getDistance(arcCenter, start);
|
||||
if (!crsOrthogonalOnEndpoint.equal(crsOrthogonalOnOrigin) && arcCenter)
|
||||
arcRad = getPreciseDistance(arcCenter, start);
|
||||
else {
|
||||
arcRad = getPreciseDistance(start, end) / 2;
|
||||
arcCenter = computeDestinationPoint(start, arcRad, crsOrthogonalOnOrigin);
|
||||
}
|
||||
|
||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
|
||||
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
|
||||
@@ -74,19 +79,24 @@ export const generateTangentArc = (
|
||||
crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
||||
}
|
||||
|
||||
let lastDistance = getPreciseDistance(start, end);
|
||||
while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
|
||||
if (turnDir === 'R') {
|
||||
const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
|
||||
crsOrthogonalOnOrigin += delta < 1 ? delta : 1;
|
||||
crsOrthogonalOnOrigin += delta < 0.1 ? delta : 0.1;
|
||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||
} else {
|
||||
const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
|
||||
crsOrthogonalOnOrigin -= delta < 1 ? delta : 1;
|
||||
crsOrthogonalOnOrigin -= delta < 0.1 ? delta : 0.1;
|
||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(arcCenter, arcRad, crsOrthogonalOnOrigin);
|
||||
|
||||
const newDistance = getPreciseDistance(arcFix, end);
|
||||
if (lastDistance <= newDistance && lastDistance < 25) break;
|
||||
lastDistance = newDistance;
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,11 @@ export const TerminatorsAF = (
|
||||
previousFix: NavFix,
|
||||
waypoint?: Waypoint
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const navaid = {
|
||||
latitude: leg.NavLat,
|
||||
longitude: leg.NavLon,
|
||||
};
|
||||
|
||||
const targetFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
@@ -16,27 +21,13 @@ export const TerminatorsAF = (
|
||||
speed: computeSpeed(leg, previousFix),
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
const arcEndCrs = getGreatCircleBearing(
|
||||
{
|
||||
latitude: leg.NavLat,
|
||||
longitude: leg.NavLon,
|
||||
},
|
||||
{
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
}
|
||||
);
|
||||
const arcEndCrs = getGreatCircleBearing(navaid, targetFix);
|
||||
|
||||
const line = generateAFArc(
|
||||
arcEndCrs,
|
||||
leg.Course.toTrue({ latitude: leg.NavLat, longitude: leg.NavLon }),
|
||||
previousFix,
|
||||
{ latitude: leg.NavLat, longitude: leg.NavLon },
|
||||
leg.NavDist,
|
||||
leg.TurnDir
|
||||
);
|
||||
const line = generateAFArc(arcEndCrs, leg.Course.toTrue(navaid), previousFix, navaid, leg.NavDist, leg.TurnDir);
|
||||
|
||||
return [targetFix, line];
|
||||
};
|
||||
@@ -31,6 +31,8 @@ export const TerminatorsCA = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getDistance from 'geolib/es/getDistance';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
@@ -23,7 +23,7 @@ export const TerminatorsCD = (
|
||||
|
||||
// Compute distance to fly from arc end
|
||||
const crsToNavaid = getGreatCircleBearing(arcEnd, navaid);
|
||||
const distToNavaid = getDistance(arcEnd, navaid);
|
||||
const distToNavaid = getPreciseDistance(arcEnd, navaid);
|
||||
let remainingDistance = leg.Distance.toMetre();
|
||||
// Navaid behind us
|
||||
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
||||
@@ -44,6 +44,8 @@ export const TerminatorsCD = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
import Parser from '../parser';
|
||||
import { generateTangentArc } from '../pathGenerators/generateTangentArc';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
export const TerminatorsCF = (
|
||||
leg: CFTerminalEntry,
|
||||
previousFix: NavFix,
|
||||
lastCourse: number,
|
||||
waypoint?: Waypoint
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const crsIntoEndpoint = leg.Course.toTrue(previousFix);
|
||||
const line: LineSegment[] = [];
|
||||
|
||||
const targetFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoint?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
// Compute overfly arc
|
||||
let arc1: LineSegment[] | null = null;
|
||||
let arc2: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
if (previousFix.isFlyOver) {
|
||||
arc1 = generateTangentArc(crsIntoEndpoint, lastCourse, previousFix, targetFix, leg.TurnDir);
|
||||
} else {
|
||||
arc1 = [[previousFix.longitude, previousFix.latitude]];
|
||||
}
|
||||
|
||||
let arc;
|
||||
if (arc1 && arc1.length > 1) {
|
||||
const endCrs = getGreatCircleBearing(
|
||||
{
|
||||
latitude: arc1.at(-1)![1],
|
||||
longitude: arc1.at(-1)![0],
|
||||
},
|
||||
targetFix
|
||||
);
|
||||
const endDist = getPreciseDistance(
|
||||
{
|
||||
latitude: arc1.at(-1)![1],
|
||||
longitude: arc1.at(-1)![0],
|
||||
},
|
||||
targetFix
|
||||
);
|
||||
|
||||
if (endDist <= 25 || (endCrs <= crsIntoEndpoint + 1 && endCrs >= crsIntoEndpoint - 1)) arc = arc1;
|
||||
}
|
||||
|
||||
const intercept = computeIntersection(previousFix, lastCourse, targetFix, crsIntoEndpoint.reciprocalCourse());
|
||||
if (
|
||||
!arc &&
|
||||
previousFix.isFlyOver &&
|
||||
(!lastCourse.equal(crsIntoEndpoint) || !lastCourse.equal(crsIntoEndpoint)) &&
|
||||
!(intercept?.latitude === targetFix.latitude && intercept.longitude === targetFix.longitude)
|
||||
) {
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
let updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
// Turn Dir
|
||||
if (!leg.TurnDir || leg.TurnDir === 'E') {
|
||||
let prov = lastCourse - crsIntoEndpoint;
|
||||
prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
|
||||
leg.TurnDir = prov > 0 ? 'L' : 'R';
|
||||
}
|
||||
|
||||
// Generate arc
|
||||
let lastDistance = getPreciseDistance(previousFix, targetFix);
|
||||
const reversal = Math.abs(lastCourse - crsIntoEndpoint) >= 90;
|
||||
while (!updatedCrsToIntercept.equal(crsIntoEndpoint)) {
|
||||
let interceptAngle = 0;
|
||||
if (leg.TurnDir === 'R') interceptAngle = Math.abs(lastCourse - crsIntoEndpoint);
|
||||
else interceptAngle = Math.abs(crsIntoEndpoint - lastCourse);
|
||||
|
||||
let time = 0;
|
||||
const increment = 0.1;
|
||||
if (interceptAngle < 44.9 || interceptAngle >= 45.1) {
|
||||
if (leg.TurnDir === 'R') {
|
||||
lastCourse =
|
||||
reversal || updatedCrsToIntercept >= crsIntoEndpoint
|
||||
? (lastCourse + increment).normaliseDegrees()
|
||||
: lastCourse;
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
lastCourse =
|
||||
reversal || updatedCrsToIntercept <= crsIntoEndpoint
|
||||
? (lastCourse - increment).normaliseDegrees()
|
||||
: lastCourse;
|
||||
time = increment / turnRate;
|
||||
}
|
||||
} else time = increment / turnRate;
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: arc2.at(-1)![1],
|
||||
longitude: arc2.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
lastCourse
|
||||
);
|
||||
|
||||
arc2.push([arcFix.longitude, arcFix.latitude]);
|
||||
|
||||
// Update previousFix
|
||||
previousFix.latitude = arcFix.latitude;
|
||||
previousFix.longitude = arcFix.longitude;
|
||||
updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
const newDistance = getPreciseDistance(previousFix, targetFix);
|
||||
if (lastDistance <= newDistance && lastDistance < 25) break;
|
||||
lastDistance = newDistance;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arc) arc = arc2;
|
||||
line.push(...arc);
|
||||
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
return [targetFix, line];
|
||||
};
|
||||
@@ -34,12 +34,12 @@ export const TerminatorsCI = (
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
@@ -68,6 +68,8 @@ export const TerminatorsCI = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||
@@ -27,6 +27,8 @@ export const TerminatorsCR = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||
@@ -0,0 +1,121 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
export const TerminatorsDF = (
|
||||
leg: DFTerminalEntry,
|
||||
previousFix: NavFix,
|
||||
lastCourse: number,
|
||||
waypoint?: Waypoint
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
const originalCrsFromOrigin = lastCourse;
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
const targetFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoint?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
let crsIntoEndpoint = getGreatCircleBearing(previousFix, targetFix);
|
||||
let force360 = getPreciseDistance(previousFix, targetFix) < 25;
|
||||
|
||||
// Check if there even is an arc
|
||||
if (force360 || !lastCourse.equal(crsIntoEndpoint)) {
|
||||
// Turn Dir
|
||||
if (!leg.TurnDir || leg.TurnDir === 'E') {
|
||||
let prov = lastCourse - crsIntoEndpoint;
|
||||
prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
|
||||
leg.TurnDir = prov > 0 ? 'L' : 'R';
|
||||
}
|
||||
|
||||
// Generate arc
|
||||
while (!lastCourse.equal(crsIntoEndpoint)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
lastCourse
|
||||
);
|
||||
|
||||
crsIntoEndpoint = getGreatCircleBearing(arcFix, targetFix);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
|
||||
// made a loop
|
||||
if (line.length >= 3600) {
|
||||
if (!force360) {
|
||||
line.splice(1);
|
||||
}
|
||||
force360 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Second half
|
||||
if (force360) {
|
||||
const temp = crsIntoEndpoint;
|
||||
crsIntoEndpoint = originalCrsFromOrigin;
|
||||
lastCourse = temp;
|
||||
|
||||
while (!lastCourse.equal(crsIntoEndpoint)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
lastCourse
|
||||
);
|
||||
|
||||
crsIntoEndpoint = getGreatCircleBearing(arcFix, targetFix);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
return [targetFix, line];
|
||||
};
|
||||
@@ -35,6 +35,8 @@ export const TerminatorsFA = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
|
||||
// NOTE: Distance not adjusted for altitude in this demo
|
||||
export const TerminatorsFC = (
|
||||
@@ -38,12 +38,12 @@ export const TerminatorsFC = (
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
@@ -80,13 +80,15 @@ export const TerminatorsFC = (
|
||||
}
|
||||
|
||||
const targetFix: NavFix = {
|
||||
...computeDestinationPoint(arcEnd, leg.Distance.toMetre(), lastCourse),
|
||||
...computeDestinationPoint(arcEnd, leg.Distance.toMetre(), leg.Course.toTrue(refFix)),
|
||||
name: leg.Distance.toString(),
|
||||
isFlyOver: true,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
@@ -1,6 +1,6 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getDistance from 'geolib/es/getDistance';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
@@ -27,7 +27,7 @@ export const TerminatorsFD = (
|
||||
|
||||
// Compute distance to fly from arc end
|
||||
const crsToNavaid = getGreatCircleBearing(arcEnd, navaid);
|
||||
const distToNavaid = getDistance(arcEnd, navaid);
|
||||
const distToNavaid = getPreciseDistance(arcEnd, navaid);
|
||||
let remainingDistance = leg.Distance.toMetre();
|
||||
// Navaid behind us
|
||||
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
||||
@@ -41,13 +41,15 @@ export const TerminatorsFD = (
|
||||
|
||||
// Compute intercept of crs from arc end and distance
|
||||
const targetFix: NavFix = {
|
||||
...computeDestinationPoint(arcEnd, remainingDistance, lastCourse),
|
||||
...computeDestinationPoint(arcEnd, remainingDistance, crsIntoEndpoint),
|
||||
name: leg.Distance.toString(),
|
||||
isFlyOver: true,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
// NOTE: Distance is interpreted as distance and not time
|
||||
export const TerminatorsHA = (leg: HATerminalEntry, previousFix: NavFix): [NavFix?, LineSegment[]?] => {
|
||||
const refFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
};
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
const inboundCrs = leg.Course.toTrue(refFix);
|
||||
const outboundCrs = inboundCrs.reciprocalCourse();
|
||||
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
// Generate top arc
|
||||
let currentCrs = inboundCrs;
|
||||
while (!currentCrs.equal(outboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (outboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - outboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
const outboundStart = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
leg.Distance.toMetre(),
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outboundStart.longitude, outboundStart.latitude]);
|
||||
|
||||
// Generate bottom arc
|
||||
currentCrs = outboundCrs;
|
||||
while (!currentCrs.equal(inboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (inboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - inboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
line.push([refFix.longitude, refFix.latitude]);
|
||||
|
||||
return [refFix, line];
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
// NOTE: Distance is interpreted as distance and not time
|
||||
export const TerminatorsHF = (leg: HFTerminalEntry, previousFix: NavFix): [NavFix?, LineSegment[]?] => {
|
||||
const refFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
};
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
const inboundCrs = leg.Course.toTrue(refFix);
|
||||
const outboundCrs = inboundCrs.reciprocalCourse();
|
||||
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
// Generate top arc
|
||||
let currentCrs = inboundCrs;
|
||||
while (!currentCrs.equal(outboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (outboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - outboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
const outboundStart = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
leg.Distance.toMetre(),
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outboundStart.longitude, outboundStart.latitude]);
|
||||
|
||||
// Generate bottom arc
|
||||
currentCrs = outboundCrs;
|
||||
while (!currentCrs.equal(inboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (inboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - inboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
line.push([refFix.longitude, refFix.latitude]);
|
||||
|
||||
return [refFix, line];
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
// NOTE: Distance is interpreted as distance and not time
|
||||
export const TerminatorsHM = (leg: HMTerminalEntry, previousFix: NavFix): [NavFix?, LineSegment[]?] => {
|
||||
const refFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
};
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
const inboundCrs = leg.Course.toTrue(refFix);
|
||||
const outboundCrs = inboundCrs.reciprocalCourse();
|
||||
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
// Generate top arc
|
||||
let currentCrs = inboundCrs;
|
||||
while (!currentCrs.equal(outboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (outboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - outboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
const outboundStart = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
leg.Distance.toMetre(),
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outboundStart.longitude, outboundStart.latitude]);
|
||||
|
||||
// Generate bottom arc
|
||||
currentCrs = outboundCrs;
|
||||
while (!currentCrs.equal(inboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (inboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - inboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
line.push([refFix.longitude, refFix.latitude]);
|
||||
|
||||
return [refFix, line];
|
||||
};
|
||||
@@ -10,6 +10,9 @@ export const TerminatorsIF = (leg: IFTerminalEntry, waypoint?: Waypoint): NavFix
|
||||
speed: leg.SpeedLimit ? leg.SpeedLimit : Parser.AC_SPEED,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
isInitial: true,
|
||||
};
|
||||
|
||||
return targetFix;
|
||||
@@ -0,0 +1,72 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import { generatePerformanceArc } from '../pathGenerators/generatePerformanceArc';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
export const TerminatorsPI = (
|
||||
leg: PITerminalEntry,
|
||||
nextLeg: CFTerminalEntry, // As per NG docs in the TrmLegTypes.json
|
||||
previousFix: NavFix,
|
||||
waypoints: [Waypoint?, Waypoint?]
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
|
||||
const originFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoints[0]?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
const endFix: NavFix = {
|
||||
latitude: nextLeg.WptLat,
|
||||
longitude: nextLeg.WptLon,
|
||||
name: waypoints[1]?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
const outboundCrs = leg.Course.toTrue(endFix);
|
||||
const interceptCrs = nextLeg.Course.toTrue(endFix);
|
||||
|
||||
const line: LineSegment[] = [
|
||||
[originFix.longitude, originFix.latitude],
|
||||
[endFix.longitude, endFix.latitude],
|
||||
];
|
||||
|
||||
// Outbound end
|
||||
const outEnd = computeDestinationPoint(
|
||||
endFix,
|
||||
((speed / 3600) * 60).toMetre(), // 1min leg
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outEnd.longitude, outEnd.latitude]);
|
||||
|
||||
// Arc
|
||||
line.push(...generatePerformanceArc(outboundCrs.reciprocalCourse(), outboundCrs, outEnd, speed, leg.TurnDir));
|
||||
|
||||
// Intercept
|
||||
const interceptFix = computeIntersection(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
outboundCrs.reciprocalCourse(),
|
||||
endFix,
|
||||
interceptCrs.reciprocalCourse()
|
||||
);
|
||||
|
||||
line.push([interceptFix!.longitude, interceptFix!.latitude]);
|
||||
line.push([endFix!.longitude, endFix!.latitude]);
|
||||
|
||||
return [undefined, line];
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { generateRFArc } from '../pathGenerators/generateRFArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
// NOTE: Direct entry into an RF does not calculate a usable line, given inbound course is unknown.
|
||||
export const TerminatorsRF = (
|
||||
leg: RFTerminalEntry,
|
||||
previousFix: NavFix,
|
||||
@@ -16,6 +17,8 @@ export const TerminatorsRF = (
|
||||
speed: computeSpeed(leg, previousFix),
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
const line = generateRFArc(
|
||||
@@ -23,6 +26,7 @@ export const TerminatorsRF = (
|
||||
lastCourse,
|
||||
previousFix,
|
||||
{ latitude: leg.CenterLat, longitude: leg.CenterLon },
|
||||
targetFix,
|
||||
leg.TurnDir
|
||||
);
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
import Parser from '../parser';
|
||||
import { generateTangentArc } from '../pathGenerators/generateTangentArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
export const TerminatorsTF = (
|
||||
leg: TFTerminalEntry,
|
||||
previousFix: NavFix,
|
||||
lastCourse: number,
|
||||
waypoint?: Waypoint
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const line: LineSegment[] = [];
|
||||
|
||||
const targetFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoint?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
const crsIntoEndpoint = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
// Compute overfly arc
|
||||
let arc1: LineSegment[] | null = null;
|
||||
let arc2: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
if (previousFix.isFlyOver && !lastCourse.equal(crsIntoEndpoint, 0.5)) {
|
||||
arc1 = generateTangentArc(crsIntoEndpoint, lastCourse, previousFix, targetFix, leg.TurnDir);
|
||||
} else {
|
||||
arc1 = [[previousFix.longitude, previousFix.latitude]];
|
||||
}
|
||||
|
||||
let arc;
|
||||
if (arc1 && arc1.length > 1) {
|
||||
const endCrs = getGreatCircleBearing(
|
||||
{
|
||||
latitude: arc1.at(-1)![1],
|
||||
longitude: arc1.at(-1)![0],
|
||||
},
|
||||
targetFix
|
||||
);
|
||||
const endDist = getPreciseDistance(
|
||||
{
|
||||
latitude: arc1.at(-1)![1],
|
||||
longitude: arc1.at(-1)![0],
|
||||
},
|
||||
targetFix
|
||||
);
|
||||
|
||||
if (endDist <= 25 || (endCrs <= crsIntoEndpoint + 1 && endCrs >= crsIntoEndpoint - 1)) arc = arc1;
|
||||
}
|
||||
|
||||
if (!arc && previousFix.isFlyOver && (!lastCourse.equal(crsIntoEndpoint) || !lastCourse.equal(crsIntoEndpoint))) {
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
let updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
// Turn Dir
|
||||
if (!leg.TurnDir || leg.TurnDir === 'E') {
|
||||
let prov = lastCourse - crsIntoEndpoint;
|
||||
prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
|
||||
leg.TurnDir = prov > 0 ? 'L' : 'R';
|
||||
}
|
||||
|
||||
// Generate arc
|
||||
let lastDistance = getPreciseDistance(previousFix, targetFix);
|
||||
while (!updatedCrsToIntercept.equal(crsIntoEndpoint)) {
|
||||
let interceptAngle = 0;
|
||||
if (leg.TurnDir === 'R') interceptAngle = Math.abs(lastCourse - crsIntoEndpoint);
|
||||
else interceptAngle = Math.abs(crsIntoEndpoint - lastCourse);
|
||||
|
||||
let time = 0;
|
||||
const increment = 0.1;
|
||||
if (interceptAngle < 44.9 || interceptAngle >= 45.1) {
|
||||
if (leg.TurnDir === 'R') {
|
||||
lastCourse =
|
||||
updatedCrsToIntercept >= crsIntoEndpoint ? (lastCourse + increment).normaliseDegrees() : lastCourse;
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
lastCourse =
|
||||
updatedCrsToIntercept >= crsIntoEndpoint ? (lastCourse - increment).normaliseDegrees() : lastCourse;
|
||||
time = increment / turnRate;
|
||||
}
|
||||
} else time = increment / turnRate;
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: arc2.at(-1)![1],
|
||||
longitude: arc2.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
lastCourse
|
||||
);
|
||||
|
||||
arc2.push([arcFix.longitude, arcFix.latitude]);
|
||||
|
||||
// Update previousFix
|
||||
previousFix.latitude = arcFix.latitude;
|
||||
previousFix.longitude = arcFix.longitude;
|
||||
updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix);
|
||||
|
||||
const newDistance = getPreciseDistance(previousFix, targetFix);
|
||||
if (lastDistance <= newDistance && lastDistance < 25) break;
|
||||
lastDistance = newDistance;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arc) arc = arc2;
|
||||
line.push(...arc);
|
||||
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
return [targetFix, line];
|
||||
};
|
||||
@@ -32,6 +32,8 @@ export const TerminatorsVA = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getDistance from 'geolib/es/getDistance';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
|
||||
@@ -24,7 +24,7 @@ export const TerminatorsVD = (
|
||||
|
||||
// Compute distance to fly from arc end
|
||||
const crsToNavaid = getGreatCircleBearing(arcEnd, navaid);
|
||||
const distToNavaid = getDistance(arcEnd, navaid);
|
||||
const distToNavaid = getPreciseDistance(arcEnd, navaid);
|
||||
let remainingDistance = leg.Distance.toMetre();
|
||||
// Navaid behind us
|
||||
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
||||
@@ -45,6 +45,8 @@ export const TerminatorsVD = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
line.push([targetFix.longitude, targetFix.latitude]);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
import Parser from '../parser';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
|
||||
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
||||
export const TerminatorsVI = (
|
||||
@@ -35,12 +35,12 @@ export const TerminatorsVI = (
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||
const increment = delta < 1 ? delta : 1;
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
lastCourse = (lastCourse - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
@@ -69,6 +69,8 @@ export const TerminatorsVI = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||
@@ -28,6 +28,8 @@ export const TerminatorsVR = (
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
|
||||
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||
+7
-2
@@ -5,7 +5,12 @@
|
||||
* @param brng2 bearing from Point 2
|
||||
* @returns Intersection point
|
||||
*/
|
||||
export const computeIntersection = (p1: NavFix, brng1: number, p2: NavFix, brng2: number): NavFix | undefined => {
|
||||
export const computeIntersection = (
|
||||
p1: NavFix,
|
||||
brng1: number,
|
||||
p2: NavFix,
|
||||
brng2: number
|
||||
): NavFix | undefined | null => {
|
||||
if (isNaN(brng1)) throw new TypeError(`invalid brng1 ${brng1}`);
|
||||
if (isNaN(brng2)) throw new TypeError(`invalid brng2 ${brng2}`);
|
||||
|
||||
@@ -43,7 +48,7 @@ export const computeIntersection = (p1: NavFix, brng1: number, p2: NavFix, brng2
|
||||
const α2 = θ21 - θ23; // angle 1-2-3
|
||||
|
||||
if (Math.sin(α1) == 0 && Math.sin(α2) == 0) return undefined; // infinite intersections
|
||||
if (Math.sin(α1) * Math.sin(α2) < 0) return undefined; // ambiguous intersection (antipodal/360°)
|
||||
if (Math.sin(α1) * Math.sin(α2) < 0) return p2; // ambiguous intersection (antipodal/360°)
|
||||
|
||||
const cosα3 = -Math.cos(α1) * Math.cos(α2) + Math.sin(α1) * Math.sin(α2) * Math.cos(δ12);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import Parser from '../parser';
|
||||
|
||||
Number.prototype.toRadians = function () {
|
||||
return ((this as number) * Math.PI) / 180;
|
||||
};
|
||||
Number.prototype.toDegrees = function () {
|
||||
return ((this as number) * 180) / Math.PI;
|
||||
};
|
||||
Number.prototype.reciprocalCourse = function () {
|
||||
let inv = (this as number) + 180;
|
||||
inv = inv >= 360 ? inv - 360 : inv;
|
||||
return inv;
|
||||
};
|
||||
Number.prototype.normaliseDegrees = function () {
|
||||
return (this as number) >= 360
|
||||
? (this as number) - 360
|
||||
: (this as number) < 0
|
||||
? (this as number) + 360
|
||||
: (this as number);
|
||||
};
|
||||
Number.prototype.toTrue = function (fix) {
|
||||
const toOffset = (lat: number, lon: number) =>
|
||||
lon >= 0 ? lon * 362 + lat * 2 + 316 : (lon + 360) * 362 + lat * 2 + 316;
|
||||
|
||||
const magVarTable = Parser.MAGVAR;
|
||||
|
||||
const lowerLat = Math.floor(fix.latitude);
|
||||
const upperLat = Math.ceil(fix.latitude);
|
||||
const ratioLat = fix.latitude - lowerLat;
|
||||
const lowerLon = Math.floor(fix.longitude);
|
||||
const upperLon = Math.ceil(fix.longitude);
|
||||
const ratioLon = fix.longitude - lowerLon;
|
||||
|
||||
const lowerLatLowerLonOffset = toOffset(lowerLat, lowerLon);
|
||||
const lowerLatUpperLonOffset = toOffset(lowerLat, upperLon);
|
||||
const upperLatLowerLonOffset = toOffset(upperLat, lowerLon);
|
||||
const upperLatUpperLonOffset = toOffset(upperLat, upperLon);
|
||||
|
||||
let lowerLatLowerLonMagVar = (magVarTable[lowerLatLowerLonOffset + 1] << 8) + magVarTable[lowerLatLowerLonOffset];
|
||||
lowerLatLowerLonMagVar = (lowerLatLowerLonMagVar * 0x168) / 0x10000;
|
||||
lowerLatLowerLonMagVar = lowerLatLowerLonMagVar > 180 ? lowerLatLowerLonMagVar - 360 : lowerLatLowerLonMagVar;
|
||||
let lowerLatUpperLonMagVar = (magVarTable[lowerLatUpperLonOffset + 1] << 8) + magVarTable[lowerLatUpperLonOffset];
|
||||
lowerLatUpperLonMagVar = (lowerLatUpperLonMagVar * 0x168) / 0x10000;
|
||||
lowerLatUpperLonMagVar = lowerLatUpperLonMagVar > 180 ? lowerLatUpperLonMagVar - 360 : lowerLatUpperLonMagVar;
|
||||
let upperLatLowerLonMagVar = (magVarTable[upperLatLowerLonOffset + 1] << 8) + magVarTable[upperLatLowerLonOffset];
|
||||
upperLatLowerLonMagVar = (upperLatLowerLonMagVar * 0x168) / 0x10000;
|
||||
upperLatLowerLonMagVar = upperLatLowerLonMagVar > 180 ? upperLatLowerLonMagVar - 360 : upperLatLowerLonMagVar;
|
||||
let upperLatUpperLonMagVar = (magVarTable[upperLatUpperLonOffset + 1] << 8) + magVarTable[upperLatUpperLonOffset];
|
||||
upperLatUpperLonMagVar = (upperLatUpperLonMagVar * 0x168) / 0x10000;
|
||||
upperLatUpperLonMagVar = upperLatUpperLonMagVar > 180 ? upperLatUpperLonMagVar - 360 : upperLatUpperLonMagVar;
|
||||
|
||||
const lowerLatRatioLon = lowerLatLowerLonMagVar + ratioLon * (lowerLatUpperLonMagVar - lowerLatLowerLonMagVar);
|
||||
const upperLatRatioLon = upperLatLowerLonMagVar + ratioLon * (upperLatUpperLonMagVar - upperLatLowerLonMagVar);
|
||||
|
||||
const magVar = lowerLatRatioLon + ratioLat + (upperLatRatioLon - lowerLatRatioLon);
|
||||
|
||||
return ((this as number) + magVar).normaliseDegrees();
|
||||
};
|
||||
Number.prototype.toMetre = function () {
|
||||
return (this as number) * 1852.0;
|
||||
};
|
||||
Number.prototype.equal = function (other: number, accuracy = 0.1) {
|
||||
return Math.abs((this as number) - other) <= accuracy;
|
||||
};
|
||||
|
||||
String.prototype.parseAltitude = function () {
|
||||
switch (this.length) {
|
||||
case 5:
|
||||
return Number.parseInt(this as string);
|
||||
case 6:
|
||||
return Number.parseInt(this.substring(0, 5));
|
||||
case 12: {
|
||||
const upper = Number.parseInt(this.substring(0, 5));
|
||||
const lower = Number.parseInt(this.substring(6, 12));
|
||||
return lower + (upper - lower) / 2;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
+7
-2
@@ -1,3 +1,4 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||
|
||||
/**
|
||||
@@ -29,8 +30,12 @@ export const getCourseAndFixForIntercepts = (leg: TerminalEntry, origin: NavFix)
|
||||
}
|
||||
case 'AF': {
|
||||
const _leg = leg as AFTerminalEntry;
|
||||
const fix = { latitude: _leg.WptLat, longitude: _leg.WptLon };
|
||||
return [_leg.Course.reciprocalCourse().toTrue(fix), fix];
|
||||
const navAid = { latitude: _leg.NavLat, longitude: _leg.NavLon };
|
||||
const fix = {
|
||||
...computeDestinationPoint(navAid, _leg.NavDist.toMetre(), _leg.Course.toTrue(navAid)),
|
||||
isIntersection: true,
|
||||
};
|
||||
return [_leg.Course.toTrue(fix), fix];
|
||||
}
|
||||
case 'DF': {
|
||||
const _leg = leg as DFTerminalEntry;
|
||||
+2
-2
@@ -26,9 +26,9 @@ export declare global {
|
||||
*/
|
||||
toMetre: () => number;
|
||||
/**
|
||||
* @returns True if delta is less than 0.1
|
||||
* @returns True if delta is less than accuracy (default 0.1)
|
||||
*/
|
||||
equal: (other: number) => boolean;
|
||||
equal: (other: number, accuracy?: number) => boolean;
|
||||
}
|
||||
|
||||
interface String {
|
||||
@@ -75,6 +75,7 @@ export declare global {
|
||||
ICAO: string;
|
||||
FullName: string;
|
||||
RwyID?: number;
|
||||
Proc: 1 | 2 | 3;
|
||||
};
|
||||
|
||||
type NavFix = {
|
||||
@@ -84,11 +85,13 @@ export declare global {
|
||||
speed?: number;
|
||||
name?: string;
|
||||
isFlyOver?: boolean;
|
||||
'marker-color'?: string;
|
||||
altitudeConstraint?: string;
|
||||
speedConstraint?: number;
|
||||
IsFAF?: boolean;
|
||||
IsMAP?: boolean;
|
||||
// For map
|
||||
isIntersection?: boolean;
|
||||
isInitial?: boolean;
|
||||
};
|
||||
|
||||
type LineSegment = [number, number];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user