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