]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/route.cxx
Support PBD waypoints in procedures files.
[flightgear.git] / src / Navaids / route.cxx
1 // route.cxx - classes supporting waypoints and route structures
2
3 // Written by James Turner, started 2009.
4 //
5 // Copyright (C) 2009  Curtis L. Olson
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "route.hxx"
26
27 // std
28 #include <map>
29 #include <fstream>
30
31 // Boost
32 #include <boost/algorithm/string/case_conv.hpp>
33 #include <boost/algorithm/string.hpp>
34
35 // SimGear
36 #include <simgear/structure/exception.hxx>
37 #include <simgear/xml/easyxml.hxx>
38 #include <simgear/misc/sg_path.hxx>
39 #include <simgear/magvar/magvar.hxx>
40 #include <simgear/timing/sg_time.hxx>
41
42 // FlightGear
43 #include <Main/globals.hxx>
44 #include <Navaids/procedure.hxx>
45 #include <Navaids/waypoint.hxx>
46 #include <Airports/simple.hxx>
47
48 using std::string;
49 using std::vector;
50 using std::endl;
51 using std::fstream;
52
53 namespace flightgear {
54
55 const double NO_MAG_VAR = -1000.0; // an impossible mag-var value
56
57 Waypt::Waypt(Route* aOwner) :
58   _altitudeFt(0.0),
59   _speed(0.0),
60   _altRestrict(RESTRICT_NONE),
61   _speedRestrict(RESTRICT_NONE),
62   _owner(aOwner),
63   _flags(0),
64   _magVarDeg(NO_MAG_VAR)
65 {
66 }
67
68 Waypt::~Waypt()
69 {
70 }
71   
72 std::string Waypt::ident() const
73 {
74   return "";
75 }
76   
77 bool Waypt::flag(WayptFlag aFlag) const
78 {
79   return ((_flags & aFlag) != 0);
80 }
81         
82 void Waypt::setFlag(WayptFlag aFlag, bool aV)
83 {
84   _flags = (_flags & ~aFlag);
85   if (aV) _flags |= aFlag;
86 }
87
88 bool Waypt::matches(Waypt* aOther) const
89 {
90   assert(aOther);
91   if (ident() != aOther->ident()) { // cheap check first
92     return false;
93   }
94   
95   return matches(aOther->position());
96 }
97
98
99 bool Waypt::matches(const SGGeod& aPos) const
100 {
101   double d = SGGeodesy::distanceM(position(), aPos);
102   return (d < 100.0); // 100 metres seems plenty
103 }
104
105 void Waypt::setAltitude(double aAlt, RouteRestriction aRestrict)
106 {
107   _altitudeFt = aAlt;
108   _altRestrict = aRestrict;
109 }
110
111 void Waypt::setSpeed(double aSpeed, RouteRestriction aRestrict)
112 {
113   _speed = aSpeed;
114   _speedRestrict = aRestrict;
115 }
116
117 double Waypt::speedKts() const
118 {
119   assert(_speedRestrict != SPEED_RESTRICT_MACH);
120   return speed();
121 }
122   
123 double Waypt::speedMach() const
124 {
125   assert(_speedRestrict == SPEED_RESTRICT_MACH);
126   return speed();
127 }
128   
129 std::pair<double, double>
130 Waypt::courseAndDistanceFrom(const SGGeod& aPos) const
131 {
132   if (flag(WPT_DYNAMIC)) {
133     return std::make_pair(0.0, 0.0);
134   }
135   
136   double course, az2, distance;
137   SGGeodesy::inverse(aPos, position(), course, az2, distance);
138   return std::make_pair(course, distance);
139 }
140
141 double Waypt::magvarDeg() const
142 {
143   if (_magVarDeg == NO_MAG_VAR) {
144     // derived classes with a default pos must override this method
145     assert(!(position() == SGGeod()));
146     
147     double jd = globals->get_time_params()->getJD();
148     _magVarDeg = sgGetMagVar(position(), jd) * SG_RADIANS_TO_DEGREES;
149   }
150   
151   return _magVarDeg;
152 }
153   
154 double Waypt::headingRadialDeg() const
155 {
156   return 0.0;
157 }
158   
159 ///////////////////////////////////////////////////////////////////////////
160 // persistence
161
162 static RouteRestriction restrictionFromString(const char* aStr)
163 {
164   std::string l = boost::to_lower_copy(std::string(aStr));
165
166   if (l == "at") return RESTRICT_AT;
167   if (l == "above") return RESTRICT_ABOVE;
168   if (l == "below") return RESTRICT_BELOW;
169   if (l == "none") return RESTRICT_NONE;
170   if (l == "mach") return SPEED_RESTRICT_MACH;
171   
172   if (l.empty()) return RESTRICT_NONE;
173   throw sg_io_exception("unknown restriction specification:" + l, 
174     "Route restrictFromString");
175 }
176
177 static const char* restrictionToString(RouteRestriction aRestrict)
178 {
179   switch (aRestrict) {
180   case RESTRICT_AT: return "at";
181   case RESTRICT_BELOW: return "below";
182   case RESTRICT_ABOVE: return "above";
183   case RESTRICT_NONE: return "none";
184   case SPEED_RESTRICT_MACH: return "mach";
185   
186   default:
187     throw sg_exception("invalid route restriction",
188       "Route restrictToString");
189   }
190 }
191
192 Waypt* Waypt::createInstance(Route* aOwner, const std::string& aTypeName)
193 {
194   Waypt* r = NULL;
195   if (aTypeName == "basic") {
196     r = new BasicWaypt(aOwner);
197   } else if (aTypeName == "navaid") {
198     r = new NavaidWaypoint(aOwner);
199   } else if (aTypeName == "offset-navaid") {
200     r = new OffsetNavaidWaypoint(aOwner);
201   } else if (aTypeName == "hold") {
202     r = new Hold(aOwner);
203   } else if (aTypeName == "runway") {
204     r = new RunwayWaypt(aOwner);
205   } else if (aTypeName == "hdgToAlt") {
206     r = new HeadingToAltitude(aOwner);
207   } else if (aTypeName == "dmeIntercept") {
208     r = new DMEIntercept(aOwner);
209   } else if (aTypeName == "radialIntercept") {
210     r = new RadialIntercept(aOwner);
211   } else if (aTypeName == "vectors") {
212     r = new ATCVectors(aOwner);
213   } 
214
215   if (!r || (r->type() != aTypeName)) {
216     throw sg_exception("broken factory method for type:" + aTypeName,
217       "Waypt::createInstance");
218   }
219   
220   return r;
221 }
222
223 WayptRef Waypt::createFromProperties(Route* aOwner, SGPropertyNode_ptr aProp)
224 {
225   if (!aProp->hasChild("type")) {
226     throw sg_io_exception("bad props node, no type provided", 
227       "Waypt::createFromProperties");
228   }
229   
230   WayptRef nd(createInstance(aOwner, aProp->getStringValue("type")));
231   nd->initFromProperties(aProp);
232   return nd;
233 }
234   
235 void Waypt::saveAsNode(SGPropertyNode* n) const
236 {
237   n->setStringValue("type", type());
238   writeToProperties(n);
239 }
240
241 void Waypt::initFromProperties(SGPropertyNode_ptr aProp)
242 {
243   if (aProp->hasChild("generated")) {
244     setFlag(WPT_GENERATED, aProp->getBoolValue("generated")); 
245   }
246   
247   if (aProp->hasChild("overflight")) {
248     setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight")); 
249   }
250   
251   if (aProp->hasChild("arrival")) {
252     setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival")); 
253   }
254   
255   if (aProp->hasChild("departure")) {
256     setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure")); 
257   }
258   
259   if (aProp->hasChild("miss")) {
260     setFlag(WPT_MISS, aProp->getBoolValue("miss")); 
261   }
262   
263   if (aProp->hasChild("alt-restrict")) {
264     _altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
265     _altitudeFt = aProp->getDoubleValue("altitude-ft");
266   }
267   
268   if (aProp->hasChild("speed-restrict")) {
269     _speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict"));
270     _speed = aProp->getDoubleValue("speed");
271   }
272   
273   
274 }
275
276 void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
277 {
278   if (flag(WPT_OVERFLIGHT)) {
279     aProp->setBoolValue("overflight", true);
280   }
281
282   if (flag(WPT_DEPARTURE)) {
283     aProp->setBoolValue("departure", true);
284   }
285   
286   if (flag(WPT_ARRIVAL)) {
287     aProp->setBoolValue("arrival", true);
288   }
289   
290   if (flag(WPT_MISS)) {
291     aProp->setBoolValue("miss", true);
292   }
293   
294   if (flag(WPT_GENERATED)) {
295     aProp->setBoolValue("generated", true);
296   }
297   
298   if (_altRestrict != RESTRICT_NONE) {
299     aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
300     aProp->setDoubleValue("altitude-ft", _altitudeFt);
301   }
302   
303   if (_speedRestrict != RESTRICT_NONE) {
304     aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
305     aProp->setDoubleValue("speed", _speed);
306   }
307 }
308
309 void Route::dumpRouteToFile(const WayptVec& aRoute, const std::string& aName)
310 {
311   SGPath p = "/Users/jmt/Desktop/" + aName + ".kml";
312   std::fstream f;
313   f.open(p.str().c_str(), fstream::out | fstream::app);
314   if (!f.is_open()) {
315     SG_LOG(SG_GENERAL, SG_WARN, "unable to open:" << p.str());
316     return;
317   }
318   
319 // pre-amble
320   f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
321       "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
322     "<Document>\n";
323
324   dumpRouteToLineString(aName, aRoute, f);
325   
326 // post-amble
327   f << "</Document>\n" 
328     "</kml>" << endl;
329   f.close();
330 }
331
332 void Route::dumpRouteToLineString(const std::string& aIdent,
333   const WayptVec& aRoute, std::ostream& aStream)
334 {
335   // preamble
336   aStream << "<Placemark>\n";
337   aStream << "<name>" << aIdent << "</name>\n";
338   aStream << "<LineString>\n";
339   aStream << "<tessellate>1</tessellate>\n";
340   aStream << "<coordinates>\n";
341   
342   // waypoints
343   for (unsigned int i=0; i<aRoute.size(); ++i) {
344     SGGeod pos = aRoute[i]->position();
345     aStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
346   }
347   
348   // postable
349   aStream << "</coordinates>\n"
350     "</LineString>\n"
351     "</Placemark>\n" << endl;
352 }
353
354 ///////////////////////////////////////////////////////////////////////////
355
356 class NavdataVisitor : public XMLVisitor {
357 public:
358   NavdataVisitor(FGAirport* aApt, const SGPath& aPath);
359
360 protected:
361   virtual void startXML (); 
362   virtual void endXML   ();
363   virtual void startElement (const char * name, const XMLAttributes &atts);
364   virtual void endElement (const char * name);
365   virtual void data (const char * s, int len);
366   virtual void pi (const char * target, const char * data);
367   virtual void warning (const char * message, int line, int column);
368   virtual void error (const char * message, int line, int column);
369
370 private:
371   Waypt* buildWaypoint(Route* owner);
372   void processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts);
373  
374   void finishApproach();
375   void finishSid();
376   void finishStar();
377   
378   FGAirport* _airport;
379   SGPath _path;
380   string _text; ///< last element text value
381   
382   SID* _sid;
383   STAR* _star;
384   Approach* _approach;
385   Transition* _transition;
386   Procedure* _procedure;
387   
388   WayptVec _waypoints; ///< waypoint list for current approach/sid/star
389   WayptVec _transWaypts; ///< waypoint list for current transition
390   
391   string _wayptName;
392   string _wayptType;
393   string _ident; // id of segment under construction
394   string _transIdent;
395   double _longitude, _latitude, _altitude, _speed;
396   RouteRestriction _altRestrict;
397   
398   double _holdRadial; // inbound hold radial, or -1 if radial is 'inbound'
399   double _holdTD; ///< hold time (seconds) or distance (nm), based on flag below
400   bool _holdRighthanded;
401   bool _holdDistance; // true, TD is distance in nm; false, TD is time in seconds
402   
403   double _course, _radial, _dmeDistance;
404 };
405
406 void Route::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt)
407 {
408   assert(aApt);
409   try {
410     NavdataVisitor visitor(aApt, aPath);
411     readXML(aPath.str(), visitor);
412   } catch (sg_io_exception& ex) {
413     SG_LOG(SG_GENERAL, SG_WARN, "failure parsing procedures: " << aPath.str() <<
414       "\n\t" << ex.getMessage() << "\n\tat:" << ex.getLocation().asString());
415   } catch (sg_exception& ex) {
416     SG_LOG(SG_GENERAL, SG_WARN, "failure parsing procedures: " << aPath.str() <<
417       "\n\t" << ex.getMessage());
418   }
419 }
420
421 NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath):
422   _airport(aApt),
423   _path(aPath),
424   _sid(NULL),
425   _star(NULL),
426   _approach(NULL),
427   _transition(NULL),
428   _procedure(NULL)
429 {
430 }
431
432 void NavdataVisitor::startXML()
433 {
434 }
435
436 void NavdataVisitor::endXML()
437 {
438 }
439
440 void NavdataVisitor::startElement(const char* name, const XMLAttributes &atts)
441 {
442   _text.clear();
443   string tag(name);
444   if (tag == "Airport") {
445     string icao(atts.getValue("ICAOcode"));
446     if (_airport->ident() != icao) {
447       throw sg_format_exception("Airport and ICAO mismatch", icao, _path.str());
448     }
449   } else if (tag == "Sid") {
450     string ident(atts.getValue("Name"));
451     _sid = new SID(ident);
452     _procedure = _sid;
453     _waypoints.clear();
454     processRunways(_sid, atts);
455   } else if (tag == "Star") {
456     string ident(atts.getValue("Name"));
457     _star = new STAR(ident);
458     _procedure = _star;
459     _waypoints.clear();
460     processRunways(_star, atts);
461   } else if ((tag == "Sid_Waypoint") ||
462       (tag == "App_Waypoint") ||
463       (tag == "Star_Waypoint") ||
464       (tag == "AppTr_Waypoint") ||
465       (tag == "SidTr_Waypoint") ||
466       (tag == "RwyTr_Waypoint"))
467   {
468     // reset waypoint data
469     _speed = 0.0;
470     _altRestrict = RESTRICT_NONE;
471     _altitude = 0.0;
472   } else if (tag == "Approach") {
473     _ident = atts.getValue("Name");
474     _waypoints.clear();
475     _approach = new Approach(_ident);
476     _procedure = _approach;
477   } else if ((tag == "Sid_Transition") || 
478              (tag == "App_Transition") ||
479              (tag == "Star_Transition")) {
480     _transIdent = atts.getValue("Name");
481     _transition = new Transition(_transIdent, _procedure);
482     _transWaypts.clear();
483   } else if (tag == "RunwayTransition") {
484     _transIdent = atts.getValue("Runway");
485     _transition = new Transition(_transIdent, _procedure);
486     _transWaypts.clear();
487   } else {
488     
489   }
490 }
491
492 void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts)
493 {
494   string v("All");
495   if (atts.hasAttribute("Runways")) {
496     v = atts.getValue("Runways");
497   }
498   
499   if (v == "All") {
500     for (unsigned int r=0; r<_airport->numRunways(); ++r) {
501       aProc->addRunway(_airport->getRunwayByIndex(r));
502     }
503     return;
504   }
505   
506   vector<string> rwys;
507   boost::split(rwys, v, boost::is_any_of(" ,"));
508   for (unsigned int r=0; r<rwys.size(); ++r) {
509     FGRunway* rwy = _airport->getRunwayByIdent(rwys[r]);
510     aProc->addRunway(rwy);
511   }
512 }
513
514 void NavdataVisitor::endElement(const char* name)
515 {
516   string tag(name);
517   if ((tag == "Sid_Waypoint") ||
518       (tag == "App_Waypoint") ||
519       (tag == "Star_Waypoint"))
520   {
521     _waypoints.push_back(buildWaypoint(_procedure));
522   } else if ((tag == "AppTr_Waypoint") || 
523              (tag == "SidTr_Waypoint") ||
524              (tag == "RwyTr_Waypoint") ||
525              (tag == "StarTr_Waypoint")) 
526   {
527     _transWaypts.push_back(buildWaypoint(_transition));
528   } else if (tag == "Sid_Transition") {
529     assert(_sid);
530     // SID waypoints are stored backwards, to share code with STARs
531     std::reverse(_transWaypts.begin(), _transWaypts.end());
532     _transition->setPrimary(_transWaypts);
533     _sid->addTransition(_transition);
534   } else if (tag == "Star_Transition") {
535     assert(_star);
536     _transition->setPrimary(_transWaypts);
537     _star->addTransition(_transition);
538   } else if (tag == "App_Transition") {
539     assert(_approach);
540     _transition->setPrimary(_transWaypts);
541     _approach->addTransition(_transition);
542   } else if (tag == "RunwayTransition") {
543     ArrivalDeparture* ad;
544     if (_sid) {
545       // SID waypoints are stored backwards, to share code with STARs
546       std::reverse(_transWaypts.begin(), _transWaypts.end());
547       ad = _sid;
548     } else {
549       ad = _star;
550     }
551     
552     _transition->setPrimary(_transWaypts);
553     FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent);
554     ad->addRunwayTransition(rwy, _transition);
555   } else if (tag == "Approach") {
556     finishApproach();
557   } else if (tag == "Sid") {
558     finishSid();
559   } else if (tag == "Star") {
560     finishStar();  
561   } else if (tag == "Longitude") {
562     _longitude = atof(_text.c_str());
563   } else if (tag == "Latitude") {
564     _latitude = atof(_text.c_str());
565   } else if (tag == "Name") {
566     _wayptName = _text;
567   } else if (tag == "Type") {
568     _wayptType = _text;
569   } else if (tag == "Speed") {
570     _speed = atoi(_text.c_str());
571   } else if (tag == "Altitude") {
572     _altitude = atof(_text.c_str());
573   } else if (tag == "AltitudeRestriction") {
574     if (_text == "at") {
575       _altRestrict = RESTRICT_AT;
576     } else if (_text == "above") {
577       _altRestrict = RESTRICT_ABOVE;
578     } else if (_text == "below") {
579       _altRestrict = RESTRICT_BELOW;
580     } else {
581       throw sg_format_exception("Unrecognized altitude restriction", _text);
582     }
583   } else if (tag == "Hld_Rad_or_Inbd") {
584     if (_text == "Inbd") {
585       _holdRadial = -1.0;
586     }
587   } else if (tag == "Hld_Time_or_Dist") {
588     _holdDistance = (_text == "Dist");
589   } else if (tag == "Hld_Rad_value") {
590     _holdRadial = atof(_text.c_str());
591   } else if (tag == "Hld_Turn") {
592     _holdRighthanded = (_text == "Right");
593   } else if (tag == "Hld_td_value") {
594     _holdTD = atof(_text.c_str());
595   } else if (tag == "Hdg_Crs_value") {
596     _course = atof(_text.c_str());
597   } else if (tag == "DMEtoIntercept") {
598     _dmeDistance = atof(_text.c_str());
599   } else if (tag == "RadialtoIntercept") {
600     _radial = atof(_text.c_str());
601   } else {
602     
603   }
604 }
605
606 Waypt* NavdataVisitor::buildWaypoint(Route* owner)
607 {
608   Waypt* wp = NULL;
609   if (_wayptType == "Normal") {
610     // new LatLonWaypoint
611     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
612     wp = new BasicWaypt(pos, _wayptName, owner);
613   } else if (_wayptType == "Runway") {
614     string ident = _wayptName.substr(2);
615     FGRunwayRef rwy = _airport->getRunwayByIdent(ident);
616     wp = new RunwayWaypt(rwy, owner);
617   } else if (_wayptType == "Hold") {
618     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
619     Hold* h = new Hold(pos, _wayptName, owner);
620     wp = h;
621     if (_holdRighthanded) {
622       h->setRightHanded();
623     } else {
624       h->setLeftHanded();
625     }
626     
627     if (_holdDistance) {
628       h->setHoldDistance(_holdTD);
629     } else {
630       h->setHoldTime(_holdTD * 60.0);
631     }
632     
633     if (_holdRadial >= 0.0) {
634       h->setHoldRadial(_holdRadial);
635     }
636   } else if (_wayptType == "Vectors") {
637     wp = new ATCVectors(owner, _airport);
638   } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) {
639     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
640     wp = new RadialIntercept(owner, _wayptName, pos, _course, _radial);
641   } else if (_wayptType == "DmeIntc") {
642     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
643     wp = new DMEIntercept(owner, _wayptName, pos, _course, _dmeDistance);
644   } else if (_wayptType == "ConstHdgtoAlt") {
645     wp = new HeadingToAltitude(owner, _wayptName, _course);
646   } else if (_wayptType == "PBD") {
647     SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2;
648     double az2;
649     SGGeodesy::direct(pos, _course, _dmeDistance, pos2, az2);
650     wp = new BasicWaypt(pos2, _wayptName, owner);
651   } else {
652     SG_LOG(SG_GENERAL, SG_ALERT, "implement waypoint type:" << _wayptType);
653     throw sg_format_exception("Unrecognized waypt type", _wayptType);
654   }
655   
656   assert(wp);
657   if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) {
658     wp->setAltitude(_altitude,_altRestrict);
659   }
660   
661   if (_speed > 0.0) {
662     wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW?
663   }
664   
665   return wp;
666 }
667
668 void NavdataVisitor::finishApproach()
669 {
670   WayptVec::iterator it;
671   FGRunwayRef rwy;
672   
673 // find the runway node
674   for (it = _waypoints.begin(); it != _waypoints.end(); ++it) {
675     FGPositionedRef navid = (*it)->source();
676     if (!navid) {
677       continue;
678     }
679     
680     if (navid->type() == FGPositioned::RUNWAY) {
681       rwy = (FGRunway*) navid.get();
682       break;
683     }
684   }
685   
686   if (!rwy) {
687     throw sg_format_exception("Malformed approach, no runway waypt", _ident);
688   }
689   
690   WayptVec primary(_waypoints.begin(), it);
691   // erase all points up to and including the runway, to leave only the
692   // missed segments
693   _waypoints.erase(_waypoints.begin(), ++it);
694   
695   _approach->setRunway(rwy);
696   _approach->setPrimaryAndMissed(primary, _waypoints);
697   _airport->addApproach(_approach);
698   _approach = NULL;
699 }
700
701 void NavdataVisitor::finishSid()
702 {
703   // reverse order, because that's how we deal with commonality between
704   // STARs and SIDs. SID::route undoes  this
705   std::reverse(_waypoints.begin(), _waypoints.end());
706   _sid->setCommon(_waypoints);
707   _airport->addSID(_sid);
708   _sid = NULL;
709 }
710
711 void NavdataVisitor::finishStar()
712 {
713   _star->setCommon(_waypoints);
714   _airport->addSTAR(_star);
715   _star = NULL;
716 }
717
718 void NavdataVisitor::data (const char * s, int len)
719 {
720   _text += string(s, len);
721 }
722
723
724 void NavdataVisitor::pi (const char * target, const char * data) {
725   //cout << "Processing instruction " << target << ' ' << data << endl;
726 }
727
728 void NavdataVisitor::warning (const char * message, int line, int column) {
729   SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
730 }
731
732 void NavdataVisitor::error (const char * message, int line, int column) {
733   SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
734 }
735
736 } // of namespace flightgear