]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/LevelDXML.cxx
Level-D procedures: parse Flytype element
[flightgear.git] / src / Navaids / LevelDXML.cxx
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include "LevelDXML.hxx"
6
7 #include <boost/algorithm/string.hpp>
8
9 #include <simgear/structure/exception.hxx>
10 #include <simgear/misc/sg_path.hxx>
11
12 #include <Navaids/waypoint.hxx>
13 #include <Airports/airport.hxx>
14
15 using std::string;
16 using std::vector;
17
18 namespace flightgear
19 {
20
21 NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath):
22   _airport(aApt),
23   _path(aPath),
24   _sid(NULL),
25   _star(NULL),
26   _approach(NULL),
27   _transition(NULL),
28   _procedure(NULL)
29 {
30 }
31
32 void NavdataVisitor::startXML()
33 {
34 }
35
36 void NavdataVisitor::endXML()
37 {
38 }
39
40 void NavdataVisitor::startElement(const char* name, const XMLAttributes &atts)
41 {
42   _text.clear();
43   string tag(name);
44   if (tag == "Airport") {
45     string icao(atts.getValue("ICAOcode"));
46     if (_airport->ident() != icao) {
47       throw sg_format_exception("Airport and ICAO mismatch", icao, _path.str());
48     }
49   } else if (tag == "Sid") {
50     string ident(atts.getValue("Name"));
51     _sid = new SID(ident, _airport);
52     _procedure = _sid;
53     _waypoints.clear();
54     processRunways(_sid, atts);
55   } else if (tag == "Star") {
56     string ident(atts.getValue("Name"));
57     _star = new STAR(ident, _airport);
58     _procedure = _star;
59     _waypoints.clear();
60     processRunways(_star, atts);
61   } else if ((tag == "Sid_Waypoint") ||
62       (tag == "App_Waypoint") ||
63       (tag == "Star_Waypoint") ||
64       (tag == "AppTr_Waypoint") ||
65       (tag == "SidTr_Waypoint") ||
66       (tag == "RwyTr_Waypoint"))
67   {
68     // reset waypoint data
69     _speed = 0.0;
70     _altRestrict = RESTRICT_NONE;
71     _altitude = 0.0;
72     _overflightWaypt = false; // default to Fly-by
73   } else if (tag == "Approach") {
74     _ident = atts.getValue("Name");
75     _waypoints.clear();
76     ProcedureType ty = PROCEDURE_APPROACH_RNAV;
77     _approach = new Approach(_ident, ty);
78     _procedure = _approach;
79   } else if ((tag == "Sid_Transition") || 
80              (tag == "App_Transition") ||
81              (tag == "Star_Transition")) {
82     _transIdent = atts.getValue("Name");
83     _transition = new Transition(_transIdent, PROCEDURE_TRANSITION, _procedure);
84     _transWaypts.clear();
85   } else if (tag == "RunwayTransition") {
86     _transIdent = atts.getValue("Runway");
87     _transition = new Transition(_transIdent, PROCEDURE_RUNWAY_TRANSITION, _procedure);
88     _transWaypts.clear();
89   } else {
90     // nothing here, we warn on unrecognized in endElement
91   }
92 }
93
94 void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts)
95 {
96   string v("All");
97   if (atts.hasAttribute("Runways")) {
98     v = atts.getValue("Runways");
99   }
100   
101   if (v == "All") {
102     for (unsigned int r=0; r<_airport->numRunways(); ++r) {
103       aProc->addRunway(_airport->getRunwayByIndex(r));
104     }
105     return;
106   }
107   
108   vector<string> rwys;
109   boost::split(rwys, v, boost::is_any_of(" ,"));
110   for (unsigned int r=0; r<rwys.size(); ++r) {
111     FGRunway* rwy = _airport->getRunwayByIdent(rwys[r]);
112     aProc->addRunway(rwy);
113   }
114 }
115
116 void NavdataVisitor::endElement(const char* name)
117 {
118   string tag(name);
119   if ((tag == "Sid_Waypoint") ||
120       (tag == "App_Waypoint") ||
121       (tag == "Star_Waypoint"))
122   {
123     _waypoints.push_back(buildWaypoint(_procedure));
124   } else if ((tag == "AppTr_Waypoint") || 
125              (tag == "SidTr_Waypoint") ||
126              (tag == "RwyTr_Waypoint") ||
127              (tag == "StarTr_Waypoint")) 
128   {
129     _transWaypts.push_back(buildWaypoint(_transition));
130   } else if (tag == "Sid_Transition") {
131     assert(_sid);
132     // SID waypoints are stored backwards, to share code with STARs
133     std::reverse(_transWaypts.begin(), _transWaypts.end());
134     _transition->setPrimary(_transWaypts);
135     _sid->addTransition(_transition);
136   } else if (tag == "Star_Transition") {
137     assert(_star);
138     _transition->setPrimary(_transWaypts);
139     _star->addTransition(_transition);
140   } else if (tag == "App_Transition") {
141     assert(_approach);
142     _transition->setPrimary(_transWaypts);
143     _approach->addTransition(_transition);
144   } else if (tag == "RunwayTransition") {
145     ArrivalDeparture* ad;
146     if (_sid) {
147       // SID waypoints are stored backwards, to share code with STARs
148       std::reverse(_transWaypts.begin(), _transWaypts.end());
149       ad = _sid;
150     } else {
151       ad = _star;
152     }
153
154     _transition->setPrimary(_transWaypts);
155     FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent);
156     ad->addRunwayTransition(rwy, _transition);
157   } else if (tag == "Approach") {
158     finishApproach();
159   } else if (tag == "Sid") {
160     finishSid();
161   } else if (tag == "Star") {
162     finishStar();  
163   } else if (tag == "Longitude") {
164     _longitude = atof(_text.c_str());
165   } else if (tag == "Latitude") {
166     _latitude = atof(_text.c_str());
167   } else if (tag == "Name") {
168     _wayptName = _text;
169   } else if (tag == "Type") {
170     _wayptType = _text;
171   } else if (tag == "Speed") {
172     _speed = atoi(_text.c_str());
173   } else if (tag == "Altitude") {
174     _altitude = atof(_text.c_str());
175   } else if (tag == "AltitudeRestriction") {
176     if (_text == "at") {
177       _altRestrict = RESTRICT_AT;
178     } else if (_text == "above") {
179       _altRestrict = RESTRICT_ABOVE;
180     } else if (_text == "below") {
181       _altRestrict = RESTRICT_BELOW;
182     } else {
183       throw sg_format_exception("Unrecognized altitude restriction", _text);
184     }
185   } else if (tag == "Hld_Rad_or_Inbd") {
186     if (_text == "Inbd") {
187       _holdRadial = -1.0;
188     }
189   } else if (tag == "Hld_Time_or_Dist") {
190     _holdDistance = (_text == "Dist");
191   } else if (tag == "Hld_Rad_value") {
192     _holdRadial = atof(_text.c_str());
193   } else if (tag == "Hld_Turn") {
194     _holdRighthanded = (_text == "Right");
195   } else if (tag == "Hld_td_value") {
196     _holdTD = atof(_text.c_str());
197   } else if (tag == "Hdg_Crs_value") {
198     _course = atof(_text.c_str());
199   } else if (tag == "DMEtoIntercept") {
200     _dmeDistance = atof(_text.c_str());
201   } else if (tag == "RadialtoIntercept") {
202     _radial = atof(_text.c_str());
203   } else if (tag == "Flytype") {
204     // values are 'Fly-by' and 'Fly-over'
205     _overflightWaypt = (_text == "Fly-over");
206   } else {
207     SG_LOG(SG_IO, SG_INFO, "unrecognized Level-D XML element:" << tag);
208   }
209 }
210
211 Waypt* NavdataVisitor::buildWaypoint(RouteBase* owner)
212 {
213   Waypt* wp = NULL;
214   if (_wayptType == "Normal") {
215     // new LatLonWaypoint
216     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
217     wp = new BasicWaypt(pos, _wayptName, owner);
218   } else if (_wayptType == "Runway") {
219     string ident = _wayptName.substr(2);
220     FGRunwayRef rwy = _airport->getRunwayByIdent(ident);
221     wp = new RunwayWaypt(rwy, owner);
222   } else if (_wayptType == "Hold") {
223     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
224     Hold* h = new Hold(pos, _wayptName, owner);
225     wp = h;
226     if (_holdRighthanded) {
227       h->setRightHanded();
228     } else {
229       h->setLeftHanded();
230     }
231     
232     if (_holdDistance) {
233       h->setHoldDistance(_holdTD);
234     } else {
235       h->setHoldTime(_holdTD * 60.0);
236     }
237     
238     if (_holdRadial >= 0.0) {
239       h->setHoldRadial(_holdRadial);
240     }
241   } else if (_wayptType == "Vectors") {
242     wp = new ATCVectors(owner, _airport);
243   } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) {
244     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
245     wp = new RadialIntercept(owner, _wayptName, pos, _course, _radial);
246   } else if (_wayptType == "DmeIntc") {
247     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
248     wp = new DMEIntercept(owner, _wayptName, pos, _course, _dmeDistance);
249   } else if (_wayptType == "ConstHdgtoAlt") {
250     wp = new HeadingToAltitude(owner, _wayptName, _course);
251   } else if (_wayptType == "PBD") {
252     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2;
253     double az2;
254     SGGeodesy::direct(pos, _course, _dmeDistance, pos2, az2);
255     wp = new BasicWaypt(pos2, _wayptName, owner);
256   } else {
257     SG_LOG(SG_NAVAID, SG_ALERT, "implement waypoint type:" << _wayptType);
258     throw sg_format_exception("Unrecognized waypt type", _wayptType);
259   }
260   
261   assert(wp);
262   if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) {
263     wp->setAltitude(_altitude,_altRestrict);
264   }
265   
266   if (_speed > 0.0) {
267     wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW?
268   }
269   
270   if (_overflightWaypt) {
271     wp->setFlag(WPT_OVERFLIGHT);
272   }
273   
274   return wp;
275 }
276
277 void NavdataVisitor::finishApproach()
278 {
279   WayptVec::iterator it;
280   FGRunwayRef rwy;
281   
282 // find the runway node
283   for (it = _waypoints.begin(); it != _waypoints.end(); ++it) {
284     FGPositionedRef navid = (*it)->source();
285     if (!navid) {
286       continue;
287     }
288     
289     if (navid->type() == FGPositioned::RUNWAY) {
290       rwy = (FGRunway*) navid.get();
291       break;
292     }
293   }
294   
295   if (!rwy) {
296     throw sg_format_exception("Malformed approach, no runway waypt", _ident);
297   }
298   
299   WayptVec primary(_waypoints.begin(), it);
300   // erase all points up to and including the runway, to leave only the
301   // missed segments
302   _waypoints.erase(_waypoints.begin(), ++it);
303   
304   _approach->setRunway(rwy);
305   _approach->setPrimaryAndMissed(primary, _waypoints);
306   _airport->addApproach(_approach);
307   _approach = NULL;
308 }
309
310 void NavdataVisitor::finishSid()
311 {
312   // reverse order, because that's how we deal with commonality between
313   // STARs and SIDs. SID::route undoes  this
314   std::reverse(_waypoints.begin(), _waypoints.end());
315   _sid->setCommon(_waypoints);
316   _airport->addSID(_sid);
317   _sid = NULL;
318 }
319
320 void NavdataVisitor::finishStar()
321 {
322   _star->setCommon(_waypoints);
323   _airport->addSTAR(_star);
324   _star = NULL;
325 }
326
327 void NavdataVisitor::data (const char * s, int len)
328 {
329   _text += string(s, len);
330 }
331
332
333 void NavdataVisitor::pi (const char * target, const char * data) {
334   //cout << "Processing instruction " << target << ' ' << data << endl;
335 }
336
337 void NavdataVisitor::warning (const char * message, int line, int column) {
338   SG_LOG(SG_NAVAID, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
339 }
340
341 void NavdataVisitor::error (const char * message, int line, int column) {
342   SG_LOG(SG_NAVAID, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
343 }
344
345 }