FlightGear next
LevelDXML.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include <algorithm>
6
7#include "LevelDXML.hxx"
8
9#include <simgear/structure/exception.hxx>
10#include <simgear/misc/sg_path.hxx>
11#include <simgear/misc/strutils.hxx>
12
13#include <Navaids/waypoint.hxx>
14#include <Airports/airport.hxx>
15#include <Navaids/route.hxx>
16
17using std::string;
18using std::vector;
19
20namespace flightgear
21{
22
23NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath):
24 _airport(aApt),
25 _path(aPath),
26 _sid(NULL),
27 _star(NULL),
28 _approach(NULL),
29 _transition(NULL),
30 _procedure(NULL)
31{
32}
33
37
41
42void NavdataVisitor::startElement(const char* name, const XMLAttributes &atts)
43{
44 _text.clear();
45 string tag(name);
46 if (tag == "Airport") {
47 string icao(atts.getValue("ICAOcode"));
48 if (_airport->ident() != icao) {
49 throw sg_format_exception("Airport and ICAO mismatch", icao, _path.utf8Str());
50 }
51 } else if (tag == "Sid") {
52 string ident(atts.getValue("Name"));
53 _sid = new SID(ident, _airport);
54 _procedure = _sid;
55 _waypoints.clear();
56 processRunways(_sid, atts);
57 } else if (tag == "Star") {
58 string ident(atts.getValue("Name"));
59 _star = new STAR(ident, _airport);
60 _procedure = _star;
61 _waypoints.clear();
62 processRunways(_star, atts);
63 } else if ((tag == "Sid_Waypoint") ||
64 (tag == "App_Waypoint") ||
65 (tag == "Star_Waypoint") ||
66 (tag == "AppTr_Waypoint") ||
67 (tag == "SidTr_Waypoint") ||
68 (tag == "RwyTr_Waypoint"))
69 {
70 // reset waypoint data
71 _speed = 0.0;
72 _altRestrict = RESTRICT_NONE;
73 _altitude = 0.0;
74 _overflightWaypt = false; // default to Fly-by
75 _courseFlag = false; // default to heading
76 } else if (tag == "Approach") {
77 _ident = atts.getValue("Name");
78 _waypoints.clear();
80
81 // if the name of the procedure starts with the correct name, set the procedure type
82 if (_ident.find("ILS") == 0) {
84 } else if (_ident.find("VOR") == 0 || _ident.find("VDM") == 0) {
86 } else if (_ident.find("NDB") == 0 || _ident.find("NDM") == 0) {
88 }
89
90 _approach = new Approach(_ident, ty);
91 _procedure = _approach;
92 } else if ((tag == "Sid_Transition") ||
93 (tag == "App_Transition") ||
94 (tag == "Star_Transition")) {
95 _transIdent = atts.getValue("Name");
96 _transition = new Transition(_transIdent, PROCEDURE_TRANSITION, _procedure);
97 _transWaypts.clear();
98 } else if (tag == "RunwayTransition") {
99 _transIdent = atts.getValue("Runway");
100 if (!_airport->hasRunwayWithIdent(_transIdent)) {
101 _transIdent.clear();
102 _transition = nullptr;
103 } else {
104 _transition = new Transition(_transIdent, PROCEDURE_RUNWAY_TRANSITION, _procedure);
105 _transWaypts.clear();
106 }
107 } else {
108 // nothing here, we warn on unrecognized in endElement
109 }
110}
111
112void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts)
113{
114 string v("All");
115 if (atts.hasAttribute("Runways")) {
116 v = atts.getValue("Runways");
117 }
118
119 if (v == "All") {
120 for (unsigned int r=0; r<_airport->numRunways(); ++r) {
121 aProc->addRunway(_airport->getRunwayByIndex(r));
122 }
123 return;
124 }
125
126 auto rwys = simgear::strutils::split_on_any_of(v, " ,");
127 for (auto rwy : rwys) {
128 if (!_airport->hasRunwayWithIdent(rwy)) {
129 const auto renamed = _airport->findAPTRunwayForNewName(rwy);
130 if (!renamed.empty()) {
131 rwy = renamed;
132 } else {
133 SG_LOG(SG_NAVAID, SG_DEV_WARN, "Procedure file " << _path << " references unknown airport runway:" << rwy);
134 continue;
135 }
136 }
137
138 aProc->addRunway(_airport->getRunwayByIdent(rwy));
139 }
140}
141
143{
144 string tag(name);
145 if ((tag == "Sid_Waypoint") ||
146 (tag == "App_Waypoint") ||
147 (tag == "Star_Waypoint"))
148 {
149 _waypoints.push_back(buildWaypoint(_procedure));
150 } else if ((tag == "AppTr_Waypoint") ||
151 (tag == "SidTr_Waypoint") ||
152 (tag == "RwyTr_Waypoint") ||
153 (tag == "StarTr_Waypoint"))
154 {
155 _transWaypts.push_back(buildWaypoint(_transition));
156 } else if (tag == "Sid_Transition") {
157 assert(_sid);
158 // SID waypoints are stored backwards, to share code with STARs
159 std::reverse(_transWaypts.begin(), _transWaypts.end());
160 _transition->setPrimary(_transWaypts);
161 _sid->addTransition(_transition);
162 } else if (tag == "Star_Transition") {
163 assert(_star);
164 _transition->setPrimary(_transWaypts);
165 _star->addTransition(_transition);
166 } else if (tag == "App_Transition") {
167 assert(_approach);
168 _transition->setPrimary(_transWaypts);
169 _approach->addTransition(_transition);
170 } else if (tag == "RunwayTransition") {
171 if (!_transition) {
172 // transition was skipped for some reason
173 return;
174 }
175
177 if (_sid) {
178 // SID waypoints are stored backwards, to share code with STARs
179 std::reverse(_transWaypts.begin(), _transWaypts.end());
180 ad = _sid;
181 } else {
182 ad = _star;
183 }
184
185 _transition->setPrimary(_transWaypts);
186 FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent);
187 ad->addRunwayTransition(rwy, _transition);
188 } else if (tag == "Approach") {
189 finishApproach();
190 } else if (tag == "Sid") {
191 finishSid();
192 } else if (tag == "Star") {
193 finishStar();
194 } else if (tag == "Longitude") {
195 _longitude = atof(_text.c_str());
196 } else if (tag == "Latitude") {
197 _latitude = atof(_text.c_str());
198 } else if (tag == "Name") {
199 _wayptName = _text;
200 } else if (tag == "Type") {
201 _wayptType = _text;
202 } else if (tag == "Speed") {
203 _speed = atoi(_text.c_str());
204 } else if (tag == "Altitude") {
205 _altitude = atof(_text.c_str());
206 } else if (tag == "AltitudeRestriction") {
207 _altRestrict = restrictionFromString(_text);
208 } else if (tag == "Hld_Rad_or_Inbd") {
209 if (_text == "Inbd") {
210 _holdRadial = -1.0;
211 }
212 } else if (tag == "Hld_Time_or_Dist") {
213 _holdDistance = (_text == "Dist");
214 } else if (tag == "Hld_Rad_value") {
215 _holdRadial = atof(_text.c_str());
216 } else if (tag == "Hld_Turn") {
217 _holdRighthanded = (_text == "Right");
218 } else if (tag == "Hld_td_value") {
219 _holdTD = atof(_text.c_str());
220 } else if (tag == "Hdg_Crs") {
221 _courseFlag = atoi(_text.c_str());
222 } else if (tag == "Hdg_Crs_value") {
223 _courseOrHeading = atof(_text.c_str());
224 } else if (tag == "DMEtoIntercept") {
225 _dmeDistance = atof(_text.c_str());
226 } else if (tag == "RadialtoIntercept") {
227 _radial = atof(_text.c_str());
228 } else if (tag == "Flytype") {
229 // values are 'Fly-by' and 'Fly-over'
230 _overflightWaypt = (_text == "Fly-over");
231 } else if ((tag == "AltitudeCons") ||
232 (tag == "BankLimit") ||
233 (tag == "Sp_Turn") ||
234 (tag == "Airport") ||
235 (tag == "ProceduresDB"))
236 {
237 // ignored but don't warn
238 } else {
239 SG_LOG(SG_IO, SG_INFO, "unrecognized Level-D XML element:" << tag);
240 }
241}
242
243Waypt* NavdataVisitor::buildWaypoint(RouteBase* owner)
244{
245 Waypt* wp = NULL;
246 if (_wayptType == "Normal") {
247 // new LatLonWaypoint
248 SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
249 wp = new BasicWaypt(pos, _wayptName, owner);
250 } else if (_wayptType == "Runway") {
251 string ident = _wayptName.substr(2);
252 if (!_airport->hasRunwayWithIdent(ident)) {
253 const auto renamed = _airport->findAPTRunwayForNewName(ident);
254 if (renamed.empty()) {
255 SG_LOG(SG_NAVAID, SG_DEV_WARN, "Missing runway " << ident << " reading " << _path);
256 SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
257 // fall back to a basic WP
258 wp = new BasicWaypt(pos, _wayptName, owner);
259 ident.clear();
260 } else {
261 ident = renamed;
262 }
263 }
264
265 if (!ident.empty()) {
266 FGRunwayRef rwy = _airport->getRunwayByIdent(ident);
267 wp = new RunwayWaypt(rwy, owner);
268 }
269 } else if (_wayptType == "Hold") {
270 SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
271 Hold* h = new Hold(pos, _wayptName, owner);
272 wp = h;
273 if (_holdRighthanded) {
274 h->setRightHanded();
275 } else {
276 h->setLeftHanded();
277 }
278
279 if (_holdDistance) {
280 h->setHoldDistance(_holdTD);
281 } else {
282 h->setHoldTime(_holdTD * 60.0);
283 }
284
285 if (_holdRadial >= 0.0) {
286 h->setHoldRadial(_holdRadial);
287 }
288 } else if (_wayptType == "Vectors") {
289 wp = new ATCVectors(owner, _airport);
290 } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) {
291 SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
292 wp = new RadialIntercept(owner, _wayptName, pos, _courseOrHeading, _radial);
293 } else if (_wayptType == "DmeIntc") {
294 SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
295 wp = new DMEIntercept(owner, _wayptName, pos, _courseOrHeading, _dmeDistance);
296 } else if (_wayptType == "ConstHdgtoAlt") {
297 wp = new HeadingToAltitude(owner, _wayptName, _courseOrHeading);
298 } else if (_wayptType == "PBD") {
299 SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2;
300 double az2;
301 SGGeodesy::direct(pos, _courseOrHeading, _dmeDistance, pos2, az2);
302 wp = new BasicWaypt(pos2, _wayptName, owner);
303 } else {
304 SG_LOG(SG_NAVAID, SG_ALERT, "implement waypoint type:" << _wayptType);
305 throw sg_format_exception("Unrecognized waypt type", _wayptType);
306 }
307
308 assert(wp);
309 if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) {
310 wp->setAltitude(_altitude,_altRestrict);
311 }
312
313 if (_speed > 0.0) {
314 wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW?
315 }
316
317 if (_overflightWaypt) {
319 }
320
321 return wp;
322}
323
324void NavdataVisitor::finishApproach()
325{
326 WayptVec::iterator it;
327 FGRunwayRef rwy;
328
329// find the runway node
330 for (it = _waypoints.begin(); it != _waypoints.end(); ++it) {
331 FGPositionedRef navid = (*it)->source();
332 if (!navid) {
333 continue;
334 }
335
336 if (navid->type() == FGPositioned::RUNWAY) {
337 rwy = (FGRunway*) navid.get();
338 break;
339 }
340 }
341
342 if (!rwy) {
343 SG_LOG(SG_NAVAID, SG_DEV_WARN, "Parsing:" << _path << " found approach without a runway:" << _ident);
344 delete _approach;
345 _approach = nullptr;
346 return;
347 }
348
349 WayptVec primary(_waypoints.begin(), it);
350 // erase all points up to and including the runway, to leave only the
351 // missed segments
352 _waypoints.erase(_waypoints.begin(), ++it);
353
354 _approach->setRunway(rwy);
355 _approach->setPrimaryAndMissed(primary, _waypoints);
356 _airport->addApproach(_approach);
357 _approach = NULL;
358}
359
360void NavdataVisitor::finishSid()
361{
362 // reverse order, because that's how we deal with commonality between
363 // STARs and SIDs. SID::route undoes this
364 std::reverse(_waypoints.begin(), _waypoints.end());
365 _sid->setCommon(_waypoints);
366 _airport->addSID(_sid);
367 _sid = NULL;
368}
369
370void NavdataVisitor::finishStar()
371{
372 _star->setCommon(_waypoints);
373 _airport->addSTAR(_star);
374 _star = NULL;
375}
376
377void NavdataVisitor::data (const char * s, int len)
378{
379 _text += string(s, len);
380}
381
382
383void NavdataVisitor::pi (const char * target, const char * data) {
384 //cout << "Processing instruction " << target << ' ' << data << endl;
385}
386
387void NavdataVisitor::warning (const char * message, int line, int column) {
388 SG_LOG(SG_NAVAID, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
389}
390
391void NavdataVisitor::error (const char * message, int line, int column) {
392 SG_LOG(SG_NAVAID, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
393}
394
395}
SGSharedPtr< FGRunway > FGRunwayRef
unsigned int numRunways() const
Definition airport.cxx:102
std::string findAPTRunwayForNewName(const std::string &newIdent) const
Definition airport.cxx:783
FGRunwayRef getRunwayByIndex(unsigned int aIndex) const
Definition airport.cxx:116
bool hasRunwayWithIdent(const std::string &aIdent) const
Definition airport.cxx:162
Describe an approach procedure, including the missed approach segment.
void addRunway(FGRunwayRef aRwy)
NavdataVisitor(FGAirport *aApt, const SGPath &aPath)
Definition LevelDXML.cxx:23
virtual void warning(const char *message, int line, int column)
virtual void startElement(const char *name, const XMLAttributes &atts)
Definition LevelDXML.cxx:42
virtual void pi(const char *target, const char *data)
virtual void data(const char *s, int len)
virtual void error(const char *message, int line, int column)
virtual void endElement(const char *name)
Encapsulate a transition segment.
Definition procedure.hxx:70
Abstract base class for waypoints (and things that are treated similarly by navigation systems).
Definition route.hxx:105
void setAltitude(double aAlt, RouteRestriction aRestrict, RouteUnits aUnits=DEFAULT_UNITS)
Definition route.cxx:248
void setFlag(WayptFlag aFlag, bool aV=true)
Definition route.cxx:209
void setSpeed(double aSpeed, RouteRestriction aRestrict, RouteUnits aUnits=DEFAULT_UNITS)
Definition route.cxx:264
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
RouteRestriction restrictionFromString(const std::string &aStr)
Definition route.cxx:350
SGSharedPtr< FGPositioned > FGPositionedRef
Definition airways.cxx:49
const char * name
@ WPT_OVERFLIGHT
must overfly the point directly
Definition route.hxx:44
std::vector< WayptRef > WayptVec
@ RESTRICT_NONE
Definition route.hxx:71
@ RESTRICT_AT
Definition route.hxx:72
@ PROCEDURE_TRANSITION
Definition procedure.hxx:44
@ PROCEDURE_APPROACH_NDB
Definition procedure.hxx:40
@ PROCEDURE_APPROACH_VOR
Definition procedure.hxx:39
@ PROCEDURE_RUNWAY_TRANSITION
Definition procedure.hxx:45
@ PROCEDURE_APPROACH_RNAV
Definition procedure.hxx:41
@ PROCEDURE_APPROACH_ILS
Definition procedure.hxx:38
static double atof(const string &str)
Definition options.cxx:107
static int atoi(const string &str)
Definition options.cxx:113