1 // FlightPlan.cxx - flight plan object
3 // Written by James Turner, started 2012.
5 // Copyright (C) 2012 Curtis L. Olson
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.
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.
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.
25 #include "FlightPlan.hxx"
33 #include <boost/algorithm/string/case_conv.hpp>
34 #include <boost/algorithm/string.hpp>
35 #include <boost/foreach.hpp>
38 #include <simgear/structure/exception.hxx>
39 #include <simgear/misc/sg_path.hxx>
40 #include <simgear/magvar/magvar.hxx>
41 #include <simgear/timing/sg_time.hxx>
42 #include <simgear/misc/sgstream.hxx>
43 #include <simgear/misc/strutils.hxx>
44 #include <simgear/props/props_io.hxx>
45 #include <simgear/xml/easyxml.hxx>
48 #include <Main/globals.hxx>
49 #include "Main/fg_props.hxx"
50 #include <Navaids/procedure.hxx>
51 #include <Navaids/waypoint.hxx>
52 #include <Navaids/routePath.hxx>
53 #include <Navaids/airways.hxx>
60 namespace flightgear {
62 typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
63 static FPDelegateFactoryVec static_delegateFactories;
65 FlightPlan::FlightPlan() :
68 _followLegTrackToFix(true),
69 _aircraftCategory(ICAO_AIRCRAFT_CATEGORY_C),
70 _departureRunway(NULL),
71 _destinationRunway(NULL),
78 _departureChanged = _arrivalChanged = _waypointsChanged = _currentWaypointChanged = false;
80 BOOST_FOREACH(DelegateFactory* factory, static_delegateFactories) {
81 Delegate* d = factory->createFlightPlanDelegate(this);
82 if (d) { // factory might not always create a delegate
83 d->_deleteWithPlan = true;
89 FlightPlan::~FlightPlan()
91 // delete all delegates which we own.
92 Delegate* d = _delegate;
96 if (cur->_deleteWithPlan) {
102 BOOST_FOREACH(Leg* l, _legs) {
107 FlightPlan* FlightPlan::clone(const string& newIdent) const
109 FlightPlan* c = new FlightPlan();
110 c->_ident = newIdent.empty() ? _ident : newIdent;
113 // copy destination / departure data.
114 c->setDeparture(_departure);
115 c->setDeparture(_departureRunway);
118 c->setApproach(_approach);
119 } else if (_destinationRunway) {
120 c->setDestination(_destinationRunway);
121 } else if (_destination) {
122 c->setDestination(_destination);
129 c->_waypointsChanged = true;
130 for (int l=0; l < numLegs(); ++l) {
131 c->_legs.push_back(_legs[l]->cloneFor(c));
137 void FlightPlan::setIdent(const string& s)
142 string FlightPlan::ident() const
147 FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex)
157 if ((aIndex == -1) || (aIndex > (int) _legs.size())) {
158 index = _legs.size();
161 insertWayptsAtIndex(wps, index);
162 return legAtIndex(index);
165 void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex)
172 if ((aIndex == -1) || (aIndex > (int) _legs.size())) {
173 index = _legs.size();
176 LegVec::iterator it = _legs.begin();
179 int endIndex = index + wps.size() - 1;
180 if (_currentIndex >= endIndex) {
181 _currentIndex += wps.size();
185 BOOST_FOREACH(WayptRef wp, wps) {
186 newLegs.push_back(new Leg(this, wp));
190 _waypointsChanged = true;
191 _legs.insert(it, newLegs.begin(), newLegs.end());
195 void FlightPlan::deleteIndex(int aIndex)
198 if (aIndex < 0) { // negative indices count the the end
199 index = _legs.size() + index;
202 if ((index < 0) || (index >= numLegs())) {
203 SG_LOG(SG_NAVAID, SG_WARN, "removeAtIndex with invalid index:" << aIndex);
208 _waypointsChanged = true;
210 LegVec::iterator it = _legs.begin();
216 if (_currentIndex == index) {
217 // current waypoint was removed
218 _currentWaypointChanged = true;
219 } else if (_currentIndex > index) {
220 --_currentIndex; // shift current index down if necessary
226 void FlightPlan::clear()
229 _waypointsChanged = true;
230 _currentWaypointChanged = true;
231 _arrivalChanged = true;
232 _departureChanged = true;
235 BOOST_FOREACH(Leg* l, _legs) {
241 _delegate->runCleared();
249 RemoveWithFlag(WayptFlag f) : flag(f), delCount(0) { }
251 int numDeleted() const { return delCount; }
253 bool operator()(FlightPlan::Leg* leg) const
255 if (leg->waypoint()->flag(flag)) {
265 mutable int delCount;
268 int FlightPlan::clearWayptsWithFlag(WayptFlag flag)
271 // first pass, fix up currentIndex
272 for (int i=0; i<_currentIndex; ++i) {
274 if (l->waypoint()->flag(flag)) {
279 // test if the current leg will be removed
280 bool currentIsBeingCleared = false;
281 Leg* curLeg = currentLeg();
283 currentIsBeingCleared = curLeg->waypoint()->flag(flag);
286 _currentIndex -= count;
288 // if we're clearing the current waypoint, what shall we do with the
289 // index? there's various options, but safest is to select no waypoint
290 // and let the use re-activate.
291 // http://code.google.com/p/flightgear-bugs/issues/detail?id=1134
292 if (currentIsBeingCleared) {
293 SG_LOG(SG_GENERAL, SG_INFO, "currentIsBeingCleared:" << currentIsBeingCleared);
297 // now delete and remove
298 RemoveWithFlag rf(flag);
299 LegVec::iterator it = std::remove_if(_legs.begin(), _legs.end(), rf);
300 if (it == _legs.end()) {
301 return 0; // nothing was cleared, don't fire the delegate
305 _waypointsChanged = true;
306 if ((count > 0) || currentIsBeingCleared) {
307 _currentWaypointChanged = true;
310 _legs.erase(it, _legs.end());
312 if (_legs.empty()) { // maybe all legs were deleted
314 _delegate->runCleared();
319 return rf.numDeleted();
322 void FlightPlan::setCurrentIndex(int index)
324 if ((index < -1) || (index >= numLegs())) {
325 throw sg_range_exception("invalid leg index", "FlightPlan::setCurrentIndex");
328 if (index == _currentIndex) {
333 _currentIndex = index;
334 _currentWaypointChanged = true;
338 void FlightPlan::finish()
340 if (_currentIndex == -1) {
346 _currentWaypointChanged = true;
349 _delegate->runFinished();
355 int FlightPlan::findWayptIndex(const SGGeod& aPos) const
357 for (int i=0; i<numLegs(); ++i) {
358 if (_legs[i]->waypoint()->matches(aPos)) {
366 int FlightPlan::findWayptIndex(const FGPositionedRef aPos) const
368 for (int i=0; i<numLegs(); ++i) {
369 if (_legs[i]->waypoint()->source() == aPos) {
377 FlightPlan::Leg* FlightPlan::currentLeg() const
379 if ((_currentIndex < 0) || (_currentIndex >= numLegs()))
381 return legAtIndex(_currentIndex);
384 FlightPlan::Leg* FlightPlan::previousLeg() const
386 if (_currentIndex <= 0) {
390 return legAtIndex(_currentIndex - 1);
393 FlightPlan::Leg* FlightPlan::nextLeg() const
395 if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) {
399 return legAtIndex(_currentIndex + 1);
402 FlightPlan::Leg* FlightPlan::legAtIndex(int index) const
404 if ((index < 0) || (index >= numLegs())) {
405 throw sg_range_exception("index out of range", "FlightPlan::legAtIndex");
411 int FlightPlan::findLegIndex(const Leg *l) const
413 for (unsigned int i=0; i<_legs.size(); ++i) {
422 void FlightPlan::setDeparture(FGAirport* apt)
424 if (apt == _departure) {
429 _departureChanged = true;
431 _departureRunway = NULL;
436 void FlightPlan::setDeparture(FGRunway* rwy)
438 if (_departureRunway == rwy) {
443 _departureChanged = true;
445 _departureRunway = rwy;
446 if (rwy->airport() != _departure) {
447 _departure = rwy->airport();
453 void FlightPlan::setSID(SID* sid, const std::string& transition)
460 _departureChanged = true;
462 _sidTransition = transition;
466 void FlightPlan::setSID(Transition* trans)
473 if (trans->parent()->type() != PROCEDURE_SID)
474 throw sg_exception("FlightPlan::setSID: transition does not belong to a SID");
476 setSID((SID*) trans->parent(), trans->ident());
479 Transition* FlightPlan::sidTransition() const
481 if (!_sid || _sidTransition.empty()) {
485 return _sid->findTransitionByName(_sidTransition);
488 void FlightPlan::setDestination(FGAirport* apt)
490 if (apt == _destination) {
495 _arrivalChanged = true;
497 _destinationRunway = NULL;
498 setSTAR((STAR*)NULL);
503 void FlightPlan::setDestination(FGRunway* rwy)
505 if (_destinationRunway == rwy) {
510 _arrivalChanged = true;
511 _destinationRunway = rwy;
512 if (_destination != rwy->airport()) {
513 _destination = rwy->airport();
514 setSTAR((STAR*)NULL);
520 void FlightPlan::setSTAR(STAR* star, const std::string& transition)
527 _arrivalChanged = true;
529 _starTransition = transition;
533 void FlightPlan::setSTAR(Transition* trans)
536 setSTAR((STAR*) NULL);
540 if (trans->parent()->type() != PROCEDURE_STAR)
541 throw sg_exception("FlightPlan::setSTAR: transition does not belong to a STAR");
543 setSTAR((STAR*) trans->parent(), trans->ident());
546 Transition* FlightPlan::starTransition() const
548 if (!_star || _starTransition.empty()) {
552 return _star->findTransitionByName(_starTransition);
555 void FlightPlan::setApproach(flightgear::Approach *app)
557 if (_approach == app) {
562 _arrivalChanged = true;
565 // keep runway + airport in sync
566 if (_destinationRunway != _approach->runway()) {
567 _destinationRunway = _approach->runway();
570 if (_destination != _destinationRunway->airport()) {
571 _destination = _destinationRunway->airport();
577 bool FlightPlan::save(const SGPath& path)
579 SG_LOG(SG_NAVAID, SG_INFO, "Saving route to " << path.str());
581 SGPropertyNode_ptr d(new SGPropertyNode);
582 d->setIntValue("version", 2);
585 d->setStringValue("departure/airport", _departure->ident());
587 d->setStringValue("departure/sid", _sid->ident());
590 if (_departureRunway) {
591 d->setStringValue("departure/runway", _departureRunway->ident());
596 d->setStringValue("destination/airport", _destination->ident());
598 d->setStringValue("destination/star", _star->ident());
602 d->setStringValue("destination/approach", _approach->ident());
605 //d->setStringValue("destination/transition", destination->getStringValue("transition"));
607 if (_destinationRunway) {
608 d->setStringValue("destination/runway", _destinationRunway->ident());
613 SGPropertyNode* routeNode = d->getChild("route", 0, true);
614 for (unsigned int i=0; i<_legs.size(); ++i) {
615 Waypt* wpt = _legs[i]->waypoint();
616 wpt->saveAsNode(routeNode->getChild("wp", i, true));
617 } // of waypoint iteration
618 writeProperties(path.str(), d, true /* write-all */);
620 } catch (sg_exception& e) {
621 SG_LOG(SG_NAVAID, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage());
626 bool FlightPlan::load(const SGPath& path)
630 SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan '" << path.str()
631 << "'. The file does not exist.");
635 SG_LOG(SG_NAVAID, SG_INFO, "going to read flight-plan from:" << path.str());
640 // try different file formats
641 if (loadGpxFormat(path)) // GPX format
644 if (loadXmlFormat(path)) // XML property data
647 if (loadPlainTextFormat(path)) // simple textual list of waypoints
650 _waypointsChanged = true;
656 /** XML loader for GPX file format */
657 class GpxXmlVisitor : public XMLVisitor
660 GpxXmlVisitor(FlightPlan* fp) : _fp(fp), _lat(-9999), _lon(-9999) {}
662 virtual void startElement (const char * name, const XMLAttributes &atts);
663 virtual void endElement (const char * name);
664 virtual void data (const char * s, int length);
673 void GpxXmlVisitor::startElement(const char * name, const XMLAttributes &atts)
676 if (strcmp(name, "rtept")==0)
681 const char* slat = atts.getValue("lat");
682 const char* slon = atts.getValue("lon");
691 void GpxXmlVisitor::data(const char * s, int length)
693 // use "name" when given, otherwise use "cmt" (comment) as ID
694 if ((_element == "name")||
695 ((_waypoint == "")&&(_element == "cmt")))
697 char* buf = (char*) malloc(length+1);
698 memcpy(buf, s, length);
705 void GpxXmlVisitor::endElement(const char * name)
708 if (strcmp(name, "rtept") == 0)
712 _fp->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(_lon, _lat), _waypoint.c_str(), NULL), -1);
717 /** Load a flightplan in GPX format */
718 bool FlightPlan::loadGpxFormat(const SGPath& path)
720 if (path.lower_extension() != "gpx")
722 // not a valid GPX file
727 GpxXmlVisitor gpxVistor(this);
730 readXML(path.str(), gpxVistor);
731 } catch (sg_exception& e)
733 // XML parsing fails => not a GPX XML file
734 SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format: '" << e.getOrigin()
735 << "'. " << e.getMessage());
741 SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format. No route found.");
748 /** Load a flightplan in FlightGear XML property format */
749 bool FlightPlan::loadXmlFormat(const SGPath& path)
751 SGPropertyNode_ptr routeData(new SGPropertyNode);
753 readProperties(path.str(), routeData);
754 } catch (sg_exception& e) {
755 SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
756 << "'. " << e.getMessage());
757 // XML parsing fails => not a property XML file
761 if (routeData.valid())
764 int version = routeData->getIntValue("version", 1);
766 loadVersion1XMLRoute(routeData);
767 } else if (version == 2) {
768 loadVersion2XMLRoute(routeData);
770 throw sg_io_exception("unsupported XML route version");
773 } catch (sg_exception& e) {
774 SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
775 << "'. " << e.getMessage());
782 void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
785 SGPropertyNode* dep = routeData->getChild("departure");
787 string depIdent = dep->getStringValue("airport");
788 setDeparture((FGAirport*) fgFindAirportID(depIdent));
790 string rwy(dep->getStringValue("runway"));
791 if (_departure->hasRunwayWithIdent(rwy)) {
792 setDeparture(_departure->getRunwayByIdent(rwy));
795 if (dep->hasChild("sid")) {
796 setSID(_departure->findSIDWithIdent(dep->getStringValue("sid")));
798 // departure->setStringValue("transition", dep->getStringValue("transition"));
803 SGPropertyNode* dst = routeData->getChild("destination");
805 setDestination((FGAirport*) fgFindAirportID(dst->getStringValue("airport")));
807 string rwy(dst->getStringValue("runway"));
808 if (_destination->hasRunwayWithIdent(rwy)) {
809 setDestination(_destination->getRunwayByIdent(rwy));
812 if (dst->hasChild("star")) {
813 setSTAR(_destination->findSTARWithIdent(dst->getStringValue("star")));
816 if (dst->hasChild("approach")) {
817 setApproach(_destination->findApproachWithIdent(dst->getStringValue("approach")));
821 // destination->setStringValue("transition", dst->getStringValue("transition"));
825 SGPropertyNode* alt = routeData->getChild("alternate");
827 //alternate->setStringValue(alt->getStringValue("airport"));
831 SGPropertyNode* crs = routeData->getChild("cruise");
833 // cruise->setDoubleValue("speed-kts", crs->getDoubleValue("speed-kts"));
834 // cruise->setDoubleValue("mach", crs->getDoubleValue("mach"));
835 // cruise->setDoubleValue("altitude-ft", crs->getDoubleValue("altitude-ft"));
836 } // of cruise data loading
840 void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
842 loadXMLRouteHeader(routeData);
846 SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);
847 if (routeNode.valid()) {
848 for (int i=0; i<routeNode->nChildren(); ++i) {
849 SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
850 Leg* l = new Leg(this, Waypt::createFromProperties(NULL, wpNode));
852 } // of route iteration
854 _waypointsChanged = true;
857 void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData)
859 loadXMLRouteHeader(routeData);
863 SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);
864 for (int i=0; i<routeNode->nChildren(); ++i) {
865 SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
866 Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode));
868 } // of route iteration
869 _waypointsChanged = true;
872 WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
875 if (!_legs.empty()) {
876 lastPos = _legs.back()->waypoint()->position();
877 } else if (_departure) {
878 lastPos = _departure->geod();
882 string ident(aWP->getStringValue("ident"));
883 if (aWP->hasChild("longitude-deg")) {
884 // explicit longitude/latitude
885 w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"),
886 aWP->getDoubleValue("latitude-deg")), ident, NULL);
889 string nid = aWP->getStringValue("navid", ident.c_str());
890 FGPositionedRef p = FGPositioned::findClosestWithIdent(nid, lastPos);
896 SG_LOG(SG_GENERAL, SG_WARN, "unknown navaid in flightplan:" << nid);
897 pos = SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"),
898 aWP->getDoubleValue("latitude-deg"));
901 if (aWP->hasChild("offset-nm") && aWP->hasChild("offset-radial")) {
902 double radialDeg = aWP->getDoubleValue("offset-radial");
903 // convert magnetic radial to a true radial!
904 radialDeg += magvarDegAt(pos);
905 double offsetNm = aWP->getDoubleValue("offset-nm");
907 SGGeodesy::direct(pos, radialDeg, offsetNm * SG_NM_TO_METER, pos, az2);
910 w = new BasicWaypt(pos, ident, NULL);
913 double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
914 if (altFt > -9990.0) {
915 w->setAltitude(altFt, RESTRICT_AT);
921 /** Load a flightplan in FlightGear plain-text format */
922 bool FlightPlan::loadPlainTextFormat(const SGPath& path)
925 sg_gzifstream in(path.str().c_str());
927 throw sg_io_exception("Cannot open file for reading.");
933 getline(in, line, '\n');
934 // trim CR from end of line, if found
935 if (line[line.size() - 1] == '\r') {
936 line.erase(line.size() - 1, 1);
939 line = simgear::strutils::strip(line);
940 if (line.empty() || (line[0] == '#')) {
941 continue; // ignore empty/comment lines
944 WayptRef w = waypointFromString(line);
946 throw sg_io_exception("Failed to create waypoint from line '" + line + "'.");
949 _legs.push_back(new Leg(this, w));
950 } // of line iteration
951 } catch (sg_exception& e) {
952 SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage());
960 double FlightPlan::magvarDegAt(const SGGeod& pos) const
962 double jd = globals->get_time_params()->getJD();
963 return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
970 WayptRef intersectionFromString(FGPositionedRef p1,
971 const SGGeod& basePosition,
973 const string_list& pieces)
975 assert(pieces.size() == 4);
976 // navid/radial/navid/radial notation
977 FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition);
979 SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]);
983 double r1 = atof(pieces[1].c_str()),
984 r2 = atof(pieces[3].c_str());
989 bool ok = SGGeodesy::radialIntersection(p1->geod(), r1, p2->geod(), r2, intersection);
991 SG_LOG(SG_NAVAID, SG_INFO, "no valid intersection for:" << pieces[0]
992 << "/" << pieces[2]);
996 std::string name = p1->ident() + "-" + p2->ident();
997 return new BasicWaypt(intersection, name, NULL);
1000 WayptRef wayptFromLonLatString(const std::string& target)
1002 size_t pos = target.find( ',' );
1003 if ( pos == string::npos )
1006 double lon = atof( target.substr(0, pos).c_str());
1007 double lat = atof( target.c_str() + pos + 1);
1009 char ew = (lon < 0.0) ? 'W' : 'E';
1010 char ns = (lat < 0.0) ? 'S' : 'N';
1011 snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat));
1013 return new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL);
1016 WayptRef viaFromString(const SGGeod& basePosition, const std::string& target)
1018 assert(target.find("VIA ") == 0);
1019 string_list pieces(simgear::strutils::split(target, "/"));
1020 if ((pieces.size() != 4) || (pieces[2] != "TO")) {
1021 SG_LOG( SG_NAVAID, SG_WARN, "Malformed VIA specification string:" << target);
1025 // airway ident is pieces[1]
1026 Airway* airway = Airway::findByIdent(pieces[1]);
1027 if (airway == NULL) {
1028 SG_LOG( SG_NAVAID, SG_WARN, "Unknown airway:" << pieces[1]);
1032 // TO navaid is pieces[3]
1033 FGPositionedRef nav = FGPositioned::findClosestWithIdent(pieces[3], basePosition, NULL);
1034 if (!nav || !airway->containsNavaid(nav)) {
1035 SG_LOG( SG_NAVAID, SG_WARN, "TO navaid:" << pieces[3] << " unknown or not on airway");
1039 Via* via = new Via(NULL, pieces[1], nav);
1043 } // of anonymous namespace
1045 WayptRef FlightPlan::waypointFromString(const string& tgt )
1047 string target(boost::to_upper_copy(tgt));
1050 RouteRestriction altSetting = RESTRICT_NONE;
1052 size_t pos = target.find( '@' );
1053 if ( pos != string::npos ) {
1054 altFt = atof( target.c_str() + pos + 1 );
1055 target = target.substr( 0, pos );
1056 if ( !strcmp(fgGetString("/sim/startup/units"), "meter") )
1057 altFt *= SG_METER_TO_FEET;
1058 altSetting = RESTRICT_AT;
1061 // check for lon,lat
1062 WayptRef wpt = wayptFromLonLatString(target);
1064 SGGeod basePosition;
1065 if (_legs.empty()) {
1066 // route is empty, use current position
1067 basePosition = globals->get_aircraft_position();
1069 basePosition = _legs.back()->waypoint()->position();
1072 const double magvar = magvarDegAt(basePosition);
1075 // already handled in the lat/lon test above
1076 } else if (target.find("VIA ") == 0) {
1077 wpt = viaFromString(basePosition, target);
1079 string_list pieces(simgear::strutils::split(target, "/"));
1080 FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition);
1082 SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front());
1086 if (pieces.size() == 1) {
1087 wpt = new NavaidWaypoint(p, NULL);
1088 } else if (pieces.size() == 3) {
1089 // navaid/radial/distance-nm notation
1090 double radial = atof(pieces[1].c_str()),
1091 distanceNm = atof(pieces[2].c_str());
1093 wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm);
1094 } else if (pieces.size() == 2) {
1095 FGAirport* apt = dynamic_cast<FGAirport*>(p.ptr());
1097 SG_LOG(SG_NAVAID, SG_INFO, "Waypoint is not an airport:" << pieces.front());
1101 if (!apt->hasRunwayWithIdent(pieces[1])) {
1102 SG_LOG(SG_NAVAID, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]);
1106 FGRunway* runway = apt->getRunwayByIdent(pieces[1]);
1107 wpt = new NavaidWaypoint(runway, NULL);
1108 } else if (pieces.size() == 4) {
1109 wpt = intersectionFromString(p, basePosition, magvar, pieces);
1114 SG_LOG(SG_NAVAID, SG_INFO, "Unable to parse waypoint:" << target);
1118 if (altSetting != RESTRICT_NONE) {
1119 wpt->setAltitude(altFt, altSetting);
1125 void FlightPlan::activate()
1130 _currentWaypointChanged = true;
1132 for (unsigned int i=0; i < _legs.size(); ) {
1133 if (_legs[i]->waypoint()->type() == "via") {
1134 WayptRef preceeding = _legs[i - 1]->waypoint();
1135 Via* via = static_cast<Via*>(_legs[i]->waypoint());
1136 WayptVec wps = via->expandToWaypoints(preceeding);
1138 // delete the VIA leg
1139 LegVec::iterator it = _legs.begin();
1145 // create new lefs and insert
1150 BOOST_FOREACH(WayptRef wp, wps) {
1151 newLegs.push_back(new Leg(this, wp));
1154 _waypointsChanged = true;
1155 _legs.insert(it, newLegs.begin(), newLegs.end());
1157 ++i; // normal case, no expansion
1162 _delegate->runActivated();
1168 FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) :
1170 _speedRestrict(RESTRICT_NONE),
1171 _altRestrict(RESTRICT_NONE),
1175 throw sg_exception("can't create FlightPlan::Leg without underlying waypoint");
1177 _speed = _altitudeFt = 0;
1180 FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const
1182 Leg* c = new Leg(owner, _waypt);
1185 c->_speedRestrict = _speedRestrict;
1186 c->_altitudeFt = _altitudeFt;
1187 c->_altRestrict = _altRestrict;
1192 FlightPlan::Leg* FlightPlan::Leg::nextLeg() const
1194 return _parent->legAtIndex(index() + 1);
1197 unsigned int FlightPlan::Leg::index() const
1199 return _parent->findLegIndex(this);
1202 int FlightPlan::Leg::altitudeFt() const
1204 if (_altRestrict != RESTRICT_NONE) {
1208 return _waypt->altitudeFt();
1211 int FlightPlan::Leg::speed() const
1213 if (_speedRestrict != RESTRICT_NONE) {
1217 return _waypt->speed();
1220 int FlightPlan::Leg::speedKts() const
1225 double FlightPlan::Leg::speedMach() const
1227 if (!isMachRestrict(_speedRestrict)) {
1231 return -(_speed / 100.0);
1234 RouteRestriction FlightPlan::Leg::altitudeRestriction() const
1236 if (_altRestrict != RESTRICT_NONE) {
1237 return _altRestrict;
1240 return _waypt->altitudeRestriction();
1243 RouteRestriction FlightPlan::Leg::speedRestriction() const
1245 if (_speedRestrict != RESTRICT_NONE) {
1246 return _speedRestrict;
1249 return _waypt->speedRestriction();
1252 void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed)
1254 _speedRestrict = ty;
1255 if (isMachRestrict(ty)) {
1256 _speed = (speed * -100);
1262 void FlightPlan::Leg::setAltitude(RouteRestriction ty, int altFt)
1265 _altitudeFt = altFt;
1268 double FlightPlan::Leg::courseDeg() const
1273 double FlightPlan::Leg::distanceNm() const
1275 return _pathDistance;
1278 double FlightPlan::Leg::distanceAlongRoute() const
1280 return _distanceAlongPath;
1283 void FlightPlan::rebuildLegData()
1285 _totalDistance = 0.0;
1286 double totalDistanceIncludingMissed = 0.0;
1287 RoutePath path(this);
1289 for (unsigned int l=0; l<_legs.size(); ++l) {
1290 _legs[l]->_courseDeg = path.trackForIndex(l);
1291 _legs[l]->_pathDistance = path.distanceForIndex(l) * SG_METER_TO_NM;
1293 totalDistanceIncludingMissed += _legs[l]->_pathDistance;
1294 // distance along path includes our own leg distance
1295 _legs[l]->_distanceAlongPath = totalDistanceIncludingMissed;
1297 // omit missed-approach waypoints from total distance calculation
1298 if (!_legs[l]->waypoint()->flag(WPT_MISS)) {
1299 _totalDistance += _legs[l]->_pathDistance;
1301 } // of legs iteration
1305 SGGeod FlightPlan::pointAlongRoute(int aIndex, double aOffsetNm) const
1308 return rp.positionForDistanceFrom(aIndex, aOffsetNm * SG_NM_TO_METER);
1311 void FlightPlan::lockDelegate()
1313 if (_delegateLock == 0) {
1314 assert(!_departureChanged && !_arrivalChanged &&
1315 !_waypointsChanged && !_currentWaypointChanged);
1321 void FlightPlan::unlockDelegate()
1323 assert(_delegateLock > 0);
1324 if (_delegateLock > 1) {
1329 if (_departureChanged) {
1330 _departureChanged = false;
1332 _delegate->runDepartureChanged();
1336 if (_arrivalChanged) {
1337 _arrivalChanged = false;
1339 _delegate->runArrivalChanged();
1343 if (_waypointsChanged) {
1344 _waypointsChanged = false;
1347 _delegate->runWaypointsChanged();
1351 if (_currentWaypointChanged) {
1352 _currentWaypointChanged = false;
1354 _delegate->runCurrentWaypointChanged();
1361 void FlightPlan::registerDelegateFactory(DelegateFactory* df)
1363 FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(),
1364 static_delegateFactories.end(), df);
1365 if (it != static_delegateFactories.end()) {
1366 throw sg_exception("duplicate delegate factory registration");
1369 static_delegateFactories.push_back(df);
1372 void FlightPlan::unregisterDelegateFactory(DelegateFactory* df)
1374 FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(),
1375 static_delegateFactories.end(), df);
1376 if (it == static_delegateFactories.end()) {
1380 static_delegateFactories.erase(it);
1383 void FlightPlan::addDelegate(Delegate* d)
1385 // wrap any existing delegate(s) in the new one
1386 d->_inner = _delegate;
1390 void FlightPlan::removeDelegate(Delegate* d)
1392 if (d == _delegate) {
1393 _delegate = _delegate->_inner;
1394 } else if (_delegate) {
1395 _delegate->removeInner(d);
1399 FlightPlan::Delegate::Delegate() :
1400 _deleteWithPlan(false),
1405 FlightPlan::Delegate::~Delegate()
1409 void FlightPlan::Delegate::removeInner(Delegate* d)
1412 throw sg_exception("FlightPlan delegate not found");
1416 // replace with grand-child
1418 } else { // recurse downwards
1419 _inner->removeInner(d);
1423 void FlightPlan::Delegate::runDepartureChanged()
1425 if (_inner) _inner->runDepartureChanged();
1429 void FlightPlan::Delegate::runArrivalChanged()
1431 if (_inner) _inner->runArrivalChanged();
1435 void FlightPlan::Delegate::runWaypointsChanged()
1437 if (_inner) _inner->runWaypointsChanged();
1441 void FlightPlan::Delegate::runCurrentWaypointChanged()
1443 if (_inner) _inner->runCurrentWaypointChanged();
1444 currentWaypointChanged();
1447 void FlightPlan::Delegate::runCleared()
1449 if (_inner) _inner->runCleared();
1453 void FlightPlan::Delegate::runFinished()
1455 if (_inner) _inner->runFinished();
1459 void FlightPlan::Delegate::runActivated()
1461 if (_inner) _inner->runActivated();
1465 void FlightPlan::setFollowLegTrackToFixes(bool tf)
1467 _followLegTrackToFix = tf;
1470 bool FlightPlan::followLegTrackToFixes() const
1472 return _followLegTrackToFix;
1475 std::string FlightPlan::icaoAircraftCategory() const
1478 r.push_back(_aircraftCategory);
1482 void FlightPlan::setIcaoAircraftCategory(const std::string& cat)
1485 throw sg_range_exception("Invalid ICAO aircraft category:", cat);
1488 if ((cat[0] < ICAO_AIRCRAFT_CATEGORY_A) || (cat[0] > ICAO_AIRCRAFT_CATEGORY_E)) {
1489 throw sg_range_exception("Invalid ICAO aircraft category:", cat);
1492 _aircraftCategory = cat[0];
1496 } // of namespace flightgear