Compare commits
16 Commits
f593cbd29a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 266606d9ab | |||
| 900babf4cc | |||
| 048d5129aa | |||
| 73373420d3 | |||
| e9bab0306f | |||
| 4e4b921f79 | |||
| d102ec4834 | |||
| e91323c79b | |||
| bbda565272 | |||
| 56ecfe38d1 | |||
| e0ee6d11dd | |||
| 2bdb7e78c4 | |||
| c5cd3c7a0e | |||
| 779a78649e | |||
| e4adf30632 | |||
| 287ad8859b |
+3
-1
@@ -23,4 +23,6 @@ dist-ssr
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
NavData/
|
||||||
Vendored
+2
-1
@@ -9,7 +9,8 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch Chrome against localhost",
|
"name": "Launch Chrome against localhost",
|
||||||
"url": "http://localhost:3000",
|
"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
|
### Example
|
||||||
|
|
||||||
LGAV BIBE1K SID (Cycle 2507, ID 10653)
|
LGAV 03L BIBE1K SID (Cycle 2507, ID 10653)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### 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.
|
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)
|
## Course to Altitude (CA)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV BIBE1L SID (Cycle 2507, ID 10654)
|
LGAV 03L BIBE1L SID (Cycle 2507, ID 10654)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -74,7 +76,7 @@ This new origin is an implicit overfly.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
LGAV 21L BIBE2F SID (Cycle 2507, ID 10657)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -98,14 +100,13 @@ LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
|||||||
|
|
||||||
The leg terminates upon reaching `Distance`.
|
The leg terminates upon reaching `Distance`.
|
||||||
This intercept point then becomes the origin fix of the succeeding leg.
|
This intercept point then becomes the origin fix of the succeeding leg.
|
||||||
This new origin is an implicit overfly.
|
|
||||||
|
|
||||||
|
|
||||||
## Course to Fix (CF)
|
## Course to Fix (CF)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
LGAV 21L BIBE2F SID (Cycle 2507, ID 10657)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ shall be `TurnDir`.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV BIBE1L SID (Cycle 2507, ID 10654)
|
LGAV 03L BIBE1L SID (Cycle 2507, ID 10654)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -170,7 +171,7 @@ This new origin can never be an overfly due to the intercept nature.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV KOR1D SID (Cycle 2507, ID 10679)
|
LGAV 03L KOR1D SID (Cycle 2507, ID 10679)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -197,7 +198,7 @@ This intercept point then becomes the origin fix of the succeeding leg.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV KOR1D SID (Cycle 2507, ID 10679)
|
LGAV 03L KOR1D SID (Cycle 2507, ID 10679)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -219,7 +220,7 @@ LGAV KOR1D SID (Cycle 2507, ID 10679)
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV BIBE2F SID (Cycle 2507, ID 10657)
|
LGAV 21L BIBE2F SID (Cycle 2507, ID 10657)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -254,7 +255,7 @@ This new origin is an implicit overfly.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LIED CAR6F SID (Cycle 2507, ID 11798)
|
LIED 34L/R CAR6F SID (Cycle 2507, ID 11798)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -290,7 +291,7 @@ This intercept point then becomes the origin fix of the succeeding leg.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LGAV BIBE2T SID (Cycle 2507, ID 10659)
|
LGAV 03R BIBE2T SID (Cycle 2507, ID 10659)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -318,14 +319,13 @@ LGAV BIBE2T SID (Cycle 2507, ID 10659)
|
|||||||
|
|
||||||
The leg terminates upon reaching `Distance`.
|
The leg terminates upon reaching `Distance`.
|
||||||
This intercept point then becomes the origin fix of the succeeding leg.
|
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)
|
## Track from Fix to Manual Termination (FM)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LFPV PB2V SID (Cycle 2507, ID 10395)
|
LFPV 27 PB2V SID (Cycle 2507, ID 10395)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ LFPV PB2V SID (Cycle 2507, ID 10395)
|
|||||||
- `NavDist` is in **nmi**. `NavDist` is geodesic.
|
- `NavDist` is in **nmi**. `NavDist` is geodesic.
|
||||||
|
|
||||||
|
|
||||||
## Holding mandatory (HA)
|
## Holding mandatory (altitude end) (HA)
|
||||||
|
|
||||||
### Example
|
### 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.
|
The Leg terminates after the hold is exited ath the hold entry fix.
|
||||||
|
|
||||||
|
|
||||||
## Holding mandatory (HM)
|
## Holding mandatory (manual end) (HM)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ My guess as for the missing time/distance decider field, assume it to be distanc
|
|||||||
|
|
||||||
### Example
|
### 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
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -493,8 +493,11 @@ FAUP VDM35 APP (Cycle 2507, ID 67790)
|
|||||||
### Instructions
|
### Instructions
|
||||||
|
|
||||||
- From the fix identified by (`WptID`, `WptLat`, `WptLon`) *or*
|
- From the fix identified by (`WptID`, `WptLat`, `WptLon`) *or*
|
||||||
(`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly `Course` for `Distance`.
|
(`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly direct to succeeding leg fix.
|
||||||
- Turn `TurnDir` until aligned with succeeding leg course.
|
- 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.
|
- `Alt` shall be the altitude during the procedure.
|
||||||
|
|
||||||
### Units
|
### Units
|
||||||
@@ -505,15 +508,17 @@ FAUP VDM35 APP (Cycle 2507, ID 67790)
|
|||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
This needs to look into the succeeding leg in order to calculate the intercept point.
|
This needs to look into the succeeding leg in order to fly the first segment and get the course
|
||||||
This intercept point then becomes the origin fix of the succeeding leg.
|
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)
|
## Radius to Fix (RF)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LFRN GODA5R SID (cycle 2507, ID 10485)
|
LFRN 10 GODA5R SID (cycle 2507, ID 10485)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -530,6 +535,8 @@ LFRN GODA5R SID (cycle 2507, ID 10485)
|
|||||||
- Arc center shall be navaid identified by `CenterID`, `CenterLat`, `CenterLon`.
|
- Arc center shall be navaid identified by `CenterID`, `CenterLat`, `CenterLon`.
|
||||||
- Arc and turn shall be flown in direction specified by `TurnDir`.
|
- Arc and turn shall be flown in direction specified by `TurnDir`.
|
||||||
- `Distance` shall be the track miles along the curved path
|
- `Distance` shall be the track miles along the curved path
|
||||||
|
- `Course` shall be the inbound course of the tangent to the arc at the fix identified by
|
||||||
|
(`WptID`, `WptLat`, `WptLon`).
|
||||||
|
|
||||||
### Units
|
### Units
|
||||||
|
|
||||||
@@ -542,14 +549,15 @@ While similar to an AF, the center point is coded differently.
|
|||||||
No radius is specified, but can be inferred based on center point, both endpoints and arc length
|
No radius is specified, but can be inferred based on center point, both endpoints and arc length
|
||||||
|
|
||||||
Example has `NavBear` set to `null`, significance of the inbound tangential track is unknown.
|
Example has `NavBear` set to `null`, significance of the inbound tangential track is unknown.
|
||||||
Same for the `Course`, which is set, but lacks any documentation.
|
|
||||||
|
Use `Distance` for calculations in FMS.
|
||||||
|
|
||||||
|
|
||||||
## Track to Fix (TF)
|
## Track to Fix (TF)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LFRN GODA5R SID (cycle 2507, ID 10485)
|
LFRN 10 GODA5R SID (cycle 2507, ID 10485)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -570,7 +578,7 @@ LFRN GODA5R SID (cycle 2507, ID 10485)
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LFRK LGL4X SID (Cycle 2507, ID 10475)
|
LFRK 10 LGL4X SID (Cycle 2507, ID 10475)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -598,7 +606,7 @@ This new origin is an implicit overfly.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LFRK NEVI4Y SID (Cycle 2507, ID 10482)
|
LFRK 31 NEVI4Y SID (Cycle 2507, ID 10482)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -621,14 +629,13 @@ LFRK NEVI4Y SID (Cycle 2507, ID 10482)
|
|||||||
|
|
||||||
The leg terminates upon reaching `Distance`.
|
The leg terminates upon reaching `Distance`.
|
||||||
This intercept point then becomes the origin fix of the succeeding leg.
|
This intercept point then becomes the origin fix of the succeeding leg.
|
||||||
This new origin is an implicit overfly.
|
|
||||||
|
|
||||||
|
|
||||||
## Heading to Intercept (VI)
|
## Heading to Intercept (VI)
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LFRK LUSI4Y SID (Cycle 2507, ID 10480)
|
LFRK 31 LUSI4Y SID (Cycle 2507, ID 10480)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -655,7 +662,7 @@ This new origin can never be an overfly due to the intercept nature.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LFPV PB2P SID (Cycle 2507, ID 10394)
|
LFPV 27 PB2P SID (Cycle 2507, ID 10394)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -675,7 +682,7 @@ LFPV PB2P SID (Cycle 2507, ID 10394)
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
LIMC MMP8G SID (Cycle 2507, ID 11909)
|
LIMC 35R MMP8G SID (Cycle 2507, ID 11909)
|
||||||
|
|
||||||
### Minimum Required Fields
|
### Minimum Required Fields
|
||||||
|
|
||||||
@@ -695,4 +702,28 @@ LIMC MMP8G SID (Cycle 2507, ID 11909)
|
|||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
The leg terminates at the intercept point.
|
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`
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
Revise list of charts to only show those applicable to the selected procedure (so terminal itself and all transitions accompanied)
|
|
||||||
Revise image overlay
|
|
||||||
- Find center of full page
|
|
||||||
- Find center of georeferenced area
|
|
||||||
- Calculate skew parameters
|
|
||||||
- Skew geobounds
|
|
||||||
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,78 +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 { useNavigraphAuth } from './hooks/useNavigraphAuth';
|
|
||||||
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 [procedures, setProcedures] = useState<{ name: string; data: object }[]>([]);
|
|
||||||
const [params, setParams] = useState<DeviceFlowParams | null>(null);
|
|
||||||
|
|
||||||
const { user, signIn, initialized } = useNavigraphAuth();
|
|
||||||
|
|
||||||
const handleSignIn = () => signIn((p) => setParams(p));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{procedures.length === 0 ? (
|
|
||||||
<div className="flex min-h-dvh w-full">
|
|
||||||
{!initialized && <div>Loading...</div>}
|
|
||||||
|
|
||||||
{initialized && !params && !user && <button onClick={handleSignIn}>Sign in</button>}
|
|
||||||
|
|
||||||
{params?.verification_uri_complete && !user && (
|
|
||||||
<>
|
|
||||||
<QRCodeSVG value={params.verification_uri_complete} size={250} />
|
|
||||||
<a href={params.verification_uri_complete} target="_blank" rel="noreferrer">
|
|
||||||
Open sign in page
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{user && (
|
|
||||||
<ProcedureSelect
|
|
||||||
selectedAirport={selectedAirport}
|
|
||||||
selectedRunway={selectedRunway}
|
|
||||||
selectedTerminal={selectedTerminal}
|
|
||||||
setSelectedAirport={setSelectedAirport}
|
|
||||||
setSelectedRunway={setSelectedRunway}
|
|
||||||
setSelectedTerminal={setSelectedTerminal}
|
|
||||||
handleSelection={(selectedTransitions) =>
|
|
||||||
setProcedures(
|
|
||||||
selectedTransitions.map((transition) => ({
|
|
||||||
name: transition,
|
|
||||||
data: parser.parse(selectedRunway!, transition),
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex h-dvh w-dvw">
|
|
||||||
{procedures.length > 0 && selectedAirport && selectedTerminal ? (
|
|
||||||
<Map
|
|
||||||
airport={selectedAirport}
|
|
||||||
terminal={selectedTerminal}
|
|
||||||
procedures={procedures}
|
|
||||||
backAction={() => {
|
|
||||||
setSelectedTerminal(undefined);
|
|
||||||
setProcedures([]);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<h1 className="text-center text-3xl">Error</h1>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
import BrowserImageManipulation from 'browser-image-manipulation';
|
|
||||||
import { default as L, type LatLngBoundsExpression } from 'leaflet';
|
|
||||||
import 'leaflet-svg-shape-markers';
|
|
||||||
import { type Chart } from 'navigraph/charts';
|
|
||||||
import { createRef, useEffect, useState, type FC } from 'react';
|
|
||||||
import { GeoJSON, ImageOverlay, MapContainer, TileLayer } from 'react-leaflet';
|
|
||||||
import { charts } from '../lib/navigraph';
|
|
||||||
|
|
||||||
interface MapProps {
|
|
||||||
airport: Airport;
|
|
||||||
terminal: Terminal;
|
|
||||||
procedures: { name: string; data: object }[];
|
|
||||||
backAction: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Map: FC<MapProps> = ({ airport, terminal, procedures, backAction }) => {
|
|
||||||
const [selectedProcedure, setSelectedProcedure] = useState(procedures[0]);
|
|
||||||
const [chartIndex, setChartIndex] = useState<Chart[]>([]);
|
|
||||||
const [selectedChart, setSelectedChart] = useState<{
|
|
||||||
data: string;
|
|
||||||
index_number: string;
|
|
||||||
bounds: LatLngBoundsExpression;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const mapRef = createRef<L.Map>();
|
|
||||||
const imageRef = createRef<L.ImageOverlay>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
setChartIndex((await charts.getChartsIndex({ icao: airport.ICAO, version: 'STD' })) ?? []);
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
className="fixed top-2 left-2 z-[5000] cursor-pointer rounded border border-red-500 bg-red-500 px-2 py-1 font-semibold text-stone-50 focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black"
|
|
||||||
onClick={backAction}
|
|
||||||
>
|
|
||||||
Go back
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<MapContainer
|
|
||||||
center={[airport.Latitude, airport.Longitude]}
|
|
||||||
zoom={13}
|
|
||||||
zoomSnap={0}
|
|
||||||
className="h-full w-full"
|
|
||||||
ref={(_mapRef) => {
|
|
||||||
_mapRef?.attributionControl.setPosition('topright');
|
|
||||||
_mapRef?.zoomControl.setPosition('topright');
|
|
||||||
mapRef.current = _mapRef;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TileLayer
|
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{selectedChart && selectedChart.bounds && (
|
|
||||||
<ImageOverlay
|
|
||||||
url={selectedChart.data}
|
|
||||||
bounds={selectedChart.bounds}
|
|
||||||
opacity={0.75}
|
|
||||||
ref={(_imageRef) => {
|
|
||||||
if (_imageRef) {
|
|
||||||
mapRef.current?.fitBounds(_imageRef.getBounds(), {
|
|
||||||
padding: [-50, -50],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
imageRef.current = _imageRef;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<GeoJSON
|
|
||||||
key={`${selectedProcedure.name}-lines`}
|
|
||||||
data={selectedProcedure.data}
|
|
||||||
style={({ properties }) => ({
|
|
||||||
color: '#ff00ff',
|
|
||||||
stroke: true,
|
|
||||||
weight: 5,
|
|
||||||
opacity: 1,
|
|
||||||
dashArray: properties.isManual ? '20, 20' : undefined,
|
|
||||||
})}
|
|
||||||
filter={(feature) => feature.geometry.type !== 'Point'}
|
|
||||||
/>
|
|
||||||
<GeoJSON
|
|
||||||
key={`${selectedProcedure.name}-points`}
|
|
||||||
data={selectedProcedure.data}
|
|
||||||
style={{
|
|
||||||
color: 'black',
|
|
||||||
fill: true,
|
|
||||||
fillColor: 'transparent',
|
|
||||||
stroke: true,
|
|
||||||
weight: 3,
|
|
||||||
}}
|
|
||||||
pointToLayer={({ properties }, latlng) => {
|
|
||||||
if (properties.isFlyOver)
|
|
||||||
return L.shapeMarker(latlng, {
|
|
||||||
shape: 'triangle',
|
|
||||||
radius: 6,
|
|
||||||
});
|
|
||||||
if (properties.isIntersection) return L.circleMarker(latlng, { radius: 6 });
|
|
||||||
|
|
||||||
return L.shapeMarker(latlng, {
|
|
||||||
shape: 'star-4',
|
|
||||||
radius: 10,
|
|
||||||
rotation: 45,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onEachFeature={({ geometry, properties }, layer) => {
|
|
||||||
if (geometry.type === 'Point') {
|
|
||||||
layer.bindPopup(
|
|
||||||
`${properties.name}<br>
|
|
||||||
${properties.altitude} ft<br>
|
|
||||||
${properties.speed} kts<br>
|
|
||||||
CNSTR:
|
|
||||||
${properties.altitudeConstraint ?? ''}
|
|
||||||
${properties.speedConstraint ?? ''}<br>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
filter={(feature) => feature.geometry.type === 'Point'}
|
|
||||||
/>
|
|
||||||
</MapContainer>
|
|
||||||
|
|
||||||
<div className="absolute right-0 bottom-0 left-0 z-[5000] bg-[#ffffff77] bg-blend-color backdrop-blur-xs">
|
|
||||||
<div className="flex items-center gap-2 overflow-x-auto p-2">
|
|
||||||
<span className="text-lg font-semibold">Procedures:</span>
|
|
||||||
{procedures.map((procedure) => (
|
|
||||||
<button
|
|
||||||
key={procedure.name}
|
|
||||||
className={`cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${selectedProcedure.name === procedure.name ? 'outline-2' : ''}`}
|
|
||||||
onClick={() => {
|
|
||||||
if (selectedProcedure.name === procedure.name) return;
|
|
||||||
|
|
||||||
setSelectedProcedure(procedure);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{procedure.name ? procedure.name : 'ZZZZ'}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-lg font-semibold">Charts:</span>
|
|
||||||
<div className="flex items-center gap-2 overflow-x-auto p-2">
|
|
||||||
{chartIndex
|
|
||||||
.filter((chart) => chart.is_georeferenced)
|
|
||||||
.map((chart) => (
|
|
||||||
<button
|
|
||||||
key={chart.index_number}
|
|
||||||
className={`cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold whitespace-nowrap focus:outline-2 focus:outline-black focus-visible:outline-2 focus-visible:outline-black ${selectedChart?.index_number === chart.index_number ? 'outline-2' : ''}`}
|
|
||||||
onClick={async () => {
|
|
||||||
if (selectedChart?.index_number === chart.index_number) return;
|
|
||||||
|
|
||||||
if (!mapRef.current) return;
|
|
||||||
|
|
||||||
if (!chart.bounding_boxes) return;
|
|
||||||
|
|
||||||
const planView = chart.bounding_boxes.planview;
|
|
||||||
|
|
||||||
const chartImage = await charts.getChartImage({ chart, theme: 'light' });
|
|
||||||
if (!chartImage) return;
|
|
||||||
// Crop
|
|
||||||
const dataURL = await new BrowserImageManipulation()
|
|
||||||
.loadBlob(chartImage)
|
|
||||||
.crop(
|
|
||||||
planView.pixels.x2 - planView.pixels.x1,
|
|
||||||
planView.pixels.y1 - planView.pixels.y2,
|
|
||||||
planView.pixels.x1,
|
|
||||||
planView.pixels.y2
|
|
||||||
)
|
|
||||||
.saveAsImage();
|
|
||||||
|
|
||||||
const bounds = new L.LatLngBounds(
|
|
||||||
[planView.latlng.lat1, planView.latlng.lng1],
|
|
||||||
[planView.latlng.lat2, planView.latlng.lng2]
|
|
||||||
);
|
|
||||||
|
|
||||||
setSelectedChart({ data: dataURL, index_number: chart.index_number, bounds });
|
|
||||||
|
|
||||||
if (imageRef.current) {
|
|
||||||
mapRef.current?.fitBounds(imageRef.current.getBounds(), {
|
|
||||||
padding: [-50, -50],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{chart.name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -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,35 +0,0 @@
|
|||||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
|
||||||
|
|
||||||
export const TerminatorsCF = (
|
|
||||||
leg: CFTerminalEntry,
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compute arc
|
|
||||||
const line = handleTurnAtFix(
|
|
||||||
leg.Course.toTrue(previousFix),
|
|
||||||
leg.Course.toTrue(previousFix),
|
|
||||||
lastCourse,
|
|
||||||
previousFix,
|
|
||||||
targetFix,
|
|
||||||
speed,
|
|
||||||
leg.TurnDir
|
|
||||||
);
|
|
||||||
|
|
||||||
return [targetFix, line];
|
|
||||||
};
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
|
||||||
import { computeIntersection } from '../utils/computeIntersection';
|
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
|
||||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';
|
|
||||||
|
|
||||||
export const TerminatorsCI = (
|
|
||||||
leg: CITerminalEntry,
|
|
||||||
nextLeg: TerminalEntry,
|
|
||||||
previousFix: NavFix,
|
|
||||||
lastCourse: number
|
|
||||||
): [NavFix?, LineSegment[]?] => {
|
|
||||||
const [crs, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix);
|
|
||||||
const speed = computeSpeed(leg, previousFix);
|
|
||||||
|
|
||||||
// Compute intercept fix
|
|
||||||
const interceptFix: NavFix = {
|
|
||||||
...computeIntersection(previousFix, leg.Course.toTrue(nextFix), nextFix, crs)!,
|
|
||||||
isFlyOver: leg.IsFlyOver,
|
|
||||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
|
||||||
speed: speed,
|
|
||||||
speedConstraint: leg.SpeedLimit,
|
|
||||||
altitudeConstraint: leg.Alt,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compute arc
|
|
||||||
const line = handleTurnAtFix(
|
|
||||||
crs,
|
|
||||||
leg.Course.toTrue(nextFix),
|
|
||||||
lastCourse,
|
|
||||||
previousFix,
|
|
||||||
interceptFix,
|
|
||||||
speed,
|
|
||||||
leg.TurnDir
|
|
||||||
);
|
|
||||||
|
|
||||||
// Recompute intercept
|
|
||||||
const interceptPoint2 = computeIntersection(
|
|
||||||
{ latitude: line.at(-2)![1], longitude: line.at(-2)![0] },
|
|
||||||
leg.Course.toTrue(nextFix),
|
|
||||||
nextFix,
|
|
||||||
crs
|
|
||||||
);
|
|
||||||
if (interceptPoint2)
|
|
||||||
return [
|
|
||||||
{ ...interceptFix, ...interceptPoint2 },
|
|
||||||
[...line.slice(0, -1), [interceptPoint2.longitude, interceptPoint2.latitude]],
|
|
||||||
];
|
|
||||||
|
|
||||||
return [interceptFix, line];
|
|
||||||
};
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import * as geolib from 'geolib';
|
|
||||||
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 = geolib.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,76 +0,0 @@
|
|||||||
import * as geolib from 'geolib';
|
|
||||||
import Parser from '../parser';
|
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
|
||||||
|
|
||||||
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 = geolib.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 = geolib.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 = geolib.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,50 +0,0 @@
|
|||||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
|
||||||
import { computeIntersection } from '../utils/computeIntersection';
|
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
|
||||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';
|
|
||||||
|
|
||||||
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
|
||||||
export const TerminatorsVI = (
|
|
||||||
leg: VITerminalEntry,
|
|
||||||
nextLeg: TerminalEntry,
|
|
||||||
previousFix: NavFix,
|
|
||||||
lastCourse: number
|
|
||||||
): [NavFix?, LineSegment[]?] => {
|
|
||||||
const [crs, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix);
|
|
||||||
const speed = computeSpeed(leg, previousFix);
|
|
||||||
|
|
||||||
// Compute INTC
|
|
||||||
const interceptFix: NavFix = {
|
|
||||||
...computeIntersection(previousFix, leg.Course.toTrue(nextFix), nextFix, crs)!,
|
|
||||||
isFlyOver: leg.IsFlyOver,
|
|
||||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
|
||||||
speed: speed,
|
|
||||||
speedConstraint: leg.SpeedLimit,
|
|
||||||
altitudeConstraint: leg.Alt,
|
|
||||||
};
|
|
||||||
|
|
||||||
const line = handleTurnAtFix(
|
|
||||||
crs,
|
|
||||||
leg.Course.toTrue(nextFix),
|
|
||||||
lastCourse,
|
|
||||||
previousFix,
|
|
||||||
interceptFix,
|
|
||||||
speed,
|
|
||||||
leg.TurnDir
|
|
||||||
);
|
|
||||||
|
|
||||||
// Intercept based on previous intercept
|
|
||||||
const interceptPoint2 = computeIntersection(
|
|
||||||
{ latitude: line.at(-2)![1], longitude: line.at(-2)![0] },
|
|
||||||
leg.Course.toTrue(nextFix),
|
|
||||||
nextFix,
|
|
||||||
crs
|
|
||||||
);
|
|
||||||
if (interceptPoint2)
|
|
||||||
return [
|
|
||||||
{ ...interceptFix, ...interceptPoint2 },
|
|
||||||
[...line.slice(0, -1), [interceptPoint2.longitude, interceptPoint2.latitude]],
|
|
||||||
];
|
|
||||||
|
|
||||||
return [interceptFix, 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",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint . --ext .ts,.tsx --fix",
|
"lint": "eslint . --ext .ts,.tsx --fix",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"parser": "node --experimental-strip-types src/parser/node.ts"
|
"serve": "pnpm npx http-server dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browser-image-manipulation": "^0.4.0",
|
"browser-image-manipulation": "^0.4.0",
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
"geolib": "^3.3.4",
|
"geolib": "^3.3.4",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet-svg-shape-markers": "^1.4.0",
|
"leaflet-svg-shape-markers": "^1.4.0",
|
||||||
"magvar": "^2.0.0",
|
|
||||||
"navigraph": "^1.4.1",
|
"navigraph": "^1.4.1",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
@@ -23,9 +23,6 @@ importers:
|
|||||||
leaflet-svg-shape-markers:
|
leaflet-svg-shape-markers:
|
||||||
specifier: ^1.4.0
|
specifier: ^1.4.0
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
magvar:
|
|
||||||
specifier: ^2.0.0
|
|
||||||
version: 2.0.0
|
|
||||||
navigraph:
|
navigraph:
|
||||||
specifier: ^1.4.1
|
specifier: ^1.4.1
|
||||||
version: 1.4.1
|
version: 1.4.1
|
||||||
@@ -1102,9 +1099,6 @@ packages:
|
|||||||
magic-string@0.30.17:
|
magic-string@0.30.17:
|
||||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||||
|
|
||||||
magvar@2.0.0:
|
|
||||||
resolution: {integrity: sha512-00LpSwEJZcnyX3VsdCM2CHSCvB+M6sVQTawLCB3J9oK7eEueNltNx9GFL4YR+/HIkLM+l8rFKiY77JTKnOf0jw==}
|
|
||||||
|
|
||||||
merge2@1.4.1:
|
merge2@1.4.1:
|
||||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -2339,8 +2333,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/sourcemap-codec': 1.5.4
|
'@jridgewell/sourcemap-codec': 1.5.4
|
||||||
|
|
||||||
magvar@2.0.0: {}
|
|
||||||
|
|
||||||
merge2@1.4.1: {}
|
merge2@1.4.1: {}
|
||||||
|
|
||||||
micromatch@4.0.8:
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import { default as L } from 'leaflet';
|
||||||
|
import 'leaflet-svg-shape-markers';
|
||||||
|
import { createRef, Fragment, useEffect, type FC } from 'react';
|
||||||
|
import { GeoJSON, ImageOverlay, MapContainer, TileLayer } from 'react-leaflet';
|
||||||
|
|
||||||
|
interface MapProps {
|
||||||
|
airport: Airport | undefined;
|
||||||
|
chart: Chart | undefined;
|
||||||
|
procedures: 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={[0, 0]}
|
||||||
|
zoom={5}
|
||||||
|
zoomSnap={0}
|
||||||
|
className="h-full w-full"
|
||||||
|
ref={(_mapRef) => {
|
||||||
|
_mapRef?.attributionControl.setPosition('topright');
|
||||||
|
_mapRef?.zoomControl.setPosition('topright');
|
||||||
|
mapRef.current = _mapRef;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TileLayer
|
||||||
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{chart && chart.bounds && (
|
||||||
|
<ImageOverlay
|
||||||
|
url={chart.data}
|
||||||
|
bounds={chart.bounds}
|
||||||
|
opacity={0.75}
|
||||||
|
ref={(_imageRef) => {
|
||||||
|
if (_imageRef) {
|
||||||
|
mapRef.current?.fitBounds(_imageRef.getBounds(), {
|
||||||
|
padding: [-50, -50],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
imageRef.current = _imageRef;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{procedures.map((procedure) => (
|
||||||
|
<Fragment key={procedure.name}>
|
||||||
|
<GeoJSON
|
||||||
|
key={`${procedure.name}-lines`}
|
||||||
|
data={procedure.data}
|
||||||
|
style={({ properties }) => ({
|
||||||
|
color: properties.isMAP ? '#00ffff' : '#ff00ff',
|
||||||
|
stroke: true,
|
||||||
|
weight: properties.isMAP ? 2.5 : 5,
|
||||||
|
opacity: 1,
|
||||||
|
dashArray: properties.isManual ? '20, 20' : undefined,
|
||||||
|
})}
|
||||||
|
filter={(feature) => feature.geometry.type !== 'Point'}
|
||||||
|
/>
|
||||||
|
<GeoJSON
|
||||||
|
key={`${procedure.name}-points`}
|
||||||
|
data={procedure.data}
|
||||||
|
style={{
|
||||||
|
color: 'black',
|
||||||
|
fill: true,
|
||||||
|
fillColor: 'transparent',
|
||||||
|
stroke: true,
|
||||||
|
weight: 3,
|
||||||
|
}}
|
||||||
|
pointToLayer={({ properties }, latlng) => {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
else if (properties.isInitial)
|
||||||
|
return L.shapeMarker(latlng, {
|
||||||
|
shape: 'arrowhead',
|
||||||
|
radius: 6,
|
||||||
|
});
|
||||||
|
|
||||||
|
return L.shapeMarker(latlng, {
|
||||||
|
shape: 'star-4',
|
||||||
|
radius: 10,
|
||||||
|
rotation: 45,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onEachFeature={({ geometry, properties }, layer) => {
|
||||||
|
if (geometry.type === 'Point') {
|
||||||
|
layer.bindPopup(
|
||||||
|
`${properties.name}<br>
|
||||||
|
${properties.altitude} ft<br>
|
||||||
|
${properties.speed} kts<br>
|
||||||
|
CNSTR:
|
||||||
|
${properties.altitudeConstraint ?? ''}
|
||||||
|
${properties.speedConstraint ?? ''}<br>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
filter={(feature) => feature.geometry.type === 'Point'}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</MapContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { type User } from 'navigraph/auth';
|
||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
import { auth } from '../../lib/navigraph';
|
||||||
|
|
||||||
|
interface NavigraphAuthContext {
|
||||||
|
initialized: boolean;
|
||||||
|
user: User | null;
|
||||||
|
signIn: typeof auth.signInWithDeviceFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authContext = createContext<NavigraphAuthContext>({
|
||||||
|
initialized: false,
|
||||||
|
user: null,
|
||||||
|
signIn: () => Promise.reject('Not initialized'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useNavigraphAuth = () => {
|
||||||
|
return useContext(authContext);
|
||||||
|
};
|
||||||
+6
-22
@@ -1,18 +1,7 @@
|
|||||||
import { type User } from 'navigraph/auth';
|
import type { User } from 'navigraph/auth';
|
||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { auth } from '../lib/navigraph';
|
import { auth } from '../../lib/navigraph';
|
||||||
|
import { authContext } from './NavigraphAuthContext';
|
||||||
interface NavigraphAuthContext {
|
|
||||||
initialized: boolean;
|
|
||||||
user: User | null;
|
|
||||||
signIn: typeof auth.signInWithDeviceFlow;
|
|
||||||
}
|
|
||||||
|
|
||||||
const authContext = createContext<NavigraphAuthContext>({
|
|
||||||
initialized: false,
|
|
||||||
user: null,
|
|
||||||
signIn: () => Promise.reject('Not initialized'),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Provider hook that creates auth object and handles state
|
// Provider hook that creates auth object and handles state
|
||||||
function useProvideAuth() {
|
function useProvideAuth() {
|
||||||
@@ -42,12 +31,7 @@ function useProvideAuth() {
|
|||||||
// Provider component that wraps your app and makes auth object
|
// Provider component that wraps your app and makes auth object
|
||||||
// available to any child component that calls useAuth().
|
// available to any child component that calls useAuth().
|
||||||
export function NavigraphAuthProvider({ children }: { children: React.ReactNode }) {
|
export function NavigraphAuthProvider({ children }: { children: React.ReactNode }) {
|
||||||
const auth = useProvideAuth();
|
const _auth = useProvideAuth();
|
||||||
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
|
return <authContext.Provider value={_auth}>{children}</authContext.Provider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hook for child components to get the auth object
|
|
||||||
// and re-render when it changes.
|
|
||||||
export const useNavigraphAuth = () => {
|
|
||||||
return useContext(authContext);
|
|
||||||
};
|
|
||||||
@@ -3,7 +3,7 @@ import { createRoot } from 'react-dom/client';
|
|||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import { NavigraphAuthProvider } from './hooks/useNavigraphAuth.tsx';
|
import { NavigraphAuthProvider } from './contexts/NavigraphAuth/NavigraphAuthProvider.tsx';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import geojson from 'geojson';
|
import geojson from 'geojson';
|
||||||
import * as geolib from 'geolib';
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
import { TerminatorsAF } from './terminators/AF';
|
import { TerminatorsAF } from './terminators/AF';
|
||||||
import { TerminatorsCA } from './terminators/CA';
|
import { TerminatorsCA } from './terminators/CA';
|
||||||
import { TerminatorsCD } from './terminators/CD';
|
import { TerminatorsCD } from './terminators/CD';
|
||||||
@@ -11,7 +11,11 @@ import { TerminatorsFA } from './terminators/FA';
|
|||||||
import { TerminatorsFC } from './terminators/FC';
|
import { TerminatorsFC } from './terminators/FC';
|
||||||
import { TerminatorsFD } from './terminators/FD';
|
import { TerminatorsFD } from './terminators/FD';
|
||||||
import { TerminatorsFM } from './terminators/FM';
|
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 { TerminatorsIF } from './terminators/IF';
|
||||||
|
import { TerminatorsPI } from './terminators/PI';
|
||||||
import { TerminatorsRF } from './terminators/RF';
|
import { TerminatorsRF } from './terminators/RF';
|
||||||
import { TerminatorsTF } from './terminators/TF';
|
import { TerminatorsTF } from './terminators/TF';
|
||||||
import { TerminatorsVA } from './terminators/VA';
|
import { TerminatorsVA } from './terminators/VA';
|
||||||
@@ -21,12 +25,6 @@ import { TerminatorsVM } from './terminators/VM';
|
|||||||
import { TerminatorsVR } from './terminators/VR';
|
import { TerminatorsVR } from './terminators/VR';
|
||||||
import './utils/extensions';
|
import './utils/extensions';
|
||||||
|
|
||||||
/*
|
|
||||||
Runway IDs for LIED
|
|
||||||
26156 - 34L
|
|
||||||
26157 - 34R
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
private static _instance: Parser;
|
private static _instance: Parser;
|
||||||
|
|
||||||
@@ -37,11 +35,19 @@ class Parser {
|
|||||||
|
|
||||||
private _procedures: TerminalEntry[] = [];
|
private _procedures: TerminalEntry[] = [];
|
||||||
|
|
||||||
|
public static MAGVAR: Uint8Array;
|
||||||
public static AC_SPEED = 250;
|
public static AC_SPEED = 250;
|
||||||
public static AC_BANK = 30;
|
public static AC_BANK = 30;
|
||||||
public static AC_VS = 1400;
|
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._airports = airports;
|
||||||
this._waypoints = waypoints;
|
this._waypoints = waypoints;
|
||||||
this._runways = runways;
|
this._runways = runways;
|
||||||
@@ -50,12 +56,13 @@ class Parser {
|
|||||||
|
|
||||||
public static instance = async () => {
|
public static instance = async () => {
|
||||||
if (!Parser._instance) {
|
if (!Parser._instance) {
|
||||||
|
const magVar = await (await fetch('NavData/magdec.bgl')).bytes();
|
||||||
const airports = await (await fetch('NavData/Airports.json')).json();
|
const airports = await (await fetch('NavData/Airports.json')).json();
|
||||||
const waypoints = await (await fetch('NavData/Waypoints.json')).json();
|
const waypoints = await (await fetch('NavData/Waypoints.json')).json();
|
||||||
const runways = await (await fetch('NavData/Runways.json')).json();
|
const runways = await (await fetch('NavData/Runways.json')).json();
|
||||||
const terminals = await (await fetch('NavData/Terminals.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;
|
return Parser._instance;
|
||||||
@@ -84,13 +91,13 @@ class Parser {
|
|||||||
).json()) as TerminalEntry[];
|
).json()) as TerminalEntry[];
|
||||||
};
|
};
|
||||||
|
|
||||||
public parse = (runway: Runway, transition: string) => {
|
public parse = (runway: Runway, terminal: Terminal, transition: string) => {
|
||||||
// Private functions
|
// Private functions
|
||||||
/**
|
/**
|
||||||
* @param line Line segments
|
* @param line Line segments
|
||||||
*/
|
*/
|
||||||
const updateLastCourse = (line: LineSegment[]) => {
|
const updateLastCourse = (line: LineSegment[]) => {
|
||||||
lastCourse = geolib.getGreatCircleBearing(
|
lastCourse = getGreatCircleBearing(
|
||||||
{
|
{
|
||||||
latitude: line.at(-2)![1],
|
latitude: line.at(-2)![1],
|
||||||
longitude: line.at(-2)![0],
|
longitude: line.at(-2)![0],
|
||||||
@@ -119,142 +126,206 @@ class Parser {
|
|||||||
const navFixes: NavFix[] = [];
|
const navFixes: NavFix[] = [];
|
||||||
const lineSegments: { line: LineSegment[]; [x: string]: unknown }[] = [];
|
const lineSegments: { line: LineSegment[]; [x: string]: unknown }[] = [];
|
||||||
|
|
||||||
// Initials
|
let lastCourse = 0;
|
||||||
navFixes.push({
|
|
||||||
latitude: runway.Latitude,
|
// RWY as origin for Departures
|
||||||
longitude: runway.Longitude,
|
if (terminal.Proc === 2) {
|
||||||
altitude: runway.Elevation,
|
navFixes.push({
|
||||||
speed: 0,
|
latitude: runway.Latitude,
|
||||||
name: runway.Ident,
|
longitude: runway.Longitude,
|
||||||
});
|
altitude: runway.Elevation,
|
||||||
let lastCourse = runway.TrueHeading;
|
speed: 0,
|
||||||
const procedure = this._procedures.filter(({ Transition }) => !Transition || Transition === transition);
|
name: runway.Ident,
|
||||||
|
});
|
||||||
|
lastCourse = runway.TrueHeading;
|
||||||
|
}
|
||||||
|
const legOptions = { isMAP: false };
|
||||||
|
const procedure = this._procedures.filter(
|
||||||
|
({ Transition }) => !Transition || Transition === transition || Transition === 'ALL'
|
||||||
|
);
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
for (let index = 0; index < procedure.length; index++) {
|
for (let index = 0; index < procedure.length; index++) {
|
||||||
|
console.log('Processing leg with ID', procedure[index].ID);
|
||||||
|
|
||||||
const leg = procedure[index];
|
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 previousFix = navFixes.at(-1)!;
|
||||||
const waypoint = this.waypoints.filter(({ ID }) => ID === leg.WptID)[0];
|
legOptions.isMAP ||= previousFix.IsMAP ?? false;
|
||||||
|
|
||||||
switch (leg.TrackCode) {
|
switch (leg.TrackCode) {
|
||||||
case 'AF': {
|
case 'AF': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsAF(leg as AFTerminalEntry, previousFix, waypoint);
|
const [fixToAdd, lineToAdd] = TerminatorsAF(leg as AFTerminalEntry, previousFix, waypoint);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'CA': {
|
case 'CA': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsCA(leg as CATerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsCA(leg as CATerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'CD': {
|
case 'CD': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsCD(leg as CDTerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsCD(leg as CDTerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'CF': {
|
case 'CF': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsCF(leg as CFTerminalEntry, previousFix, lastCourse, waypoint);
|
const [fixToAdd, lineToAdd] = TerminatorsCF(
|
||||||
update(fixToAdd, lineToAdd);
|
leg as CFTerminalEntry,
|
||||||
|
{ ...previousFix }, // COPY
|
||||||
|
lastCourse,
|
||||||
|
waypoint
|
||||||
|
);
|
||||||
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'CI': {
|
case 'CI': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsCI(
|
const [fixToAdd, lineToAdd] = TerminatorsCI(
|
||||||
leg as CITerminalEntry,
|
leg as CITerminalEntry,
|
||||||
procedure[index + 1],
|
procedure[index + 1],
|
||||||
previousFix,
|
{ ...previousFix }, // COPY
|
||||||
lastCourse
|
lastCourse
|
||||||
);
|
);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'CR': {
|
case 'CR': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsCR(leg as CRTerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsCR(leg as CRTerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'DF': {
|
case 'DF': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsDF(leg as DFTerminalEntry, previousFix, lastCourse, waypoint);
|
const [fixToAdd, lineToAdd] = TerminatorsDF(leg as DFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'FA': {
|
case 'FA': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsFA(leg as FATerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsFA(leg as FATerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'FC': {
|
case 'FC': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsFC(leg as FCTerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsFC(leg as FCTerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'FD': {
|
case 'FD': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsFD(leg as FDTerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsFD(leg as FDTerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'FM': {
|
case 'FM': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsFM(leg as FMTerminalEntry, previousFix, lastCourse);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 'HA':
|
case 'HA': {
|
||||||
case 'HF':
|
const [fixToAdd, lineToAdd] = TerminatorsHA(leg as HATerminalEntry, previousFix);
|
||||||
case 'HM':
|
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||||
console.error('Unknown TrackCode', leg.TrackCode);
|
|
||||||
break;
|
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': {
|
case 'IF': {
|
||||||
const fixToAdd = TerminatorsIF(leg as RFTerminalEntry, waypoint);
|
const fixToAdd = TerminatorsIF(leg as RFTerminalEntry, waypoint);
|
||||||
navFixes.length = 0;
|
// Only Runway, replace
|
||||||
navFixes.push(fixToAdd);
|
if (navFixes.length <= 1) {
|
||||||
|
navFixes.push(fixToAdd);
|
||||||
|
lastCourse = -1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'PI':
|
case 'PI': {
|
||||||
console.error('Unknown TrackCode', leg.TrackCode);
|
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;
|
break;
|
||||||
|
}
|
||||||
case 'RF': {
|
case 'RF': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsRF(
|
const [fixToAdd, lineToAdd] = TerminatorsRF(leg as RFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||||
leg as RFTerminalEntry,
|
if (fixToAdd) {
|
||||||
procedure[index + 1],
|
navFixes.push(fixToAdd);
|
||||||
previousFix,
|
lastCourse = (leg as RFTerminalEntry).Course?.toTrue(fixToAdd);
|
||||||
lastCourse,
|
}
|
||||||
waypoint
|
if (lineToAdd) {
|
||||||
);
|
lineSegments.push({ line: lineToAdd, ...legOptions });
|
||||||
update(fixToAdd, lineToAdd);
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'TF': {
|
case 'TF': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsTF(leg as TFTerminalEntry, previousFix, lastCourse, waypoint);
|
const [fixToAdd, lineToAdd] = TerminatorsTF(
|
||||||
update(fixToAdd, lineToAdd);
|
leg as TFTerminalEntry,
|
||||||
|
{ ...previousFix }, // COPY
|
||||||
|
lastCourse,
|
||||||
|
waypoint
|
||||||
|
);
|
||||||
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'VA': {
|
case 'VA': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsVA(leg as VATerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsVA(leg as VATerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'VD': {
|
case 'VD': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsVD(leg as VDTerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsVD(leg as VDTerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'VI': {
|
case 'VI': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsVI(
|
const [fixToAdd, lineToAdd] = TerminatorsVI(
|
||||||
leg as VITerminalEntry,
|
leg as VITerminalEntry,
|
||||||
procedure[index + 1],
|
procedure[index + 1],
|
||||||
previousFix,
|
{ ...previousFix }, // COPY
|
||||||
lastCourse
|
lastCourse
|
||||||
);
|
);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'VM': {
|
case 'VM': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsVM(leg as VMTerminalEntry, previousFix, lastCourse);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 'VR': {
|
case 'VR': {
|
||||||
const [fixToAdd, lineToAdd] = TerminatorsVR(leg as VRTerminalEntry, previousFix, lastCourse);
|
const [fixToAdd, lineToAdd] = TerminatorsVR(leg as VRTerminalEntry, previousFix, lastCourse);
|
||||||
update(fixToAdd, lineToAdd);
|
update(fixToAdd, lineToAdd, legOptions);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param crsIntoEndpoint Course into arc endpoint
|
* @param crsIntoEndpoint Course into arc endpoint
|
||||||
@@ -6,7 +6,7 @@ import * as geolib from 'geolib';
|
|||||||
* @param start Arc origin point
|
* @param start Arc origin point
|
||||||
* @param center Arc center point
|
* @param center Arc center point
|
||||||
* @param radius Arc radius in nmi
|
* @param radius Arc radius in nmi
|
||||||
* @param turnDir
|
* @param turnDir Turn direction
|
||||||
* @returns Line segments
|
* @returns Line segments
|
||||||
*/
|
*/
|
||||||
export const generateAFArc = (
|
export const generateAFArc = (
|
||||||
@@ -30,16 +30,16 @@ export const generateAFArc = (
|
|||||||
while (crsFromOrigin !== crsIntoEndpoint) {
|
while (crsFromOrigin !== crsIntoEndpoint) {
|
||||||
if (turnDir === 'R') {
|
if (turnDir === 'R') {
|
||||||
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
||||||
crsFromOrigin += delta < 1 ? delta : 1;
|
crsFromOrigin += delta < 0.1 ? delta : 0.1;
|
||||||
crsFromOrigin = crsFromOrigin.normaliseDegrees();
|
crsFromOrigin = crsFromOrigin.normaliseDegrees();
|
||||||
} else {
|
} else {
|
||||||
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
||||||
crsFromOrigin -= delta < 1 ? delta : 1;
|
crsFromOrigin -= delta < 0.1 ? delta : 0.1;
|
||||||
crsFromOrigin = crsFromOrigin.normaliseDegrees();
|
crsFromOrigin = crsFromOrigin.normaliseDegrees();
|
||||||
}
|
}
|
||||||
if (crsFromOrigin === crsIntoEndpoint) break;
|
if (crsFromOrigin === crsIntoEndpoint) break;
|
||||||
|
|
||||||
const arcFix = geolib.computeDestinationPoint(center, radius.toMetre(), crsFromOrigin);
|
const arcFix = computeDestinationPoint(center, radius.toMetre(), crsFromOrigin);
|
||||||
|
|
||||||
line.push([arcFix.longitude, arcFix.latitude]);
|
line.push([arcFix.longitude, arcFix.latitude]);
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
import { generatePerformanceArc } from './generatePerformanceArc';
|
import { generatePerformanceArc } from './generatePerformanceArc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,7 +31,7 @@ export const generateOverflyArc = (
|
|||||||
// Get arc endpoint and crs into arc endpoint
|
// Get arc endpoint and crs into arc endpoint
|
||||||
const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
|
const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
|
||||||
if (line.length > 1) {
|
if (line.length > 1) {
|
||||||
crsFromOrigin = geolib.getGreatCircleBearing(
|
crsFromOrigin = getGreatCircleBearing(
|
||||||
{
|
{
|
||||||
latitude: line.at(-2)![1],
|
latitude: line.at(-2)![1],
|
||||||
longitude: line.at(-2)![0],
|
longitude: line.at(-2)![0],
|
||||||
+7
-7
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
import Parser from '../parser';
|
import Parser from '../parser';
|
||||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||||
|
|
||||||
@@ -38,17 +38,17 @@ export const generatePerformanceArc = (
|
|||||||
let time = 0;
|
let time = 0;
|
||||||
if (turnDir === 'R') {
|
if (turnDir === 'R') {
|
||||||
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
||||||
const increment = delta < 1 ? delta : 1;
|
const increment = delta < 0.1 ? delta : 0.1;
|
||||||
crsFromOrigin = (crsFromOrigin + increment).normaliseDegrees();
|
crsFromOrigin = (crsFromOrigin + increment).normaliseDegrees();
|
||||||
time = increment / turnRate;
|
time = increment / turnRate;
|
||||||
} else {
|
} else {
|
||||||
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
||||||
const increment = delta < 1 ? delta : 1;
|
const increment = delta < 0.1 ? delta : 0.1;
|
||||||
crsFromOrigin = (crsFromOrigin - increment).normaliseDegrees();
|
crsFromOrigin = (crsFromOrigin - increment).normaliseDegrees();
|
||||||
time = increment / turnRate;
|
time = increment / turnRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
const arcFix = geolib.computeDestinationPoint(
|
const arcFix = computeDestinationPoint(
|
||||||
{
|
{
|
||||||
latitude: line.at(-1)![1],
|
latitude: line.at(-1)![1],
|
||||||
longitude: line.at(-1)![0],
|
longitude: line.at(-1)![0],
|
||||||
@@ -70,17 +70,17 @@ export const generatePerformanceArc = (
|
|||||||
let time = 0;
|
let time = 0;
|
||||||
if (turnDir === 'R') {
|
if (turnDir === 'R') {
|
||||||
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees();
|
||||||
const increment = delta < 1 ? delta : 1;
|
const increment = delta < 0.1 ? delta : 0.1;
|
||||||
crsFromOrigin = (crsFromOrigin + increment).normaliseDegrees();
|
crsFromOrigin = (crsFromOrigin + increment).normaliseDegrees();
|
||||||
time = increment / turnRate;
|
time = increment / turnRate;
|
||||||
} else {
|
} else {
|
||||||
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees();
|
||||||
const increment = delta < 1 ? delta : 1;
|
const increment = delta < 0.1 ? delta : 0.1;
|
||||||
crsFromOrigin = (crsFromOrigin - increment).normaliseDegrees();
|
crsFromOrigin = (crsFromOrigin - increment).normaliseDegrees();
|
||||||
time = increment / turnRate;
|
time = increment / turnRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
const arcFix = geolib.computeDestinationPoint(
|
const arcFix = computeDestinationPoint(
|
||||||
{
|
{
|
||||||
latitude: line.at(-1)![1],
|
latitude: line.at(-1)![1],
|
||||||
longitude: line.at(-1)![0],
|
longitude: line.at(-1)![0],
|
||||||
+14
-20
@@ -1,11 +1,14 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
|
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param crsIntoEndpoint Course into arc endpoint
|
* @param crsIntoEndpoint Course into arc endpoint
|
||||||
* @param crsIntoOrigin Course into arc origin point
|
* @param crsIntoOrigin Course into arc origin point
|
||||||
* @param start Arc origin point
|
* @param start Arc origin point
|
||||||
* @param center Arc center point
|
* @param center Arc center point
|
||||||
* @param turnDir
|
* @param end Arc end point
|
||||||
|
* @param turnDir Turn Direction
|
||||||
* @returns Line segments
|
* @returns Line segments
|
||||||
*/
|
*/
|
||||||
export const generateRFArc = (
|
export const generateRFArc = (
|
||||||
@@ -13,11 +16,12 @@ export const generateRFArc = (
|
|||||||
crsIntoOrigin: number,
|
crsIntoOrigin: number,
|
||||||
start: NavFix,
|
start: NavFix,
|
||||||
center: NavFix,
|
center: NavFix,
|
||||||
|
end: NavFix,
|
||||||
turnDir: TurnDirection
|
turnDir: TurnDirection
|
||||||
) => {
|
) => {
|
||||||
const line: LineSegment[] = [[start.longitude, start.latitude]];
|
const line: LineSegment[] = [[start.longitude, start.latitude]];
|
||||||
|
|
||||||
if (crsIntoEndpoint !== crsIntoOrigin) {
|
if (!crsIntoEndpoint.equal(crsIntoOrigin)) {
|
||||||
// Turn Dir
|
// Turn Dir
|
||||||
if (!turnDir || turnDir === 'E') {
|
if (!turnDir || turnDir === 'E') {
|
||||||
let prov = crsIntoOrigin - crsIntoEndpoint;
|
let prov = crsIntoOrigin - crsIntoEndpoint;
|
||||||
@@ -25,20 +29,10 @@ export const generateRFArc = (
|
|||||||
turnDir = prov > 0 ? 'L' : 'R';
|
turnDir = prov > 0 ? 'L' : 'R';
|
||||||
}
|
}
|
||||||
|
|
||||||
let crsOrthogonalOnOrigin;
|
let crsOrthogonalOnOrigin = getGreatCircleBearing(center, start);
|
||||||
let crsOrthogonalOnEndpoint;
|
const crsOrthogonalOnEndpoint = getGreatCircleBearing(center, end);
|
||||||
if (turnDir === 'R') {
|
const arcRad = getPreciseDistance(center, start);
|
||||||
crsOrthogonalOnOrigin = (crsIntoOrigin + 90).normaliseDegrees();
|
|
||||||
crsOrthogonalOnEndpoint = (crsIntoEndpoint + 90).normaliseDegrees();
|
|
||||||
} else {
|
|
||||||
crsOrthogonalOnOrigin = (crsIntoOrigin - 90).normaliseDegrees();
|
|
||||||
crsOrthogonalOnEndpoint = (crsIntoEndpoint - 90).normaliseDegrees();
|
|
||||||
}
|
|
||||||
|
|
||||||
const arcRad = geolib.getDistance(center, start);
|
|
||||||
|
|
||||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
|
|
||||||
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
|
|
||||||
// Start turn immediately
|
// Start turn immediately
|
||||||
if (turnDir === 'R') {
|
if (turnDir === 'R') {
|
||||||
crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
||||||
@@ -46,18 +40,18 @@ export const generateRFArc = (
|
|||||||
crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (crsOrthogonalOnOrigin !== crsOrthogonalOnEndpoint) {
|
while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
|
||||||
if (turnDir === 'R') {
|
if (turnDir === 'R') {
|
||||||
const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
|
const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
|
||||||
crsOrthogonalOnOrigin += delta < 1 ? delta : 1;
|
crsOrthogonalOnOrigin += delta < 0.1 ? delta : 0.1;
|
||||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||||
} else {
|
} else {
|
||||||
const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
|
const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
|
||||||
crsOrthogonalOnOrigin -= delta < 1 ? delta : 1;
|
crsOrthogonalOnOrigin -= delta < 0.1 ? delta : 0.1;
|
||||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||||
}
|
}
|
||||||
|
|
||||||
const arcFix = geolib.computeDestinationPoint(center, arcRad, crsOrthogonalOnOrigin);
|
const arcFix = computeDestinationPoint(center, arcRad, crsOrthogonalOnOrigin);
|
||||||
|
|
||||||
line.push([arcFix.longitude, arcFix.latitude]);
|
line.push([arcFix.longitude, arcFix.latitude]);
|
||||||
}
|
}
|
||||||
+18
-7
@@ -1,4 +1,5 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||||
import { computeIntersection } from '../utils/computeIntersection';
|
import { computeIntersection } from '../utils/computeIntersection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,14 +56,19 @@ export const generateTangentArc = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate arc
|
// Generate arc
|
||||||
const arcCenter = computeIntersection(
|
let arcRad = 0;
|
||||||
|
let arcCenter = computeIntersection(
|
||||||
start,
|
start,
|
||||||
crsOrthogonalOnOrigin,
|
crsOrthogonalOnOrigin,
|
||||||
intcArcOnCrsIntoEndpoint,
|
intcArcOnCrsIntoEndpoint,
|
||||||
crsOrthogonalOnEndpoint
|
crsOrthogonalOnEndpoint
|
||||||
);
|
);
|
||||||
if (!arcCenter) return null;
|
if (!crsOrthogonalOnEndpoint.equal(crsOrthogonalOnOrigin) && arcCenter)
|
||||||
const arcRad = geolib.getDistance(arcCenter, start);
|
arcRad = getPreciseDistance(arcCenter, start);
|
||||||
|
else {
|
||||||
|
arcRad = getPreciseDistance(start, end) / 2;
|
||||||
|
arcCenter = computeDestinationPoint(start, arcRad, crsOrthogonalOnOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
|
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
|
||||||
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
|
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
|
||||||
@@ -73,18 +79,23 @@ export const generateTangentArc = (
|
|||||||
crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let lastDistance = getPreciseDistance(start, end);
|
||||||
while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
|
while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
|
||||||
if (turnDir === 'R') {
|
if (turnDir === 'R') {
|
||||||
const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
|
const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
|
||||||
crsOrthogonalOnOrigin += delta < 1 ? delta : 1;
|
crsOrthogonalOnOrigin += delta < 0.1 ? delta : 0.1;
|
||||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||||
} else {
|
} else {
|
||||||
const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
|
const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
|
||||||
crsOrthogonalOnOrigin -= delta < 1 ? delta : 1;
|
crsOrthogonalOnOrigin -= delta < 0.1 ? delta : 0.1;
|
||||||
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
|
||||||
}
|
}
|
||||||
|
|
||||||
const arcFix = geolib.computeDestinationPoint(arcCenter, arcRad, crsOrthogonalOnOrigin);
|
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]);
|
line.push([arcFix.longitude, arcFix.latitude]);
|
||||||
}
|
}
|
||||||
+3
-4
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
import { generatePerformanceArc } from './generatePerformanceArc';
|
import { generatePerformanceArc } from './generatePerformanceArc';
|
||||||
import { generateTangentArc } from './generateTangentArc';
|
import { generateTangentArc } from './generateTangentArc';
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export const handleTurnAtFix = (
|
|||||||
// Decide on arc
|
// Decide on arc
|
||||||
let arc;
|
let arc;
|
||||||
if (arc1) {
|
if (arc1) {
|
||||||
const endCrs = geolib.getGreatCircleBearing(
|
const endCrs = getGreatCircleBearing(
|
||||||
{
|
{
|
||||||
latitude: arc1.at(-1)![1],
|
latitude: arc1.at(-1)![1],
|
||||||
longitude: arc1.at(-1)![0],
|
longitude: arc1.at(-1)![0],
|
||||||
@@ -48,9 +48,8 @@ export const handleTurnAtFix = (
|
|||||||
line.push(...arc);
|
line.push(...arc);
|
||||||
line.push([end.longitude, end.latitude]);
|
line.push([end.longitude, end.latitude]);
|
||||||
}
|
}
|
||||||
// FIXME: Procedural turn
|
// Procedural turn
|
||||||
else {
|
else {
|
||||||
// Direct line for now
|
|
||||||
line.push([start.longitude, start.latitude], [end.longitude, end.latitude]);
|
line.push([start.longitude, start.latitude], [end.longitude, end.latitude]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
import { generateAFArc } from '../pathGenerators/generateAFArc';
|
import { generateAFArc } from '../pathGenerators/generateAFArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
|
|
||||||
@@ -7,6 +7,11 @@ export const TerminatorsAF = (
|
|||||||
previousFix: NavFix,
|
previousFix: NavFix,
|
||||||
waypoint?: Waypoint
|
waypoint?: Waypoint
|
||||||
): [NavFix?, LineSegment[]?] => {
|
): [NavFix?, LineSegment[]?] => {
|
||||||
|
const navaid = {
|
||||||
|
latitude: leg.NavLat,
|
||||||
|
longitude: leg.NavLon,
|
||||||
|
};
|
||||||
|
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
latitude: leg.WptLat,
|
latitude: leg.WptLat,
|
||||||
longitude: leg.WptLon,
|
longitude: leg.WptLon,
|
||||||
@@ -16,27 +21,13 @@ export const TerminatorsAF = (
|
|||||||
speed: computeSpeed(leg, previousFix),
|
speed: computeSpeed(leg, previousFix),
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
|
|
||||||
const arcEndCrs = geolib.getGreatCircleBearing(
|
const arcEndCrs = getGreatCircleBearing(navaid, targetFix);
|
||||||
{
|
|
||||||
latitude: leg.NavLat,
|
|
||||||
longitude: leg.NavLon,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
latitude: leg.WptLat,
|
|
||||||
longitude: leg.WptLon,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const line = generateAFArc(
|
const line = generateAFArc(arcEndCrs, leg.Course.toTrue(navaid), previousFix, navaid, leg.NavDist, leg.TurnDir);
|
||||||
arcEndCrs,
|
|
||||||
leg.Course.toTrue({ latitude: leg.NavLat, longitude: leg.NavLon }),
|
|
||||||
previousFix,
|
|
||||||
{ latitude: leg.NavLat, longitude: leg.NavLon },
|
|
||||||
leg.NavDist,
|
|
||||||
leg.TurnDir
|
|
||||||
);
|
|
||||||
|
|
||||||
return [targetFix, line];
|
return [targetFix, line];
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
import Parser from '../parser';
|
import Parser from '../parser';
|
||||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
@@ -17,7 +17,7 @@ export const TerminatorsCA = (
|
|||||||
|
|
||||||
// Compute intercept of crs from arc end and expected altitude
|
// Compute intercept of crs from arc end and expected altitude
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
...geolib.computeDestinationPoint(
|
...computeDestinationPoint(
|
||||||
arcEnd,
|
arcEnd,
|
||||||
(
|
(
|
||||||
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) *
|
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) *
|
||||||
@@ -31,6 +31,8 @@ export const TerminatorsCA = (
|
|||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
line.push([targetFix.longitude, targetFix.latitude]);
|
line.push([targetFix.longitude, targetFix.latitude]);
|
||||||
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
|
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
|
|
||||||
@@ -20,8 +22,8 @@ export const TerminatorsCD = (
|
|||||||
lastCourse = _lastCourse;
|
lastCourse = _lastCourse;
|
||||||
|
|
||||||
// Compute distance to fly from arc end
|
// Compute distance to fly from arc end
|
||||||
const crsToNavaid = geolib.getGreatCircleBearing(arcEnd, navaid);
|
const crsToNavaid = getGreatCircleBearing(arcEnd, navaid);
|
||||||
const distToNavaid = geolib.getDistance(arcEnd, navaid);
|
const distToNavaid = getPreciseDistance(arcEnd, navaid);
|
||||||
let remainingDistance = leg.Distance.toMetre();
|
let remainingDistance = leg.Distance.toMetre();
|
||||||
// Navaid behind us
|
// Navaid behind us
|
||||||
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
||||||
@@ -35,13 +37,15 @@ export const TerminatorsCD = (
|
|||||||
|
|
||||||
// Compute intercept of crs from arc end and distance
|
// Compute intercept of crs from arc end and distance
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse),
|
...computeDestinationPoint(arcEnd, remainingDistance, lastCourse),
|
||||||
name: leg.Distance.toString(),
|
name: leg.Distance.toString(),
|
||||||
isFlyOver: true,
|
isFlyOver: true,
|
||||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
line.push([targetFix.longitude, targetFix.latitude]);
|
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];
|
||||||
|
};
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
export const TerminatorsCI = (
|
||||||
|
leg: CITerminalEntry,
|
||||||
|
nextLeg: TerminalEntry,
|
||||||
|
previousFix: NavFix,
|
||||||
|
lastCourse: number
|
||||||
|
): [NavFix?, LineSegment[]?] => {
|
||||||
|
const speed = computeSpeed(leg, previousFix);
|
||||||
|
const crsIntoEndpoint = leg.Course.toTrue(previousFix);
|
||||||
|
const [crsToIntercept, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix);
|
||||||
|
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||||
|
|
||||||
|
// Compute overfly arc
|
||||||
|
if (previousFix.isFlyOver && !lastCourse.equal(crsIntoEndpoint)) {
|
||||||
|
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||||
|
const updatedCrsToIntercept = getGreatCircleBearing(previousFix, nextFix);
|
||||||
|
|
||||||
|
// 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) && !updatedCrsToIntercept.equal(crsToIntercept)) {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
line.push([arcFix.longitude, arcFix.latitude]);
|
||||||
|
|
||||||
|
// Update previousFix
|
||||||
|
previousFix.latitude = arcFix.latitude;
|
||||||
|
previousFix.longitude = arcFix.longitude;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const interceptFix: NavFix = {
|
||||||
|
...computeIntersection(previousFix, leg.Course.toTrue(nextFix), nextFix, crsToIntercept)!,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||||
|
|
||||||
|
return [interceptFix, line];
|
||||||
|
};
|
||||||
@@ -27,6 +27,8 @@ export const TerminatorsCR = (
|
|||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
|
|
||||||
line.push([interceptFix.longitude, interceptFix.latitude]);
|
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];
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
import Parser from '../parser';
|
import Parser from '../parser';
|
||||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
@@ -21,7 +21,7 @@ export const TerminatorsFA = (
|
|||||||
|
|
||||||
// Compute intercept of crs from arc end and expected altitude
|
// Compute intercept of crs from arc end and expected altitude
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
...geolib.computeDestinationPoint(
|
...computeDestinationPoint(
|
||||||
arcEnd,
|
arcEnd,
|
||||||
(
|
(
|
||||||
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) *
|
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) *
|
||||||
@@ -35,6 +35,8 @@ export const TerminatorsFA = (
|
|||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
line.push([targetFix.longitude, targetFix.latitude]);
|
line.push([targetFix.longitude, targetFix.latitude]);
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
import Parser from '../parser';
|
import Parser from '../parser';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||||
@@ -37,17 +38,17 @@ export const TerminatorsFC = (
|
|||||||
let time = 0;
|
let time = 0;
|
||||||
if (leg.TurnDir === 'R') {
|
if (leg.TurnDir === 'R') {
|
||||||
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees();
|
||||||
const increment = delta < 1 ? delta : 1;
|
const increment = delta < 0.1 ? delta : 0.1;
|
||||||
lastCourse = (lastCourse + increment).normaliseDegrees();
|
lastCourse = (lastCourse + increment).normaliseDegrees();
|
||||||
time = increment / turnRate;
|
time = increment / turnRate;
|
||||||
} else {
|
} else {
|
||||||
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees();
|
||||||
const increment = delta < 1 ? delta : 1;
|
const increment = delta < 0.1 ? delta : 0.1;
|
||||||
lastCourse = (lastCourse - increment).normaliseDegrees();
|
lastCourse = (lastCourse - increment).normaliseDegrees();
|
||||||
time = increment / turnRate;
|
time = increment / turnRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
const arcFix = geolib.computeDestinationPoint(
|
const arcFix = computeDestinationPoint(
|
||||||
{
|
{
|
||||||
latitude: line.at(-1)![1],
|
latitude: line.at(-1)![1],
|
||||||
longitude: line.at(-1)![0],
|
longitude: line.at(-1)![0],
|
||||||
@@ -69,7 +70,7 @@ export const TerminatorsFC = (
|
|||||||
|
|
||||||
const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
|
const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
|
||||||
if (line.length > 1) {
|
if (line.length > 1) {
|
||||||
lastCourse = geolib.getGreatCircleBearing(
|
lastCourse = getGreatCircleBearing(
|
||||||
{
|
{
|
||||||
latitude: line.at(-2)![1],
|
latitude: line.at(-2)![1],
|
||||||
longitude: line.at(-2)![0],
|
longitude: line.at(-2)![0],
|
||||||
@@ -79,13 +80,15 @@ export const TerminatorsFC = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
...geolib.computeDestinationPoint(arcEnd, leg.Distance.toMetre(), lastCourse),
|
...computeDestinationPoint(arcEnd, leg.Distance.toMetre(), leg.Course.toTrue(refFix)),
|
||||||
name: leg.Distance.toString(),
|
name: leg.Distance.toString(),
|
||||||
isFlyOver: true,
|
isFlyOver: true,
|
||||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
|
|
||||||
line.push([targetFix.longitude, targetFix.latitude]);
|
line.push([targetFix.longitude, targetFix.latitude]);
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
|
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
|
|
||||||
@@ -24,8 +26,8 @@ export const TerminatorsFD = (
|
|||||||
lastCourse = _lastCourse;
|
lastCourse = _lastCourse;
|
||||||
|
|
||||||
// Compute distance to fly from arc end
|
// Compute distance to fly from arc end
|
||||||
const crsToNavaid = geolib.getGreatCircleBearing(arcEnd, navaid);
|
const crsToNavaid = getGreatCircleBearing(arcEnd, navaid);
|
||||||
const distToNavaid = geolib.getDistance(arcEnd, navaid);
|
const distToNavaid = getPreciseDistance(arcEnd, navaid);
|
||||||
let remainingDistance = leg.Distance.toMetre();
|
let remainingDistance = leg.Distance.toMetre();
|
||||||
// Navaid behind us
|
// Navaid behind us
|
||||||
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
||||||
@@ -39,13 +41,15 @@ export const TerminatorsFD = (
|
|||||||
|
|
||||||
// Compute intercept of crs from arc end and distance
|
// Compute intercept of crs from arc end and distance
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse),
|
...computeDestinationPoint(arcEnd, remainingDistance, crsIntoEndpoint),
|
||||||
name: leg.Distance.toString(),
|
name: leg.Distance.toString(),
|
||||||
isFlyOver: true,
|
isFlyOver: true,
|
||||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
line.push([targetFix.longitude, targetFix.latitude]);
|
line.push([targetFix.longitude, targetFix.latitude]);
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ export const TerminatorsFM = (
|
|||||||
): [NavFix?, LineSegment[]?] => {
|
): [NavFix?, LineSegment[]?] => {
|
||||||
const speed = computeSpeed(leg, previousFix);
|
const speed = computeSpeed(leg, previousFix);
|
||||||
|
|
||||||
const endpoint = geolib.computeDestinationPoint(previousFix, (10).toMetre(), leg.Course.toTrue(previousFix));
|
const endpoint = computeDestinationPoint(previousFix, (10).toMetre(), leg.Course.toTrue(previousFix));
|
||||||
|
|
||||||
const line = handleTurnAtFix(
|
const line = handleTurnAtFix(
|
||||||
leg.Course.toTrue(previousFix),
|
leg.Course.toTrue(previousFix),
|
||||||
@@ -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,
|
speed: leg.SpeedLimit ? leg.SpeedLimit : Parser.AC_SPEED,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
|
isInitial: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return targetFix;
|
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,10 +1,9 @@
|
|||||||
import { generateRFArc } from '../pathGenerators/generateRFArc';
|
import { generateRFArc } from '../pathGenerators/generateRFArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
import { getCourseAndFixForIntercepts } from '../utils/getCourseAndFixForIntercepts';
|
|
||||||
|
|
||||||
|
// NOTE: Direct entry into an RF does not calculate a usable line, given inbound course is unknown.
|
||||||
export const TerminatorsRF = (
|
export const TerminatorsRF = (
|
||||||
leg: RFTerminalEntry,
|
leg: RFTerminalEntry,
|
||||||
nextLeg: TerminalEntry,
|
|
||||||
previousFix: NavFix,
|
previousFix: NavFix,
|
||||||
lastCourse: number,
|
lastCourse: number,
|
||||||
waypoint?: Waypoint
|
waypoint?: Waypoint
|
||||||
@@ -18,15 +17,16 @@ export const TerminatorsRF = (
|
|||||||
speed: computeSpeed(leg, previousFix),
|
speed: computeSpeed(leg, previousFix),
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
|
|
||||||
const [crs] = getCourseAndFixForIntercepts(nextLeg, previousFix);
|
|
||||||
|
|
||||||
const line = generateRFArc(
|
const line = generateRFArc(
|
||||||
crs,
|
leg.Course.toTrue(targetFix),
|
||||||
lastCourse,
|
lastCourse,
|
||||||
previousFix,
|
previousFix,
|
||||||
{ latitude: leg.CenterLat, longitude: leg.CenterLon },
|
{ latitude: leg.CenterLat, longitude: leg.CenterLon },
|
||||||
|
targetFix,
|
||||||
leg.TurnDir
|
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];
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
import Parser from '../parser';
|
import Parser from '../parser';
|
||||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
@@ -18,7 +18,7 @@ export const TerminatorsVA = (
|
|||||||
|
|
||||||
// Compute intercept of crs from arc end and expected altitude
|
// Compute intercept of crs from arc end and expected altitude
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
...geolib.computeDestinationPoint(
|
...computeDestinationPoint(
|
||||||
arcEnd,
|
arcEnd,
|
||||||
(
|
(
|
||||||
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) *
|
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) *
|
||||||
@@ -32,6 +32,8 @@ export const TerminatorsVA = (
|
|||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
line.push([targetFix.longitude, targetFix.latitude]);
|
line.push([targetFix.longitude, targetFix.latitude]);
|
||||||
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
|
import getPreciseDistance from 'geolib/es/getPreciseDistance';
|
||||||
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
import { generateOverflyArc } from '../pathGenerators/generateOverflyArc';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
|
|
||||||
@@ -21,8 +23,8 @@ export const TerminatorsVD = (
|
|||||||
lastCourse = _lastCourse;
|
lastCourse = _lastCourse;
|
||||||
|
|
||||||
// Compute distance to fly from arc end
|
// Compute distance to fly from arc end
|
||||||
const crsToNavaid = geolib.getGreatCircleBearing(arcEnd, navaid);
|
const crsToNavaid = getGreatCircleBearing(arcEnd, navaid);
|
||||||
const distToNavaid = geolib.getDistance(arcEnd, navaid);
|
const distToNavaid = getPreciseDistance(arcEnd, navaid);
|
||||||
let remainingDistance = leg.Distance.toMetre();
|
let remainingDistance = leg.Distance.toMetre();
|
||||||
// Navaid behind us
|
// Navaid behind us
|
||||||
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
if (Math.abs(crsToNavaid - lastCourse) > 90) {
|
||||||
@@ -36,13 +38,15 @@ export const TerminatorsVD = (
|
|||||||
|
|
||||||
// Compute intercept of crs from arc end and distance
|
// Compute intercept of crs from arc end and distance
|
||||||
const targetFix: NavFix = {
|
const targetFix: NavFix = {
|
||||||
...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse),
|
...computeDestinationPoint(arcEnd, remainingDistance, lastCourse),
|
||||||
name: leg.Distance.toString(),
|
name: leg.Distance.toString(),
|
||||||
isFlyOver: true,
|
isFlyOver: true,
|
||||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
line.push([targetFix.longitude, targetFix.latitude]);
|
line.push([targetFix.longitude, targetFix.latitude]);
|
||||||
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
// NOTE: No wind adjustments to be made, no clue how *that* would draw
|
||||||
|
export const TerminatorsVI = (
|
||||||
|
leg: VITerminalEntry,
|
||||||
|
nextLeg: TerminalEntry,
|
||||||
|
previousFix: NavFix,
|
||||||
|
lastCourse: number
|
||||||
|
): [NavFix?, LineSegment[]?] => {
|
||||||
|
const speed = computeSpeed(leg, previousFix);
|
||||||
|
const crsIntoEndpoint = leg.Course.toTrue(previousFix);
|
||||||
|
const [crsToIntercept, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix);
|
||||||
|
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||||
|
|
||||||
|
// Compute overfly arc
|
||||||
|
if (previousFix.isFlyOver && !lastCourse.equal(crsIntoEndpoint)) {
|
||||||
|
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||||
|
const updatedCrsToIntercept = getGreatCircleBearing(previousFix, nextFix);
|
||||||
|
|
||||||
|
// 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) && !updatedCrsToIntercept.equal(crsToIntercept)) {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
line.push([arcFix.longitude, arcFix.latitude]);
|
||||||
|
|
||||||
|
// Update previousFix
|
||||||
|
previousFix.latitude = arcFix.latitude;
|
||||||
|
previousFix.longitude = arcFix.longitude;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const interceptFix: NavFix = {
|
||||||
|
...computeIntersection(previousFix, leg.Course.toTrue(nextFix), nextFix, crsToIntercept)!,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||||
|
|
||||||
|
return [interceptFix, line];
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
import { handleTurnAtFix } from '../pathGenerators/handleTurnAtFix';
|
||||||
import { computeSpeed } from '../utils/computeSpeed';
|
import { computeSpeed } from '../utils/computeSpeed';
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ export const TerminatorsVM = (
|
|||||||
): [NavFix?, LineSegment[]?] => {
|
): [NavFix?, LineSegment[]?] => {
|
||||||
const speed = computeSpeed(leg, previousFix);
|
const speed = computeSpeed(leg, previousFix);
|
||||||
|
|
||||||
const endpoint = geolib.computeDestinationPoint(previousFix, (10).toMetre(), leg.Course.toTrue(previousFix));
|
const endpoint = computeDestinationPoint(previousFix, (10).toMetre(), leg.Course.toTrue(previousFix));
|
||||||
|
|
||||||
const line = handleTurnAtFix(
|
const line = handleTurnAtFix(
|
||||||
leg.Course.toTrue(previousFix),
|
leg.Course.toTrue(previousFix),
|
||||||
@@ -28,6 +28,8 @@ export const TerminatorsVR = (
|
|||||||
speed: speed,
|
speed: speed,
|
||||||
speedConstraint: leg.SpeedLimit,
|
speedConstraint: leg.SpeedLimit,
|
||||||
altitudeConstraint: leg.Alt,
|
altitudeConstraint: leg.Alt,
|
||||||
|
IsFAF: leg.IsFAF,
|
||||||
|
IsMAP: leg.IsMAP,
|
||||||
};
|
};
|
||||||
|
|
||||||
line.push([interceptFix.longitude, interceptFix.latitude]);
|
line.push([interceptFix.longitude, interceptFix.latitude]);
|
||||||
+7
-2
@@ -5,7 +5,12 @@
|
|||||||
* @param brng2 bearing from Point 2
|
* @param brng2 bearing from Point 2
|
||||||
* @returns Intersection point
|
* @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(brng1)) throw new TypeError(`invalid brng1 ${brng1}`);
|
||||||
if (isNaN(brng2)) throw new TypeError(`invalid brng2 ${brng2}`);
|
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
|
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) == 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);
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
+16
-6
@@ -1,4 +1,5 @@
|
|||||||
import * as geolib from 'geolib';
|
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||||
|
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param leg Leg to examine
|
* @param leg Leg to examine
|
||||||
@@ -18,9 +19,9 @@ export const getCourseAndFixForIntercepts = (leg: TerminalEntry, origin: NavFix)
|
|||||||
return [_leg.Course.toTrue(fix), fix];
|
return [_leg.Course.toTrue(fix), fix];
|
||||||
}
|
}
|
||||||
case 'TF': {
|
case 'TF': {
|
||||||
const _leg = leg as FMTerminalEntry;
|
const _leg = leg as TFTerminalEntry;
|
||||||
return [
|
return [
|
||||||
geolib.getGreatCircleBearing(origin, {
|
getGreatCircleBearing(origin, {
|
||||||
latitude: _leg.WptLat,
|
latitude: _leg.WptLat,
|
||||||
longitude: _leg.WptLon,
|
longitude: _leg.WptLon,
|
||||||
}),
|
}),
|
||||||
@@ -29,14 +30,23 @@ export const getCourseAndFixForIntercepts = (leg: TerminalEntry, origin: NavFix)
|
|||||||
}
|
}
|
||||||
case 'AF': {
|
case 'AF': {
|
||||||
const _leg = leg as AFTerminalEntry;
|
const _leg = leg as AFTerminalEntry;
|
||||||
const fix = { latitude: _leg.WptLat, longitude: _leg.WptLon };
|
const navAid = { latitude: _leg.NavLat, longitude: _leg.NavLon };
|
||||||
return [_leg.Course.reciprocalCourse().toTrue(fix), fix];
|
const fix = {
|
||||||
|
...computeDestinationPoint(navAid, _leg.NavDist.toMetre(), _leg.Course.toTrue(navAid)),
|
||||||
|
isIntersection: true,
|
||||||
|
};
|
||||||
|
return [_leg.Course.toTrue(fix), fix];
|
||||||
}
|
}
|
||||||
case 'DF': {
|
case 'DF': {
|
||||||
const _leg = leg as FMTerminalEntry;
|
const _leg = leg as DFTerminalEntry;
|
||||||
const fix = { latitude: _leg.WptLat, longitude: _leg.WptLon };
|
const fix = { latitude: _leg.WptLat, longitude: _leg.WptLon };
|
||||||
return [-1, fix];
|
return [-1, fix];
|
||||||
}
|
}
|
||||||
|
case 'RF': {
|
||||||
|
const _leg = leg as RFTerminalEntry;
|
||||||
|
const fix = { latitude: _leg.WptLat, longitude: _leg.WptLon };
|
||||||
|
return [_leg.Course.toTrue(fix), fix];
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return [-1, origin];
|
return [-1, origin];
|
||||||
}
|
}
|
||||||
+2
-2
@@ -26,9 +26,9 @@ export declare global {
|
|||||||
*/
|
*/
|
||||||
toMetre: () => number;
|
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 {
|
interface String {
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
export declare global {
|
||||||
|
type Chart = {
|
||||||
|
data: string;
|
||||||
|
index_number: string;
|
||||||
|
bounds: LatLngBoundsExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Procedure = { name: string; data: object };
|
||||||
|
}
|
||||||
@@ -75,6 +75,7 @@ export declare global {
|
|||||||
ICAO: string;
|
ICAO: string;
|
||||||
FullName: string;
|
FullName: string;
|
||||||
RwyID?: number;
|
RwyID?: number;
|
||||||
|
Proc: 1 | 2 | 3;
|
||||||
};
|
};
|
||||||
|
|
||||||
type NavFix = {
|
type NavFix = {
|
||||||
@@ -84,11 +85,13 @@ export declare global {
|
|||||||
speed?: number;
|
speed?: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
isFlyOver?: boolean;
|
isFlyOver?: boolean;
|
||||||
'marker-color'?: string;
|
|
||||||
altitudeConstraint?: string;
|
altitudeConstraint?: string;
|
||||||
speedConstraint?: number;
|
speedConstraint?: number;
|
||||||
|
IsFAF?: boolean;
|
||||||
|
IsMAP?: boolean;
|
||||||
// For map
|
// For map
|
||||||
isIntersection?: boolean;
|
isIntersection?: boolean;
|
||||||
|
isInitial?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type LineSegment = [number, number];
|
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