forked from mmp/vice
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathairport.go
181 lines (150 loc) · 4.74 KB
/
airport.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// airport.go
// Copyright(c) 2022 Matt Pharr, licensed under the GNU Public License, Version 3.
// SPDX: GPL-3.0-only
package main
import (
"fmt"
"strings"
)
type Airport struct {
Elevation int `json:"elevation"`
Location Point2LL `json:"location"`
TowerListIndex int `json:"tower_list"`
Approaches map[string]Approach `json:"approaches,omitempty"`
Departures []Departure `json:"departures,omitempty"`
ExitCategories map[string]string `json:"exit_categories"`
// runway -> (exit -> route)
DepartureRoutes map[string]map[string]ExitRoute `json:"departure_routes"`
}
func (ap *Airport) PostDeserialize(sg *ScenarioGroup, e *ErrorLogger) {
for name, ap := range ap.Approaches {
e.Push("Approach " + name)
for i := range ap.Waypoints {
n := len(ap.Waypoints[i])
ap.Waypoints[i][n-1].Commands = append(ap.Waypoints[i][n-1].Commands, WaypointCommandDelete)
sg.InitializeWaypointLocations(ap.Waypoints[i], e)
}
e.Pop()
}
// Departure routes are specified in the JSON as comma-separated lists
// of exits. We'll split those out into individual entries in the
// Airport's DepartureRoutes, one per exit, for convenience of future code.
splitDepartureRoutes := make(map[string]map[string]ExitRoute)
for rwy, rwyRoutes := range ap.DepartureRoutes {
e.Push("Departure runway " + rwy)
seenExits := make(map[string]interface{})
splitDepartureRoutes[rwy] = make(map[string]ExitRoute)
for exitList, route := range rwyRoutes {
e.Push("Exit " + exitList)
sg.InitializeWaypointLocations(route.Waypoints, e)
for _, exit := range strings.Split(exitList, ",") {
if _, ok := seenExits[exit]; ok {
e.ErrorString("exit repeatedly specified in routes")
}
seenExits[exit] = nil
splitDepartureRoutes[rwy][exit] = route
}
e.Pop()
}
e.Pop()
}
ap.DepartureRoutes = splitDepartureRoutes
for i, dep := range ap.Departures {
e.Push("Departure exit " + dep.Exit)
e.Push("Destination " + dep.Destination)
if _, ok := sg.Scratchpads[dep.Exit]; !ok {
e.ErrorString("exit not in scenario group \"scratchpads\"")
}
// Make sure that all runways have a route to the exit
for rwy, routes := range ap.DepartureRoutes {
e.Push("Runway " + rwy)
if _, ok := routes[dep.Exit]; !ok {
e.ErrorString("exit \"%s\" not found in runway's \"departure_routes\"", dep.Exit)
}
e.Pop()
}
sawExit := false
for _, fix := range strings.Fields(dep.Route) {
sawExit = sawExit || fix == dep.Exit
wp := []Waypoint{Waypoint{Fix: fix}}
// Best effort only to find waypoint locations; this will fail
// for airways, international ones not in the FAA database,
// latlongs in the flight plan, etc.
if fix == dep.Exit {
sg.InitializeWaypointLocations(wp, e)
} else {
// nil here so errors aren't logged if it's not the actual exit.
sg.InitializeWaypointLocations(wp, nil)
}
ap.Departures[i].routeWaypoints = append(ap.Departures[i].routeWaypoints, wp[0])
}
if !sawExit {
e.ErrorString("exit not found in departure route")
}
for _, al := range dep.Airlines {
database.CheckAirline(al.ICAO, al.Fleet, e)
}
e.Pop()
e.Pop()
}
}
type ExitRoute struct {
InitialRoute string `json:"route"`
ClearedAltitude int `json:"cleared_altitude"`
Waypoints WaypointArray `json:"waypoints"`
}
type Departure struct {
Exit string `json:"exit"`
Destination string `json:"destination"`
Altitude int `json:"altitude,omitempty"`
Route string `json:"route"`
routeWaypoints []Waypoint
Airlines []DepartureAirline `json:"airlines"`
}
type DepartureAirline struct {
ICAO string `json:"icao"`
Fleet string `json:"fleet,omitempty"`
}
type ApproachType int
const (
ILSApproach = iota
RNAVApproach
)
func (at ApproachType) MarshalJSON() ([]byte, error) {
switch at {
case ILSApproach:
return []byte("\"ILS\""), nil
case RNAVApproach:
return []byte("\"RNAV\""), nil
default:
return nil, fmt.Errorf("unhandled approach type in MarshalJSON()")
}
}
func (at *ApproachType) UnmarshalJSON(b []byte) error {
switch string(b) {
case "\"ILS\"":
*at = ILSApproach
return nil
case "\"RNAV\"":
*at = RNAVApproach
return nil
default:
return fmt.Errorf("%s: unknown approach_type", string(b))
}
}
type Approach struct {
FullName string `json:"full_name"`
Type ApproachType `json:"type"`
Waypoints []WaypointArray `json:"waypoints"`
}
func (ap *Approach) Line() [2]Point2LL {
// assume we have at least one set of waypoints and that it has >= 2 waypoints!
wp := ap.Waypoints[0]
// use the last two waypoints
n := len(wp)
return [2]Point2LL{wp[n-2].Location, wp[n-1].Location}
}
func (ap *Approach) Heading() int {
p := ap.Line()
return int(headingp2ll(p[0], p[1], scenarioGroup.MagneticVariation) + 0.5)
}