From: James Turner Date: Sat, 12 May 2012 09:25:56 +0000 (+0100) Subject: Break FlightPlan out into its own file. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=0f61108f5b00518217e41de445660181ecf603d3;p=flightgear.git Break FlightPlan out into its own file. --- diff --git a/src/Autopilot/route_mgr.hxx b/src/Autopilot/route_mgr.hxx index babfe31ef..bb16fc668 100644 --- a/src/Autopilot/route_mgr.hxx +++ b/src/Autopilot/route_mgr.hxx @@ -27,17 +27,12 @@ #include #include -#include +#include // forward decls class SGPath; class PropertyWatcher; -class FGAirport; -class FGRunway; - -typedef SGSharedPtr FGAirportRef; - /** * Top level route manager class * diff --git a/src/Navaids/CMakeLists.txt b/src/Navaids/CMakeLists.txt index cd806a65e..1d2919902 100644 --- a/src/Navaids/CMakeLists.txt +++ b/src/Navaids/CMakeLists.txt @@ -13,6 +13,7 @@ set(SOURCES routePath.cxx waypoint.cxx LevelDXML.cxx + FlightPlan.cxx ) set(HEADERS @@ -28,6 +29,7 @@ set(HEADERS routePath.hxx waypoint.hxx LevelDXML.hxx + FlightPlan.hxx ) flightgear_component(Navaids "${SOURCES}" "${HEADERS}") \ No newline at end of file diff --git a/src/Navaids/FlightPlan.cxx b/src/Navaids/FlightPlan.cxx new file mode 100644 index 000000000..7d7233be4 --- /dev/null +++ b/src/Navaids/FlightPlan.cxx @@ -0,0 +1,1054 @@ +// FlightPlan.cxx - flight plan object + +// Written by James Turner, started 2012. +// +// Copyright (C) 2012 Curtis L. Olson +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "FlightPlan.hxx" + +// std +#include +#include + +// Boost +#include +#include +#include + +// SimGear +#include +#include +#include +#include +#include +#include +#include + +// FlightGear +#include
+#include "Main/fg_props.hxx" +#include +#include + +using std::string; +using std::vector; +using std::endl; +using std::fstream; + +namespace flightgear { + +FlightPlan::FlightPlan() : + _currentIndex(-1), + _departureRunway(NULL), + _destinationRunway(NULL), + _sid(NULL), + _star(NULL), + _approach(NULL), + _delegate(NULL) +{ + +} + +FlightPlan::~FlightPlan() +{ + +} + +FlightPlan* FlightPlan::clone(const string& newIdent) const +{ + FlightPlan* c = new FlightPlan(); + c->_ident = newIdent.empty() ? _ident : newIdent; + +// copy destination / departure data. + c->setDeparture(_departure); + c->setDeparture(_departureRunway); + + if (_approach) { + c->setApproach(_approach); + } else if (_destinationRunway) { + c->setDestination(_destinationRunway); + } else if (_destination) { + c->setDestination(_destination); + } + + c->setSTAR(_star); + c->setSID(_sid); + +// copy legs + for (int l=0; l < numLegs(); ++l) { + c->_legs.push_back(_legs[l]->cloneFor(c)); + } + + return c; +} + +void FlightPlan::setIdent(const string& s) +{ + _ident = s; +} + +string FlightPlan::ident() const +{ + return _ident; +} + +FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex) +{ + if (!aWpt) { + return NULL; + } + + WayptVec wps; + wps.push_back(aWpt); + + int index = aIndex; + if ((aIndex == -1) || (aIndex > (int) _legs.size())) { + index = _legs.size(); + } + + insertWayptsAtIndex(wps, index); + return legAtIndex(aIndex); +} + +void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex) +{ + if (wps.empty()) { + return; + } + + int index = aIndex; + if ((aIndex == -1) || (aIndex > (int) _legs.size())) { + index = _legs.size(); + } + + LegVec::iterator it = _legs.begin(); + it += index; + + int endIndex = index + wps.size() - 1; + if (_currentIndex >= endIndex) { + _currentIndex += wps.size(); + } + + LegVec newLegs; + BOOST_FOREACH(WayptRef wp, wps) { + newLegs.push_back(new Leg(this, wp)); + } + + _legs.insert(it, newLegs.begin(), newLegs.end()); + rebuildLegData(); + + if (_delegate) { + _delegate->runWaypointsChanged(); + } +} + +void FlightPlan::deleteIndex(int aIndex) +{ + int index = aIndex; + if (aIndex < 0) { // negative indices count the the end + index = _legs.size() + index; + } + + if ((index < 0) || (index >= numLegs())) { + SG_LOG(SG_AUTOPILOT, SG_WARN, "removeAtIndex with invalid index:" << aIndex); + return; + } + LegVec::iterator it = _legs.begin(); + it += index; + Leg* l = *it; + _legs.erase(it); + delete l; + + bool curChanged = false; + if (_currentIndex == index) { + // current waypoint was removed + curChanged = true; + } else if (_currentIndex > index) { + --_currentIndex; // shift current index down if necessary + } + + rebuildLegData(); + if (_delegate) { + _delegate->runWaypointsChanged(); + if (curChanged) { + _delegate->runCurrentWaypointChanged(); + } + } +} + +void FlightPlan::clear() +{ + _currentIndex = -1; + BOOST_FOREACH(Leg* l, _legs) { + delete l; + } + _legs.clear(); + rebuildLegData(); + if (_delegate) { + _delegate->runDepartureChanged(); + _delegate->runArrivalChanged(); + _delegate->runWaypointsChanged(); + _delegate->runCurrentWaypointChanged(); + } +} + +int FlightPlan::clearWayptsWithFlag(WayptFlag flag) +{ + int count = 0; + for (unsigned int i=0; i<_legs.size(); ++i) { + Leg* l = _legs[i]; + if (!l->waypoint()->flag(flag)) { + continue; + } + + // okay, we're going to clear this leg + ++count; + if (_currentIndex > (int) i) { + --_currentIndex; + } + + delete l; + LegVec::iterator it = _legs.begin(); + it += i; + _legs.erase(it); + } + + if (count == 0) { + return 0; // nothing was cleared, don't fire the delegate + } + + rebuildLegData(); + if (_delegate) { + _delegate->runWaypointsChanged(); + _delegate->runCurrentWaypointChanged(); + } + + return count; +} + +void FlightPlan::setCurrentIndex(int index) +{ + if ((index < -1) || (index >= numLegs())) { + throw sg_range_exception("invalid leg index", "FlightPlan::setCurrentIndex"); + } + + if (index == _currentIndex) { + return; + } + + _currentIndex = index; + if (_delegate) { + _delegate->runCurrentWaypointChanged(); + } +} + +int FlightPlan::findWayptIndex(const SGGeod& aPos) const +{ + for (int i=0; iwaypoint()->matches(aPos)) { + return i; + } + } + + return -1; +} + +FlightPlan::Leg* FlightPlan::currentLeg() const +{ + if ((_currentIndex < 0) || (_currentIndex >= numLegs())) + return NULL; + return legAtIndex(_currentIndex); +} + +FlightPlan::Leg* FlightPlan::previousLeg() const +{ + if (_currentIndex == 0) { + return NULL; + } + + return legAtIndex(_currentIndex - 1); +} + +FlightPlan::Leg* FlightPlan::nextLeg() const +{ + if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) { + return NULL; + } + + return legAtIndex(_currentIndex + 1); +} + +FlightPlan::Leg* FlightPlan::legAtIndex(int index) const +{ + if ((index < 0) || (index >= numLegs())) { + throw sg_range_exception("index out of range", "FlightPlan::legAtIndex"); + } + + return _legs[index]; +} + +int FlightPlan::findLegIndex(const Leg *l) const +{ + for (unsigned int i=0; i<_legs.size(); ++i) { + if (_legs[i] == l) { + return i; + } + } + + return -1; +} + +void FlightPlan::setDeparture(FGAirport* apt) +{ + if (apt == _departure) { + return; + } + + _departure = apt; + _departureRunway = NULL; + setSID((SID*)NULL); + + if (_delegate) { + _delegate->runDepartureChanged(); + } +} + +void FlightPlan::setDeparture(FGRunway* rwy) +{ + if (_departureRunway == rwy) { + return; + } + + _departureRunway = rwy; + if (rwy->airport() != _departure) { + _departure = rwy->airport(); + setSID((SID*)NULL); + } + + if (_delegate) { + _delegate->runDepartureChanged(); + } +} + +void FlightPlan::setSID(SID* sid, const std::string& transition) +{ + if (sid == _sid) { + return; + } + + _sid = sid; + _sidTransition = transition; + + if (_delegate) { + _delegate->runDepartureChanged(); + } +} + +void FlightPlan::setSID(Transition* trans) +{ + if (!trans) { + setSID((SID*) NULL); + return; + } + + if (trans->parent()->type() != PROCEDURE_SID) + throw sg_exception("FlightPlan::setSID: transition does not belong to a SID"); + + setSID((SID*) trans->parent(), trans->ident()); +} + +Transition* FlightPlan::sidTransition() const +{ + if (!_sid || _sidTransition.empty()) { + return NULL; + } + + return _sid->findTransitionByName(_sidTransition); +} + +void FlightPlan::setDestination(FGAirport* apt) +{ + if (apt == _destination) { + return; + } + + _destination = apt; + _destinationRunway = NULL; + setSTAR((STAR*)NULL); + + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +void FlightPlan::setDestination(FGRunway* rwy) +{ + if (_destinationRunway == rwy) { + return; + } + + _destinationRunway = rwy; + if (_destination != rwy->airport()) { + _destination = rwy->airport(); + setSTAR((STAR*)NULL); + } + + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +void FlightPlan::setSTAR(STAR* star, const std::string& transition) +{ + if (_star == star) { + return; + } + + _star = star; + _starTransition = transition; + + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +void FlightPlan::setSTAR(Transition* trans) +{ + if (!trans) { + setSTAR((STAR*) NULL); + return; + } + + if (trans->parent()->type() != PROCEDURE_STAR) + throw sg_exception("FlightPlan::setSTAR: transition does not belong to a STAR"); + + setSTAR((STAR*) trans->parent(), trans->ident()); +} + +Transition* FlightPlan::starTransition() const +{ + if (!_star || _starTransition.empty()) { + return NULL; + } + + return _star->findTransitionByName(_starTransition); +} + +void FlightPlan::setApproach(flightgear::Approach *app) +{ + if (_approach == app) { + return; + } + + _approach = app; + if (app) { + // keep runway + airport in sync + if (_destinationRunway != _approach->runway()) { + _destinationRunway = _approach->runway(); + } + + if (_destination != _destinationRunway->airport()) { + _destination = _destinationRunway->airport(); + } + } + + if (_delegate) { + _delegate->runArrivalChanged(); + } +} + +bool FlightPlan::save(const SGPath& path) +{ + SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); + try { + SGPropertyNode_ptr d(new SGPropertyNode); + d->setIntValue("version", 2); + + if (_departure) { + d->setStringValue("departure/airport", _departure->ident()); + if (_sid) { + d->setStringValue("departure/sid", _sid->ident()); + } + + if (_departureRunway) { + d->setStringValue("departure/runway", _departureRunway->ident()); + } + } + + if (_destination) { + d->setStringValue("destination/airport", _destination->ident()); + if (_star) { + d->setStringValue("destination/star", _star->ident()); + } + + if (_approach) { + d->setStringValue("destination/approach", _approach->ident()); + } + + //d->setStringValue("destination/transition", destination->getStringValue("transition")); + + if (_destinationRunway) { + d->setStringValue("destination/runway", _destinationRunway->ident()); + } + } + + // route nodes + SGPropertyNode* routeNode = d->getChild("route", 0, true); + for (unsigned int i=0; i<_legs.size(); ++i) { + Waypt* wpt = _legs[i]->waypoint(); + wpt->saveAsNode(routeNode->getChild("wp", i, true)); + } // of waypoint iteration + writeProperties(path.str(), d, true /* write-all */); + return true; + } catch (sg_exception& e) { + SG_LOG(SG_IO, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage()); + return false; + } +} + +bool FlightPlan::load(const SGPath& path) +{ + if (!path.exists()) + { + SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str() + << "'. The file does not exist."); + return false; + } + + SGPropertyNode_ptr routeData(new SGPropertyNode); + SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str()); + + bool Status = false; + try { + readProperties(path.str(), routeData); + } catch (sg_exception& ) { + // if XML parsing fails, the file might be simple textual list of waypoints + Status = loadPlainTextRoute(path); + routeData = 0; + } + + if (routeData.valid()) + { + try { + int version = routeData->getIntValue("version", 1); + if (version == 1) { + loadVersion1XMLRoute(routeData); + } else if (version == 2) { + loadVersion2XMLRoute(routeData); + } else { + throw sg_io_exception("unsupported XML route version"); + } + Status = true; + } catch (sg_exception& e) { + SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin() + << "'. " << e.getMessage()); + Status = false; + } + } + + rebuildLegData(); + if (_delegate) { + _delegate->runWaypointsChanged(); + } + + return Status; +} + +void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData) +{ + // departure nodes + SGPropertyNode* dep = routeData->getChild("departure"); + if (dep) { + string depIdent = dep->getStringValue("airport"); + setDeparture((FGAirport*) fgFindAirportID(depIdent)); + if (_departure) { + if (dep->hasChild("runway")) { + setDeparture(_departure->getRunwayByIdent(dep->getStringValue("runway"))); + } + + if (dep->hasChild("sid")) { + setSID(_departure->findSIDWithIdent(dep->getStringValue("sid"))); + } + // departure->setStringValue("transition", dep->getStringValue("transition")); + } + } + + // destination + SGPropertyNode* dst = routeData->getChild("destination"); + if (dst) { + setDestination((FGAirport*) fgFindAirportID(dst->getStringValue("airport"))); + if (_destination) { + if (dst->hasChild("runway")) { + setDestination(_destination->getRunwayByIdent(dst->getStringValue("runway"))); + } + + if (dst->hasChild("star")) { + setSTAR(_destination->findSTARWithIdent(dst->getStringValue("star"))); + } + + if (dst->hasChild("approach")) { + setApproach(_destination->findApproachWithIdent(dst->getStringValue("approach"))); + } + } + + // destination->setStringValue("transition", dst->getStringValue("transition")); + } + + // alternate + SGPropertyNode* alt = routeData->getChild("alternate"); + if (alt) { + //alternate->setStringValue(alt->getStringValue("airport")); + } + + // cruise + SGPropertyNode* crs = routeData->getChild("cruise"); + if (crs) { + // cruise->setDoubleValue("speed-kts", crs->getDoubleValue("speed-kts")); + // cruise->setDoubleValue("mach", crs->getDoubleValue("mach")); + // cruise->setDoubleValue("altitude-ft", crs->getDoubleValue("altitude-ft")); + } // of cruise data loading + +} + +void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData) +{ + loadXMLRouteHeader(routeData); + + // route nodes + _legs.clear(); + SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); + for (int i=0; inChildren(); ++i) { + SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); + Leg* l = new Leg(this, Waypt::createFromProperties(NULL, wpNode)); + _legs.push_back(l); + } // of route iteration +} + +void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData) +{ + loadXMLRouteHeader(routeData); + + // _legs nodes + _legs.clear(); + SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); + for (int i=0; inChildren(); ++i) { + SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); + Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode)); + _legs.push_back(l); + } // of route iteration + +} + +WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP) +{ + SGGeod lastPos; + if (!_legs.empty()) { + lastPos = _legs.back()->waypoint()->position(); + } else if (_departure) { + lastPos = _departure->geod(); + } + + WayptRef w; + string ident(aWP->getStringValue("ident")); + if (aWP->hasChild("longitude-deg")) { + // explicit longitude/latitude + w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"), + aWP->getDoubleValue("latitude-deg")), ident, NULL); + + } else { + string nid = aWP->getStringValue("navid", ident.c_str()); + FGPositionedRef p = FGPositioned::findClosestWithIdent(nid, lastPos); + if (!p) { + throw sg_io_exception("bad route file, unknown navid:" + nid); + } + + SGGeod pos(p->geod()); + if (aWP->hasChild("offset-nm") && aWP->hasChild("offset-radial")) { + double radialDeg = aWP->getDoubleValue("offset-radial"); + // convert magnetic radial to a true radial! + radialDeg += magvarDegAt(pos); + double offsetNm = aWP->getDoubleValue("offset-nm"); + double az2; + SGGeodesy::direct(p->geod(), radialDeg, offsetNm * SG_NM_TO_METER, pos, az2); + } + + w = new BasicWaypt(pos, ident, NULL); + } + + double altFt = aWP->getDoubleValue("altitude-ft", -9999.9); + if (altFt > -9990.0) { + w->setAltitude(altFt, RESTRICT_AT); + } + + return w; +} + +bool FlightPlan::loadPlainTextRoute(const SGPath& path) +{ + try { + sg_gzifstream in(path.str().c_str()); + if (!in.is_open()) { + throw sg_io_exception("Cannot open file for reading."); + } + + _legs.clear(); + while (!in.eof()) { + string line; + getline(in, line, '\n'); + // trim CR from end of line, if found + if (line[line.size() - 1] == '\r') { + line.erase(line.size() - 1, 1); + } + + line = simgear::strutils::strip(line); + if (line.empty() || (line[0] == '#')) { + continue; // ignore empty/comment lines + } + + WayptRef w = waypointFromString(line); + if (!w) { + throw sg_io_exception("Failed to create waypoint from line '" + line + "'."); + } + + _legs.push_back(new Leg(this, w)); + } // of line iteration + } catch (sg_exception& e) { + SG_LOG(SG_IO, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage()); + _legs.clear(); + return false; + } + + return true; +} + +double FlightPlan::magvarDegAt(const SGGeod& pos) const +{ + double jd = globals->get_time_params()->getJD(); + return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES; +} + +WayptRef FlightPlan::waypointFromString(const string& tgt ) +{ + string target(boost::to_upper_copy(tgt)); + WayptRef wpt; + + // extract altitude + double altFt = 0.0; + RouteRestriction altSetting = RESTRICT_NONE; + + size_t pos = target.find( '@' ); + if ( pos != string::npos ) { + altFt = atof( target.c_str() + pos + 1 ); + target = target.substr( 0, pos ); + if ( !strcmp(fgGetString("/sim/startup/units"), "meter") ) + altFt *= SG_METER_TO_FEET; + altSetting = RESTRICT_AT; + } + + // check for lon,lat + pos = target.find( ',' ); + if ( pos != string::npos ) { + double lon = atof( target.substr(0, pos).c_str()); + double lat = atof( target.c_str() + pos + 1); + char buf[32]; + char ew = (lon < 0.0) ? 'W' : 'E'; + char ns = (lat < 0.0) ? 'S' : 'N'; + snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat)); + + wpt = new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL); + if (altSetting != RESTRICT_NONE) { + wpt->setAltitude(altFt, altSetting); + } + return wpt; + } + + SGGeod basePosition; + if (_legs.empty()) { + // route is empty, use current position + basePosition = globals->get_aircraft_position(); + } else { + basePosition = _legs.back()->waypoint()->position(); + } + + string_list pieces(simgear::strutils::split(target, "/")); + FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition); + if (!p) { + SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front()); + return NULL; + } + + double magvar = magvarDegAt(basePosition); + + if (pieces.size() == 1) { + wpt = new NavaidWaypoint(p, NULL); + } else if (pieces.size() == 3) { + // navaid/radial/distance-nm notation + double radial = atof(pieces[1].c_str()), + distanceNm = atof(pieces[2].c_str()); + radial += magvar; + wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); + } else if (pieces.size() == 2) { + FGAirport* apt = dynamic_cast(p.ptr()); + if (!apt) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "Waypoint is not an airport:" << pieces.front()); + return NULL; + } + + if (!apt->hasRunwayWithIdent(pieces[1])) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]); + return NULL; + } + + FGRunway* runway = apt->getRunwayByIdent(pieces[1]); + wpt = new NavaidWaypoint(runway, NULL); + } else if (pieces.size() == 4) { + // navid/radial/navid/radial notation + FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition); + if (!p2) { + SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]); + return NULL; + } + + double r1 = atof(pieces[1].c_str()), + r2 = atof(pieces[3].c_str()); + r1 += magvar; + r2 += magvar; + + SGGeod intersection; + bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection); + if (!ok) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "no valid intersection for:" << target); + return NULL; + } + + std::string name = p->ident() + "-" + p2->ident(); + wpt = new BasicWaypt(intersection, name, NULL); + } + + if (!wpt) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target); + return NULL; + } + + if (altSetting != RESTRICT_NONE) { + wpt->setAltitude(altFt, altSetting); + } + return wpt; +} + +FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) : + _parent(owner), + _speedRestrict(RESTRICT_NONE), + _altRestrict(RESTRICT_NONE), + _waypt(wpt) +{ + if (!wpt.valid()) { + throw sg_exception("can't create FlightPlan::Leg without underlying waypoint"); + } + _speed = _altitudeFt = 0; +} + +FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const +{ + Leg* c = new Leg(owner, _waypt); +// clone local data + c->_speed = _speed; + c->_speedRestrict = _speedRestrict; + c->_altitudeFt = _altitudeFt; + c->_altRestrict = _altRestrict; + + return c; +} + +FlightPlan::Leg* FlightPlan::Leg::nextLeg() const +{ + return _parent->legAtIndex(index() + 1); +} + +unsigned int FlightPlan::Leg::index() const +{ + return _parent->findLegIndex(this); +} + +int FlightPlan::Leg::altitudeFt() const +{ + if (_altRestrict != RESTRICT_NONE) { + return _altitudeFt; + } + + return _waypt->altitudeFt(); +} + +int FlightPlan::Leg::speed() const +{ + if (_speedRestrict != RESTRICT_NONE) { + return _speed; + } + + return _waypt->speed(); +} + +int FlightPlan::Leg::speedKts() const +{ + return speed(); +} + +double FlightPlan::Leg::speedMach() const +{ + if (!isMachRestrict(_speedRestrict)) { + return 0.0; + } + + return -(_speed / 100.0); +} + +RouteRestriction FlightPlan::Leg::altitudeRestriction() const +{ + if (_altRestrict != RESTRICT_NONE) { + return _altRestrict; + } + + return _waypt->altitudeRestriction(); +} + +RouteRestriction FlightPlan::Leg::speedRestriction() const +{ + if (_speedRestrict != RESTRICT_NONE) { + return _speedRestrict; + } + + return _waypt->speedRestriction(); +} + +void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed) +{ + _speedRestrict = ty; + if (isMachRestrict(ty)) { + _speed = (speed * -100); + } else { + _speed = speed; + } +} + +void FlightPlan::Leg::setAltitude(RouteRestriction ty, int altFt) +{ + _altRestrict = ty; + _altitudeFt = altFt; +} + +double FlightPlan::Leg::courseDeg() const +{ + return _courseDeg; +} + +double FlightPlan::Leg::distanceNm() const +{ + return _pathDistance; +} + +double FlightPlan::Leg::distanceAlongRoute() const +{ + return _distanceAlongPath; +} + +void FlightPlan::rebuildLegData() +{ + _totalDistance = 0.0; + int lastLeg = static_cast(_legs.size()) - 1; + for (int l=0; l crsDist = + next->waypoint()->courseAndDistanceFrom(cur->waypoint()->position()); + _legs[l]->_courseDeg = crsDist.first; + _legs[l]->_pathDistance = crsDist.second * SG_METER_TO_NM; + _legs[l]->_distanceAlongPath = _totalDistance; + _totalDistance += crsDist.second * SG_METER_TO_NM; + } // of legs iteration +} + +void FlightPlan::setDelegate(Delegate* d) +{ + // wrap any existing delegate(s) in the new one + d->_inner = _delegate; + _delegate = d; +} + +void FlightPlan::removeDelegate(Delegate* d) +{ + if (d == _delegate) { + _delegate = _delegate->_inner; + } else if (_delegate) { + _delegate->removeInner(d); + } +} + +FlightPlan::Delegate::Delegate() : + _inner(NULL) +{ + +} + +FlightPlan::Delegate::~Delegate() +{ + +} + +void FlightPlan::Delegate::removeInner(Delegate* d) +{ + if (!_inner) { + return; + } + + if (_inner == d) { + // replace with grand-child + _inner = d->_inner; + } else { // recurse downwards + _inner->removeInner(d); + } +} + +void FlightPlan::Delegate::runDepartureChanged() +{ + if (_inner) _inner->runDepartureChanged(); + departureChanged(); +} + +void FlightPlan::Delegate::runArrivalChanged() +{ + if (_inner) _inner->runArrivalChanged(); + arrivalChanged(); +} + +void FlightPlan::Delegate::runWaypointsChanged() +{ + if (_inner) _inner->runWaypointsChanged(); + waypointsChanged(); +} + +void FlightPlan::Delegate::runCurrentWaypointChanged() +{ + if (_inner) _inner->runCurrentWaypointChanged(); + currentWaypointChanged(); +} + +} // of namespace flightgear diff --git a/src/Navaids/FlightPlan.hxx b/src/Navaids/FlightPlan.hxx new file mode 100644 index 000000000..b46dae03c --- /dev/null +++ b/src/Navaids/FlightPlan.hxx @@ -0,0 +1,244 @@ +/** + * FlightPlan.hxx - defines a full flight-plan object, including + * departure, cruise, arrival information and waypoints + */ + +// Written by James Turner, started 2012. +// +// Copyright (C) 2012 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef FG_FLIGHTPLAN_HXX +#define FG_FLIGHTPLAN_HXX + +#include +#include + +typedef SGSharedPtr FGAirportRef; + +namespace flightgear +{ + +class Transition; + +class FlightPlan : public RouteBase +{ +public: + FlightPlan(); + virtual ~FlightPlan(); + + virtual std::string ident() const; + void setIdent(const std::string& s); + + FlightPlan* clone(const std::string& newIdent = std::string()) const; + + /** + * flight-plan leg encapsulation + */ + class Leg + { + public: + FlightPlan* owner() const + { return _parent; } + + Waypt* waypoint() const + { return _waypt; } + + // reutrn the next leg after this one + Leg* nextLeg() const; + + unsigned int index() const; + + int altitudeFt() const; + int speed() const; + + int speedKts() const; + double speedMach() const; + + RouteRestriction altitudeRestriction() const; + RouteRestriction speedRestriction() const; + + void setSpeed(RouteRestriction ty, double speed); + void setAltitude(RouteRestriction ty, int altFt); + + double courseDeg() const; + double distanceNm() const; + double distanceAlongRoute() const; + private: + friend class FlightPlan; + + Leg(FlightPlan* owner, WayptRef wpt); + + Leg* cloneFor(FlightPlan* owner) const; + + FlightPlan* _parent; + RouteRestriction _speedRestrict, _altRestrict; + int _speed; + int _altitudeFt; + WayptRef _waypt; + /// length of this leg following the flown path + mutable double _pathDistance; + mutable double _courseDeg; + /// total distance of this leg from departure point + mutable double _distanceAlongPath; + }; + + class Delegate + { + public: + virtual ~Delegate(); + + virtual void departureChanged() { } + virtual void arrivalChanged() { } + virtual void waypointsChanged() { } + + virtual void currentWaypointChanged() { } + + protected: + Delegate(); + + private: + void removeInner(Delegate* d); + + void runDepartureChanged(); + void runArrivalChanged(); + void runWaypointsChanged(); + void runCurrentWaypointChanged(); + + friend class FlightPlan; + + Delegate* _inner; + }; + + Leg* insertWayptAtIndex(Waypt* aWpt, int aIndex); + void insertWayptsAtIndex(const WayptVec& wps, int aIndex); + + void deleteIndex(int index); + void clear(); + int clearWayptsWithFlag(WayptFlag flag); + + int currentIndex() const + { return _currentIndex; } + + void setCurrentIndex(int index); + + Leg* currentLeg() const; + Leg* nextLeg() const; + Leg* previousLeg() const; + + int numLegs() const + { return _legs.size(); } + + Leg* legAtIndex(int index) const; + int findLegIndex(const Leg* l) const; + + int findWayptIndex(const SGGeod& aPos) const; + + bool load(const SGPath& p); + bool save(const SGPath& p); + + FGAirportRef departureAirport() const + { return _departure; } + + FGAirportRef destinationAirport() const + { return _destination; } + + FGRunway* departureRunway() const + { return _departureRunway; } + + FGRunway* destinationRunway() const + { return _destinationRunway; } + + Approach* approach() const + { return _approach; } + + void setDeparture(FGAirport* apt); + void setDeparture(FGRunway* rwy); + + SID* sid() const + { return _sid; } + + Transition* sidTransition() const; + + void setSID(SID* sid, const std::string& transition = std::string()); + + void setSID(Transition* sidWithTrans); + + void setDestination(FGAirport* apt); + void setDestination(FGRunway* rwy); + + /** + * note setting an approach will implicitly update the destination + * airport and runway to match + */ + void setApproach(Approach* app); + + STAR* star() const + { return _star; } + + Transition* starTransition() const; + + void setSTAR(STAR* star, const std::string& transition = std::string()); + + void setSTAR(Transition* starWithTrans); + + double totalDistanceNm() const + { return _totalDistance; } + + /** + * Create a WayPoint from a string in the following format: + * - simple identifier + * - decimal-lon,decimal-lat + * - airport-id/runway-id + * - navaid/radial-deg/offset-nm + */ + WayptRef waypointFromString(const std::string& target); + + void setDelegate(Delegate* d); + void removeDelegate(Delegate* d); +private: + + bool loadPlainTextRoute(const SGPath& path); + + void loadVersion1XMLRoute(SGPropertyNode_ptr routeData); + void loadVersion2XMLRoute(SGPropertyNode_ptr routeData); + void loadXMLRouteHeader(SGPropertyNode_ptr routeData); + WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP); + + double magvarDegAt(const SGGeod& pos) const; + + std::string _ident; + int _currentIndex; + + FGAirportRef _departure, _destination; + FGRunway* _departureRunway, *_destinationRunway; + SID* _sid; + STAR* _star; + Approach* _approach; + std::string _sidTransition, _starTransition; + + double _totalDistance; + void rebuildLegData(); + + typedef std::vector LegVec; + LegVec _legs; + + Delegate* _delegate; +}; + +} // of namespace flightgear + +#endif // of FG_FLIGHTPLAN_HXX diff --git a/src/Navaids/route.cxx b/src/Navaids/route.cxx index 38796e9df..b52a3f574 100644 --- a/src/Navaids/route.cxx +++ b/src/Navaids/route.cxx @@ -383,1003 +383,5 @@ void RouteBase::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt) "\n\t" << ex.getMessage()); } } - -//////////////////////////////////////////////////////////////////////////// - -FlightPlan::FlightPlan() : - _currentIndex(-1), - _departureRunway(NULL), - _destinationRunway(NULL), - _sid(NULL), - _star(NULL), - _approach(NULL), - _delegate(NULL) -{ - -} - -FlightPlan::~FlightPlan() -{ - -} - -FlightPlan* FlightPlan::clone(const string& newIdent) const -{ - FlightPlan* c = new FlightPlan(); - c->_ident = newIdent.empty() ? _ident : newIdent; - -// copy destination / departure data. - c->setDeparture(_departure); - c->setDeparture(_departureRunway); - - if (_approach) { - c->setApproach(_approach); - } else if (_destinationRunway) { - c->setDestination(_destinationRunway); - } else if (_destination) { - c->setDestination(_destination); - } - - c->setSTAR(_star); - c->setSID(_sid); - -// copy legs - for (int l=0; l < numLegs(); ++l) { - c->_legs.push_back(_legs[l]->cloneFor(c)); - } - - return c; -} - -void FlightPlan::setIdent(const string& s) -{ - _ident = s; -} - -string FlightPlan::ident() const -{ - return _ident; -} - -FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex) -{ - if (!aWpt) { - return NULL; - } - - WayptVec wps; - wps.push_back(aWpt); - - int index = aIndex; - if ((aIndex == -1) || (aIndex > (int) _legs.size())) { - index = _legs.size(); - } - - insertWayptsAtIndex(wps, index); - return legAtIndex(aIndex); -} - -void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex) -{ - if (wps.empty()) { - return; - } - - int index = aIndex; - if ((aIndex == -1) || (aIndex > (int) _legs.size())) { - index = _legs.size(); - } - - LegVec::iterator it = _legs.begin(); - it += index; - - int endIndex = index + wps.size() - 1; - if (_currentIndex >= endIndex) { - _currentIndex += wps.size(); - } - - LegVec newLegs; - BOOST_FOREACH(WayptRef wp, wps) { - newLegs.push_back(new Leg(this, wp)); - } - - _legs.insert(it, newLegs.begin(), newLegs.end()); - rebuildLegData(); - - if (_delegate) { - _delegate->runWaypointsChanged(); - } -} - -void FlightPlan::deleteIndex(int aIndex) -{ - int index = aIndex; - if (aIndex < 0) { // negative indices count the the end - index = _legs.size() + index; - } - - if ((index < 0) || (index >= numLegs())) { - SG_LOG(SG_AUTOPILOT, SG_WARN, "removeAtIndex with invalid index:" << aIndex); - return; - } - LegVec::iterator it = _legs.begin(); - it += index; - Leg* l = *it; - _legs.erase(it); - delete l; - - bool curChanged = false; - if (_currentIndex == index) { - // current waypoint was removed - curChanged = true; - } else if (_currentIndex > index) { - --_currentIndex; // shift current index down if necessary - } - - rebuildLegData(); - if (_delegate) { - _delegate->runWaypointsChanged(); - if (curChanged) { - _delegate->runCurrentWaypointChanged(); - } - } -} - -void FlightPlan::clear() -{ - _currentIndex = -1; - BOOST_FOREACH(Leg* l, _legs) { - delete l; - } - _legs.clear(); - rebuildLegData(); - if (_delegate) { - _delegate->runDepartureChanged(); - _delegate->runArrivalChanged(); - _delegate->runWaypointsChanged(); - _delegate->runCurrentWaypointChanged(); - } -} - -int FlightPlan::clearWayptsWithFlag(WayptFlag flag) -{ - int count = 0; - for (unsigned int i=0; i<_legs.size(); ++i) { - Leg* l = _legs[i]; - if (!l->waypoint()->flag(flag)) { - continue; - } - - // okay, we're going to clear this leg - ++count; - if (_currentIndex > (int) i) { - --_currentIndex; - } - - delete l; - LegVec::iterator it = _legs.begin(); - it += i; - _legs.erase(it); - } - - if (count == 0) { - return 0; // nothing was cleared, don't fire the delegate - } - - rebuildLegData(); - if (_delegate) { - _delegate->runWaypointsChanged(); - _delegate->runCurrentWaypointChanged(); - } - - return count; -} - -void FlightPlan::setCurrentIndex(int index) -{ - if ((index < -1) || (index >= numLegs())) { - throw sg_range_exception("invalid leg index", "FlightPlan::setCurrentIndex"); - } - - if (index == _currentIndex) { - return; - } - - _currentIndex = index; - if (_delegate) { - _delegate->runCurrentWaypointChanged(); - } -} - -int FlightPlan::findWayptIndex(const SGGeod& aPos) const -{ - for (int i=0; iwaypoint()->matches(aPos)) { - return i; - } - } - - return -1; -} - -FlightPlan::Leg* FlightPlan::currentLeg() const -{ - if ((_currentIndex < 0) || (_currentIndex >= numLegs())) - return NULL; - return legAtIndex(_currentIndex); -} - -FlightPlan::Leg* FlightPlan::previousLeg() const -{ - if (_currentIndex == 0) { - return NULL; - } - - return legAtIndex(_currentIndex - 1); -} - -FlightPlan::Leg* FlightPlan::nextLeg() const -{ - if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) { - return NULL; - } - - return legAtIndex(_currentIndex + 1); -} - -FlightPlan::Leg* FlightPlan::legAtIndex(int index) const -{ - if ((index < 0) || (index >= numLegs())) { - throw sg_range_exception("index out of range", "FlightPlan::legAtIndex"); - } - - return _legs[index]; -} - -int FlightPlan::findLegIndex(const Leg *l) const -{ - for (unsigned int i=0; i<_legs.size(); ++i) { - if (_legs[i] == l) { - return i; - } - } - - return -1; -} - -void FlightPlan::setDeparture(FGAirport* apt) -{ - if (apt == _departure) { - return; - } - - _departure = apt; - _departureRunway = NULL; - setSID((SID*)NULL); - - if (_delegate) { - _delegate->runDepartureChanged(); - } -} - -void FlightPlan::setDeparture(FGRunway* rwy) -{ - if (_departureRunway == rwy) { - return; - } - - _departureRunway = rwy; - if (rwy->airport() != _departure) { - _departure = rwy->airport(); - setSID((SID*)NULL); - } - - if (_delegate) { - _delegate->runDepartureChanged(); - } -} - -void FlightPlan::setSID(SID* sid, const std::string& transition) -{ - if (sid == _sid) { - return; - } - - _sid = sid; - _sidTransition = transition; - - if (_delegate) { - _delegate->runDepartureChanged(); - } -} - -void FlightPlan::setSID(Transition* trans) -{ - if (!trans) { - setSID((SID*) NULL); - return; - } - - if (trans->parent()->type() != PROCEDURE_SID) - throw sg_exception("FlightPlan::setSID: transition does not belong to a SID"); - - setSID((SID*) trans->parent(), trans->ident()); -} - -Transition* FlightPlan::sidTransition() const -{ - if (!_sid || _sidTransition.empty()) { - return NULL; - } - - return _sid->findTransitionByName(_sidTransition); -} - -void FlightPlan::setDestination(FGAirport* apt) -{ - if (apt == _destination) { - return; - } - - _destination = apt; - _destinationRunway = NULL; - setSTAR((STAR*)NULL); - - if (_delegate) { - _delegate->runArrivalChanged(); - } -} - -void FlightPlan::setDestination(FGRunway* rwy) -{ - if (_destinationRunway == rwy) { - return; - } - - _destinationRunway = rwy; - if (_destination != rwy->airport()) { - _destination = rwy->airport(); - setSTAR((STAR*)NULL); - } - - if (_delegate) { - _delegate->runArrivalChanged(); - } -} - -void FlightPlan::setSTAR(STAR* star, const std::string& transition) -{ - if (_star == star) { - return; - } - - _star = star; - _starTransition = transition; - - if (_delegate) { - _delegate->runArrivalChanged(); - } -} - -void FlightPlan::setSTAR(Transition* trans) -{ - if (!trans) { - setSTAR((STAR*) NULL); - return; - } - - if (trans->parent()->type() != PROCEDURE_STAR) - throw sg_exception("FlightPlan::setSTAR: transition does not belong to a STAR"); - - setSTAR((STAR*) trans->parent(), trans->ident()); -} - -Transition* FlightPlan::starTransition() const -{ - if (!_star || _starTransition.empty()) { - return NULL; - } - - return _star->findTransitionByName(_starTransition); -} - -void FlightPlan::setApproach(flightgear::Approach *app) -{ - if (_approach == app) { - return; - } - - _approach = app; - if (app) { - // keep runway + airport in sync - if (_destinationRunway != _approach->runway()) { - _destinationRunway = _approach->runway(); - } - - if (_destination != _destinationRunway->airport()) { - _destination = _destinationRunway->airport(); - } - } - - if (_delegate) { - _delegate->runArrivalChanged(); - } -} - -bool FlightPlan::save(const SGPath& path) -{ - SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); - try { - SGPropertyNode_ptr d(new SGPropertyNode); - d->setIntValue("version", 2); - - if (_departure) { - d->setStringValue("departure/airport", _departure->ident()); - if (_sid) { - d->setStringValue("departure/sid", _sid->ident()); - } - - if (_departureRunway) { - d->setStringValue("departure/runway", _departureRunway->ident()); - } - } - - if (_destination) { - d->setStringValue("destination/airport", _destination->ident()); - if (_star) { - d->setStringValue("destination/star", _star->ident()); - } - - if (_approach) { - d->setStringValue("destination/approach", _approach->ident()); - } - - //d->setStringValue("destination/transition", destination->getStringValue("transition")); - - if (_destinationRunway) { - d->setStringValue("destination/runway", _destinationRunway->ident()); - } - } - - // route nodes - SGPropertyNode* routeNode = d->getChild("route", 0, true); - for (unsigned int i=0; i<_legs.size(); ++i) { - Waypt* wpt = _legs[i]->waypoint(); - wpt->saveAsNode(routeNode->getChild("wp", i, true)); - } // of waypoint iteration - writeProperties(path.str(), d, true /* write-all */); - return true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage()); - return false; - } -} - -bool FlightPlan::load(const SGPath& path) -{ - if (!path.exists()) - { - SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str() - << "'. The file does not exist."); - return false; - } - - SGPropertyNode_ptr routeData(new SGPropertyNode); - SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str()); - - bool Status = false; - try { - readProperties(path.str(), routeData); - } catch (sg_exception& ) { - // if XML parsing fails, the file might be simple textual list of waypoints - Status = loadPlainTextRoute(path); - routeData = 0; - } - - if (routeData.valid()) - { - try { - int version = routeData->getIntValue("version", 1); - if (version == 1) { - loadVersion1XMLRoute(routeData); - } else if (version == 2) { - loadVersion2XMLRoute(routeData); - } else { - throw sg_io_exception("unsupported XML route version"); - } - Status = true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin() - << "'. " << e.getMessage()); - Status = false; - } - } - - rebuildLegData(); - if (_delegate) { - _delegate->runWaypointsChanged(); - } - - return Status; -} - -void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData) -{ - // departure nodes - SGPropertyNode* dep = routeData->getChild("departure"); - if (dep) { - string depIdent = dep->getStringValue("airport"); - setDeparture((FGAirport*) fgFindAirportID(depIdent)); - if (_departure) { - if (dep->hasChild("runway")) { - setDeparture(_departure->getRunwayByIdent(dep->getStringValue("runway"))); - } - - if (dep->hasChild("sid")) { - setSID(_departure->findSIDWithIdent(dep->getStringValue("sid"))); - } - // departure->setStringValue("transition", dep->getStringValue("transition")); - } - } - - // destination - SGPropertyNode* dst = routeData->getChild("destination"); - if (dst) { - setDestination((FGAirport*) fgFindAirportID(dst->getStringValue("airport"))); - if (_destination) { - if (dst->hasChild("runway")) { - setDestination(_destination->getRunwayByIdent(dst->getStringValue("runway"))); - } - - if (dst->hasChild("star")) { - setSTAR(_destination->findSTARWithIdent(dst->getStringValue("star"))); - } - - if (dst->hasChild("approach")) { - setApproach(_destination->findApproachWithIdent(dst->getStringValue("approach"))); - } - } - - // destination->setStringValue("transition", dst->getStringValue("transition")); - } - - // alternate - SGPropertyNode* alt = routeData->getChild("alternate"); - if (alt) { - //alternate->setStringValue(alt->getStringValue("airport")); - } - - // cruise - SGPropertyNode* crs = routeData->getChild("cruise"); - if (crs) { - // cruise->setDoubleValue("speed-kts", crs->getDoubleValue("speed-kts")); - // cruise->setDoubleValue("mach", crs->getDoubleValue("mach")); - // cruise->setDoubleValue("altitude-ft", crs->getDoubleValue("altitude-ft")); - } // of cruise data loading - -} - -void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData) -{ - loadXMLRouteHeader(routeData); - - // route nodes - _legs.clear(); - SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); - for (int i=0; inChildren(); ++i) { - SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - Leg* l = new Leg(this, Waypt::createFromProperties(NULL, wpNode)); - _legs.push_back(l); - } // of route iteration -} - -void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData) -{ - loadXMLRouteHeader(routeData); - - // _legs nodes - _legs.clear(); - SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); - for (int i=0; inChildren(); ++i) { - SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode)); - _legs.push_back(l); - } // of route iteration - -} - -WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP) -{ - SGGeod lastPos; - if (!_legs.empty()) { - lastPos = _legs.back()->waypoint()->position(); - } else if (_departure) { - lastPos = _departure->geod(); - } - - WayptRef w; - string ident(aWP->getStringValue("ident")); - if (aWP->hasChild("longitude-deg")) { - // explicit longitude/latitude - w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"), - aWP->getDoubleValue("latitude-deg")), ident, NULL); - - } else { - string nid = aWP->getStringValue("navid", ident.c_str()); - FGPositionedRef p = FGPositioned::findClosestWithIdent(nid, lastPos); - if (!p) { - throw sg_io_exception("bad route file, unknown navid:" + nid); - } - - SGGeod pos(p->geod()); - if (aWP->hasChild("offset-nm") && aWP->hasChild("offset-radial")) { - double radialDeg = aWP->getDoubleValue("offset-radial"); - // convert magnetic radial to a true radial! - radialDeg += magvarDegAt(pos); - double offsetNm = aWP->getDoubleValue("offset-nm"); - double az2; - SGGeodesy::direct(p->geod(), radialDeg, offsetNm * SG_NM_TO_METER, pos, az2); - } - - w = new BasicWaypt(pos, ident, NULL); - } - - double altFt = aWP->getDoubleValue("altitude-ft", -9999.9); - if (altFt > -9990.0) { - w->setAltitude(altFt, RESTRICT_AT); - } - - return w; -} - -bool FlightPlan::loadPlainTextRoute(const SGPath& path) -{ - try { - sg_gzifstream in(path.str().c_str()); - if (!in.is_open()) { - throw sg_io_exception("Cannot open file for reading."); - } - - _legs.clear(); - while (!in.eof()) { - string line; - getline(in, line, '\n'); - // trim CR from end of line, if found - if (line[line.size() - 1] == '\r') { - line.erase(line.size() - 1, 1); - } - - line = simgear::strutils::strip(line); - if (line.empty() || (line[0] == '#')) { - continue; // ignore empty/comment lines - } - - WayptRef w = waypointFromString(line); - if (!w) { - throw sg_io_exception("Failed to create waypoint from line '" + line + "'."); - } - - _legs.push_back(new Leg(this, w)); - } // of line iteration - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage()); - _legs.clear(); - return false; - } - - return true; -} - -double FlightPlan::magvarDegAt(const SGGeod& pos) const -{ - double jd = globals->get_time_params()->getJD(); - return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES; -} - -WayptRef FlightPlan::waypointFromString(const string& tgt ) -{ - string target(boost::to_upper_copy(tgt)); - WayptRef wpt; - - // extract altitude - double altFt = 0.0; - RouteRestriction altSetting = RESTRICT_NONE; - - size_t pos = target.find( '@' ); - if ( pos != string::npos ) { - altFt = atof( target.c_str() + pos + 1 ); - target = target.substr( 0, pos ); - if ( !strcmp(fgGetString("/sim/startup/units"), "meter") ) - altFt *= SG_METER_TO_FEET; - altSetting = RESTRICT_AT; - } - - // check for lon,lat - pos = target.find( ',' ); - if ( pos != string::npos ) { - double lon = atof( target.substr(0, pos).c_str()); - double lat = atof( target.c_str() + pos + 1); - char buf[32]; - char ew = (lon < 0.0) ? 'W' : 'E'; - char ns = (lat < 0.0) ? 'S' : 'N'; - snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat)); - - wpt = new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL); - if (altSetting != RESTRICT_NONE) { - wpt->setAltitude(altFt, altSetting); - } - return wpt; - } - - SGGeod basePosition; - if (_legs.empty()) { - // route is empty, use current position - basePosition = globals->get_aircraft_position(); - } else { - basePosition = _legs.back()->waypoint()->position(); - } - - string_list pieces(simgear::strutils::split(target, "/")); - FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition); - if (!p) { - SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front()); - return NULL; - } - - double magvar = magvarDegAt(basePosition); - - if (pieces.size() == 1) { - wpt = new NavaidWaypoint(p, NULL); - } else if (pieces.size() == 3) { - // navaid/radial/distance-nm notation - double radial = atof(pieces[1].c_str()), - distanceNm = atof(pieces[2].c_str()); - radial += magvar; - wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); - } else if (pieces.size() == 2) { - FGAirport* apt = dynamic_cast(p.ptr()); - if (!apt) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "Waypoint is not an airport:" << pieces.front()); - return NULL; - } - - if (!apt->hasRunwayWithIdent(pieces[1])) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]); - return NULL; - } - - FGRunway* runway = apt->getRunwayByIdent(pieces[1]); - wpt = new NavaidWaypoint(runway, NULL); - } else if (pieces.size() == 4) { - // navid/radial/navid/radial notation - FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition); - if (!p2) { - SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]); - return NULL; - } - - double r1 = atof(pieces[1].c_str()), - r2 = atof(pieces[3].c_str()); - r1 += magvar; - r2 += magvar; - - SGGeod intersection; - bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection); - if (!ok) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "no valid intersection for:" << target); - return NULL; - } - - std::string name = p->ident() + "-" + p2->ident(); - wpt = new BasicWaypt(intersection, name, NULL); - } - - if (!wpt) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target); - return NULL; - } - - if (altSetting != RESTRICT_NONE) { - wpt->setAltitude(altFt, altSetting); - } - return wpt; -} - -FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) : - _parent(owner), - _speedRestrict(RESTRICT_NONE), - _altRestrict(RESTRICT_NONE), - _waypt(wpt) -{ - if (!wpt.valid()) { - throw sg_exception("can't create FlightPlan::Leg without underlying waypoint"); - } - _speed = _altitudeFt = 0; -} - -FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const -{ - Leg* c = new Leg(owner, _waypt); -// clone local data - c->_speed = _speed; - c->_speedRestrict = _speedRestrict; - c->_altitudeFt = _altitudeFt; - c->_altRestrict = _altRestrict; - - return c; -} - -FlightPlan::Leg* FlightPlan::Leg::nextLeg() const -{ - return _parent->legAtIndex(index() + 1); -} - -unsigned int FlightPlan::Leg::index() const -{ - return _parent->findLegIndex(this); -} - -int FlightPlan::Leg::altitudeFt() const -{ - if (_altRestrict != RESTRICT_NONE) { - return _altitudeFt; - } - - return _waypt->altitudeFt(); -} - -int FlightPlan::Leg::speed() const -{ - if (_speedRestrict != RESTRICT_NONE) { - return _speed; - } - - return _waypt->speed(); -} - -int FlightPlan::Leg::speedKts() const -{ - return speed(); -} - -double FlightPlan::Leg::speedMach() const -{ - if (!isMachRestrict(_speedRestrict)) { - return 0.0; - } - - return -(_speed / 100.0); -} - -RouteRestriction FlightPlan::Leg::altitudeRestriction() const -{ - if (_altRestrict != RESTRICT_NONE) { - return _altRestrict; - } - - return _waypt->altitudeRestriction(); -} - -RouteRestriction FlightPlan::Leg::speedRestriction() const -{ - if (_speedRestrict != RESTRICT_NONE) { - return _speedRestrict; - } - - return _waypt->speedRestriction(); -} - -void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed) -{ - _speedRestrict = ty; - if (isMachRestrict(ty)) { - _speed = (speed * -100); - } else { - _speed = speed; - } -} - -void FlightPlan::Leg::setAltitude(RouteRestriction ty, int altFt) -{ - _altRestrict = ty; - _altitudeFt = altFt; -} - -double FlightPlan::Leg::courseDeg() const -{ - return _courseDeg; -} - -double FlightPlan::Leg::distanceNm() const -{ - return _pathDistance; -} - -double FlightPlan::Leg::distanceAlongRoute() const -{ - return _distanceAlongPath; -} - -void FlightPlan::rebuildLegData() -{ - _totalDistance = 0.0; - int lastLeg = static_cast(_legs.size()) - 1; - for (int l=0; l crsDist = - next->waypoint()->courseAndDistanceFrom(cur->waypoint()->position()); - _legs[l]->_courseDeg = crsDist.first; - _legs[l]->_pathDistance = crsDist.second * SG_METER_TO_NM; - _legs[l]->_distanceAlongPath = _totalDistance; - _totalDistance += crsDist.second * SG_METER_TO_NM; - } // of legs iteration -} - -void FlightPlan::setDelegate(Delegate* d) -{ - // wrap any existing delegate(s) in the new one - d->_inner = _delegate; - _delegate = d; -} - -void FlightPlan::removeDelegate(Delegate* d) -{ - if (d == _delegate) { - _delegate = _delegate->_inner; - } else if (_delegate) { - _delegate->removeInner(d); - } -} - -FlightPlan::Delegate::Delegate() : - _inner(NULL) -{ - -} - -FlightPlan::Delegate::~Delegate() -{ - -} - -void FlightPlan::Delegate::removeInner(Delegate* d) -{ - if (!_inner) { - return; - } - - if (_inner == d) { - // replace with grand-child - _inner = d->_inner; - } else { // recurse downwards - _inner->removeInner(d); - } -} - -void FlightPlan::Delegate::runDepartureChanged() -{ - if (_inner) _inner->runDepartureChanged(); - departureChanged(); -} - -void FlightPlan::Delegate::runArrivalChanged() -{ - if (_inner) _inner->runArrivalChanged(); - arrivalChanged(); -} - -void FlightPlan::Delegate::runWaypointsChanged() -{ - if (_inner) _inner->runWaypointsChanged(); - waypointsChanged(); -} - -void FlightPlan::Delegate::runCurrentWaypointChanged() -{ - if (_inner) _inner->runCurrentWaypointChanged(); - currentWaypointChanged(); -} } // of namespace flightgear diff --git a/src/Navaids/route.hxx b/src/Navaids/route.hxx index 3ea83596f..33d45ce2a 100644 --- a/src/Navaids/route.hxx +++ b/src/Navaids/route.hxx @@ -39,10 +39,7 @@ // forward decls class FGPositioned; class SGPath; -class FGRunway; - -#include -typedef SGSharedPtr FGAirportRef; +class FGAirport; namespace flightgear { @@ -51,9 +48,6 @@ namespace flightgear class RouteBase; class Waypt; class NavdataVisitor; -class SID; -class STAR; -class Transition; typedef SGSharedPtr WayptRef; @@ -242,212 +236,7 @@ public: private: }; - -class FlightPlan : public RouteBase -{ -public: - FlightPlan(); - virtual ~FlightPlan(); - - virtual std::string ident() const; - void setIdent(const std::string& s); - - FlightPlan* clone(const std::string& newIdent = std::string()) const; - - /** - * flight-plan leg encapsulation - */ - class Leg - { - public: - FlightPlan* owner() const - { return _parent; } - - Waypt* waypoint() const - { return _waypt; } - - // reutrn the next leg after this one - Leg* nextLeg() const; - - unsigned int index() const; - - int altitudeFt() const; - int speed() const; - - int speedKts() const; - double speedMach() const; - - RouteRestriction altitudeRestriction() const; - RouteRestriction speedRestriction() const; - - void setSpeed(RouteRestriction ty, double speed); - void setAltitude(RouteRestriction ty, int altFt); - - double courseDeg() const; - double distanceNm() const; - double distanceAlongRoute() const; - private: - friend class FlightPlan; - - Leg(FlightPlan* owner, WayptRef wpt); - - Leg* cloneFor(FlightPlan* owner) const; - - FlightPlan* _parent; - RouteRestriction _speedRestrict, _altRestrict; - int _speed; - int _altitudeFt; - WayptRef _waypt; - /// length of this leg following the flown path - mutable double _pathDistance; - mutable double _courseDeg; - /// total distance of this leg from departure point - mutable double _distanceAlongPath; - }; - - class Delegate - { - public: - virtual ~Delegate(); - - virtual void departureChanged() { } - virtual void arrivalChanged() { } - virtual void waypointsChanged() { } - - virtual void currentWaypointChanged() { } - - protected: - Delegate(); - - private: - void removeInner(Delegate* d); - - void runDepartureChanged(); - void runArrivalChanged(); - void runWaypointsChanged(); - void runCurrentWaypointChanged(); - - friend class FlightPlan; - - Delegate* _inner; - }; - - Leg* insertWayptAtIndex(Waypt* aWpt, int aIndex); - void insertWayptsAtIndex(const WayptVec& wps, int aIndex); - - void deleteIndex(int index); - void clear(); - int clearWayptsWithFlag(WayptFlag flag); - - int currentIndex() const - { return _currentIndex; } - - void setCurrentIndex(int index); - - Leg* currentLeg() const; - Leg* nextLeg() const; - Leg* previousLeg() const; - - int numLegs() const - { return _legs.size(); } - - Leg* legAtIndex(int index) const; - int findLegIndex(const Leg* l) const; - - int findWayptIndex(const SGGeod& aPos) const; - - bool load(const SGPath& p); - bool save(const SGPath& p); - - FGAirportRef departureAirport() const - { return _departure; } - - FGAirportRef destinationAirport() const - { return _destination; } - - FGRunway* departureRunway() const - { return _departureRunway; } - - FGRunway* destinationRunway() const - { return _destinationRunway; } - - Approach* approach() const - { return _approach; } - - void setDeparture(FGAirport* apt); - void setDeparture(FGRunway* rwy); - - SID* sid() const - { return _sid; } - - Transition* sidTransition() const; - - void setSID(SID* sid, const std::string& transition = std::string()); - - void setSID(Transition* sidWithTrans); - - void setDestination(FGAirport* apt); - void setDestination(FGRunway* rwy); - - /** - * note setting an approach will implicitly update the destination - * airport and runway to match - */ - void setApproach(Approach* app); - - STAR* star() const - { return _star; } - - Transition* starTransition() const; - - void setSTAR(STAR* star, const std::string& transition = std::string()); - - void setSTAR(Transition* starWithTrans); - - double totalDistanceNm() const - { return _totalDistance; } - - /** - * Create a WayPoint from a string in the following format: - * - simple identifier - * - decimal-lon,decimal-lat - * - airport-id/runway-id - * - navaid/radial-deg/offset-nm - */ - WayptRef waypointFromString(const std::string& target); - - void setDelegate(Delegate* d); - void removeDelegate(Delegate* d); -private: - - bool loadPlainTextRoute(const SGPath& path); - - void loadVersion1XMLRoute(SGPropertyNode_ptr routeData); - void loadVersion2XMLRoute(SGPropertyNode_ptr routeData); - void loadXMLRouteHeader(SGPropertyNode_ptr routeData); - WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP); - - double magvarDegAt(const SGGeod& pos) const; - - std::string _ident; - int _currentIndex; - - FGAirportRef _departure, _destination; - FGRunway* _departureRunway, *_destinationRunway; - SID* _sid; - STAR* _star; - Approach* _approach; - std::string _sidTransition, _starTransition; - - double _totalDistance; - void rebuildLegData(); - - typedef std::vector LegVec; - LegVec _legs; - - Delegate* _delegate; -}; - + } // of namespace flightgear #endif // of FG_ROUTE_HXX diff --git a/src/Navaids/routePath.cxx b/src/Navaids/routePath.cxx index 85fae3026..160407528 100644 --- a/src/Navaids/routePath.cxx +++ b/src/Navaids/routePath.cxx @@ -12,6 +12,7 @@ #include
#include #include +#include #include namespace flightgear { diff --git a/src/Navaids/routePath.hxx b/src/Navaids/routePath.hxx index cd35642a8..97513a70d 100644 --- a/src/Navaids/routePath.hxx +++ b/src/Navaids/routePath.hxx @@ -29,6 +29,7 @@ namespace flightgear { class Hold; + class FlightPlan; } typedef std::vector SGGeodVec; diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index 031e85f1f..ff7546bfc 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -39,13 +39,14 @@ #include #include #include +#include #include #include #include
#include
#include #include -#include +#include #include #include #include