]> git.mxchange.org Git - flightgear.git/commitdiff
Create a real FlightPlan (and Leg) class
authorJames Turner <zakalawe@mac.com>
Tue, 24 Apr 2012 21:55:30 +0000 (22:55 +0100)
committerJames Turner <zakalawe@mac.com>
Sun, 6 May 2012 23:35:48 +0000 (00:35 +0100)
Convert the route-manager to use a flight-plan internally, and expose
flightplan, leg and procedure data to Nasal. Move the Level-D parser
into its own file.

24 files changed:
src/Airports/runways.cxx
src/Airports/runways.hxx
src/Airports/simple.cxx
src/Airports/simple.hxx
src/Autopilot/route_mgr.cxx
src/Autopilot/route_mgr.hxx
src/GUI/MapWidget.cxx
src/GUI/WaypointList.cxx
src/Instrumentation/NavDisplay.cxx
src/Instrumentation/gps.cxx
src/Instrumentation/rnav_waypt_controller.cxx
src/Navaids/CMakeLists.txt
src/Navaids/LevelDXML.cxx [new file with mode: 0644]
src/Navaids/LevelDXML.hxx [new file with mode: 0644]
src/Navaids/airways.hxx
src/Navaids/procedure.cxx
src/Navaids/procedure.hxx
src/Navaids/route.cxx
src/Navaids/route.hxx
src/Navaids/routePath.cxx
src/Navaids/routePath.hxx
src/Navaids/waypoint.cxx
src/Navaids/waypoint.hxx
src/Scripting/NasalPositioned.cxx

index adbd22600cdd3134cdd18fb1cacf111d9962955b..e5adba4f62998421f5185bdadbe3928237a01d56 100644 (file)
@@ -172,7 +172,7 @@ void FGRunway::setReciprocalRunway(FGRunway* other)
   _reciprocal = other;
 }
 
-std::vector<flightgear::SID*> FGRunway::getSIDs()
+std::vector<flightgear::SID*> FGRunway::getSIDs() const
 {
   std::vector<flightgear::SID*> result;
   for (unsigned int i=0; i<_airport->numSIDs(); ++i) {
@@ -185,7 +185,7 @@ std::vector<flightgear::SID*> FGRunway::getSIDs()
   return result;
 }
 
-std::vector<flightgear::STAR*> FGRunway::getSTARs()
+std::vector<flightgear::STAR*> FGRunway::getSTARs() const
 {
   std::vector<flightgear::STAR*> result;
   for (unsigned int i=0; i<_airport->numSTARs(); ++i) {
@@ -198,3 +198,16 @@ std::vector<flightgear::STAR*> FGRunway::getSTARs()
   return result;
 }
 
+std::vector<flightgear::Approach*> FGRunway::getApproaches() const
+{
+  std::vector<flightgear::Approach*> result;
+  for (unsigned int i=0; i<_airport->numApproaches(); ++i) {
+    flightgear::Approach* s = _airport->getApproachByIndex(i);
+    if (s->runway() == this) {
+      result.push_back(s);
+    }
+  } // of approaches at the airport iteration
+  
+  return result;
+}
+
index 9ae89aa7d93c046dc6972b88477478637fa172b3..651f1e68ef478bfbee7175d645e365011d09824b 100644 (file)
@@ -36,6 +36,7 @@ class SGPropertyNode;
 namespace flightgear {
   class SID;
   class STAR;
+  class Approach;
 }
 
 class FGRunway : public FGRunwayBase
@@ -124,12 +125,15 @@ public:
   /**
    * Get SIDs (DPs) associated with this runway
    */
-  std::vector<flightgear::SID*> getSIDs();
+  std::vector<flightgear::SID*> getSIDs() const;
   
   /**
    * Get STARs associared with this runway
    */ 
-  std::vector<flightgear::STAR*> getSTARs();
+  std::vector<flightgear::STAR*> getSTARs() const;
+  
+  
+  std::vector<flightgear::Approach*> getApproaches() const;
   
 };
 
index 7ef5b642bc0977c5c75f9623a43b4cf867309bf0..af3945a61e88aa519c1751e1d0e25f32a8785abe 100644 (file)
@@ -406,7 +406,7 @@ void FGAirport::loadProcedures() const
   }
   
   SG_LOG(SG_GENERAL, SG_INFO, ident() << ": loading procedures from " << path.str());
-  Route::loadAirportProcedures(path, const_cast<FGAirport*>(this));
+  RouteBase::loadAirportProcedures(path, const_cast<FGAirport*>(this));
 }
 
 void FGAirport::loadSceneryDefinitions() const
@@ -469,128 +469,6 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot)
   _tower_location = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM);
 }
 
-bool FGAirport::buildApproach(Waypt* aEnroute, STAR* aSTAR, FGRunway* aRwy, WayptVec& aRoute)
-{
-  loadProcedures();
-
-  if ((aRwy && (aRwy->airport() != this))) {
-    throw sg_exception("invalid parameters", "FGAirport::buildApproach");
-  }
-  
-  if (aSTAR) {
-    bool ok = aSTAR->route(aRwy, aEnroute, aRoute);
-    if (!ok) {
-      SG_LOG(SG_GENERAL, SG_WARN, ident() << ": build approach, STAR " << aSTAR->ident() 
-         << " failed to route from transition " << aEnroute->ident());
-      return false;
-    }
-  } else if (aEnroute) {
-    // no a STAR specified, just use enroute point directly
-    aRoute.push_back(aEnroute);
-  }
-  
-  if (!aRwy) {
-    // no runway selected yet, but we loaded the STAR, so that's fine, we're done
-    return true;
-  }
-  
-// build the approach (possibly including transition), and including the missed segment
-  vector<Approach*> aps;
-  for (unsigned int j=0; j<mApproaches.size();++j) {
-    if (mApproaches[j]->runway() == aRwy) {
-      aps.push_back(mApproaches[j]);
-    }
-  } // of approach filter by runway
-  
-  if (aps.empty()) {
-    SG_LOG(SG_GENERAL, SG_INFO, ident() << "; no approaches defined for runway " << aRwy->ident());
-    // could build a fallback approach here
-    return false;
-  }
-  
-  for (unsigned int k=0; k<aps.size(); ++k) {
-    if (aps[k]->route(aRoute.back(), aRoute)) {
-      return true;
-    }
-  } // of initial approach iteration
-  
-  SG_LOG(SG_GENERAL, SG_INFO, ident() << ": unable to find transition to runway "
-    << aRwy->ident() << ", assume vectors");
-  
-  WayptRef v(new ATCVectors(NULL, this));
-  aRoute.push_back(v);
-  return aps.front()->routeFromVectors(aRoute);
-}
-
-pair<flightgear::SID*, WayptRef>
-FGAirport::selectSID(const SGGeod& aDest, FGRunway* aRwy)
-{
-  loadProcedures();
-  
-  WayptRef enroute;
-  flightgear::SID* sid = NULL;
-  double d = 1e9;
-  
-  for (unsigned int i=0; i<mSIDs.size(); ++i) {
-    if (aRwy && !mSIDs[i]->isForRunway(aRwy)) {
-      continue;
-    }
-  
-    WayptRef e = mSIDs[i]->findBestTransition(aDest);
-    if (!e) {
-      continue; // strange, but let's not worry about it
-    }
-    
-    // assert(e->isFixedPosition());
-    double ed = SGGeodesy::distanceM(aDest, e->position());
-    if (ed < d) { // new best match
-      enroute = e;
-      d = ed;
-      sid = mSIDs[i];
-    }
-  } // of SID iteration
-  
-  if (!mSIDs.empty() && !sid) {
-    SG_LOG(SG_GENERAL, SG_INFO, ident() << "selectSID, no SID found (runway=" 
-      << (aRwy ? aRwy->ident() : "no runway preference"));
-  }
-  
-  return std::make_pair(sid, enroute);
-}
-    
-pair<STAR*, WayptRef>
-FGAirport::selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy)
-{
-  loadProcedures();
-  
-  WayptRef enroute;
-  STAR* star = NULL;
-  double d = 1e9;
-  
-  for (unsigned int i=0; i<mSTARs.size(); ++i) {
-    if (!mSTARs[i]->isForRunway(aRwy)) {
-      continue;
-    }
-    
-    SG_LOG(SG_GENERAL, SG_INFO, "STAR " << mSTARs[i]->ident() << " is valid for runway");
-    WayptRef e = mSTARs[i]->findBestTransition(aOrigin);
-    if (!e) {
-      continue; // strange, but let's not worry about it
-    }
-    
-    // assert(e->isFixedPosition());
-    double ed = SGGeodesy::distanceM(aOrigin, e->position());
-    if (ed < d) { // new best match
-      enroute = e;
-      d = ed;
-      star = mSTARs[i];
-    }
-  } // of STAR iteration
-  
-  return std::make_pair(star, enroute);
-}
-
-
 void FGAirport::addSID(flightgear::SID* aSid)
 {
   mSIDs.push_back(aSid);
@@ -666,6 +544,18 @@ Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const
   return mApproaches[aIndex];
 }
 
+Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const
+{
+  loadProcedures();
+  for (unsigned int i=0; i<mApproaches.size(); ++i) {
+    if (mApproaches[i]->ident() == aIdent) {
+      return mApproaches[i];
+    }
+  }
+  
+  return NULL;
+}
+
 void FGAirport::setCommStations(CommStationList& comms)
 {
     mCommStations.swap(comms);
index c2eafbbe15483114f572a5b61304fe27f3a447db..6995e6976e1972cf5a65105e0bc3e2bbcfed825b 100644 (file)
@@ -188,7 +188,8 @@ public:
       
       unsigned int numApproaches() const;
       flightgear::Approach* getApproachByIndex(unsigned int aIndex) const;
-      
+      flightgear::Approach* findApproachWithIdent(const std::string& aIdent) const;
+  
      /**
       * Syntactic wrapper around FGPositioned::findClosest - find the closest
       * match for filter, and return it cast to FGAirport. The default filter
@@ -216,22 +217,6 @@ public:
       */
      static char** searchNamesAndIdents(const std::string& aFilter);
      
-     bool buildApproach(flightgear::Waypt* aEnroute, flightgear::STAR* aSTAR, 
-      FGRunway* aRwy, flightgear::WayptVec& aRoute);
-    
-    /**
-     * Given a destiation point, select the best SID and transition waypt from 
-     * this airport. Returns (NULL,NULL) is no SIDs are defined, otherwise the
-     * best SID/transition is that which is closest to the destination point.
-     */
-    std::pair<flightgear::SID*, flightgear::WayptRef> selectSID(const SGGeod& aDest, FGRunway* aRwy);
-    
-    /**
-     * Select a STAR and enroute transition waypt, given an origin (departure) position.
-     * returns (NULL, NULL) is no suitable STAR is exists
-     */
-    std::pair<flightgear::STAR*, flightgear::WayptRef> selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy);
-        
     void setCommStations(flightgear::CommStationList& comms);
     
     flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const;
index b16318da76f5906054f5a081a3de6afa6ad18b6f..80313d70a8e1f35e5bfd096a68c878343008fa6d 100644 (file)
 
 using namespace flightgear;
 
-class PropertyWatcher : public SGPropertyChangeListener
-{
-public:
-  void watch(SGPropertyNode* p)
-  {
-    p->addChangeListener(this, false);
-  }
-
-  virtual void valueChanged(SGPropertyNode*)
-  {
-    fire();
-  }
-protected:
-  virtual void fire() = 0;
-};
-
-/**
- * Template adapter, created by convenience helper below
- */
-template <class T>
-class MethodPropertyWatcher : public PropertyWatcher
-{
-public:
-  typedef void (T::*fire_method)();
-
-  MethodPropertyWatcher(T* obj, fire_method m) :
-    _object(obj),
-    _method(m)
-  { ; }
-  
-protected:
-  virtual void fire()
-  { // dispatch to the object method we're helping
-    (_object->*_method)();
-  }
-  
-private:
-  T* _object;
-  fire_method _method;
-};
-
-template <class T>
-PropertyWatcher* createWatcher(T* obj, void (T::*m)())
-{
-  return new MethodPropertyWatcher<T>(obj, m);
-}
-
 static bool commandLoadFlightPlan(const SGPropertyNode* arg)
 {
   FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager");
@@ -148,7 +101,7 @@ static bool commandSetActiveWaypt(const SGPropertyNode* arg)
 {
   FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager");
   int index = arg->getIntValue("index");
-  if ((index < 0) || (index >= self->numWaypts())) {
+  if ((index < 0) || (index >= self->numLegs())) {
     return false;
   }
   
@@ -232,16 +185,16 @@ static bool commandInsertWaypt(const SGPropertyNode* arg)
   } else {
     return false; // failed to build waypoint
   }
-  
+
+  FlightPlan::Leg* leg = self->flightPlan()->insertWayptAtIndex(wp, index);
   if (alt >= 0) {
-    wp->setAltitude(alt, flightgear::RESTRICT_AT);
+    leg->setAltitude(RESTRICT_AT, alt);
   }
   
   if (ias > 0) {
-    wp->setSpeed(ias, flightgear::RESTRICT_AT);
+    leg->setSpeed(RESTRICT_AT, ias);
   }
-
-  self->insertWayptAtIndex(wp, index);
+  
   return true;
 }
 
@@ -249,18 +202,16 @@ static bool commandDeleteWaypt(const SGPropertyNode* arg)
 {
   FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager");
   int index = arg->getIntValue("index");
-  self->removeWayptAtIndex(index);
+  self->removeLegAtIndex(index);
   return true;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
 FGRouteMgr::FGRouteMgr() :
-  _currentIndex(0),
+  _plan(NULL),
   input(fgGetNode( RM "input", true )),
-  mirror(fgGetNode( RM "route", true )),
-  _departureWatcher(NULL),
-  _arrivalWatcher(NULL)
+  mirror(fgGetNode( RM "route", true ))
 {
   listener = new InputListener(this);
   input->setStringValue("");
@@ -280,32 +231,29 @@ FGRouteMgr::~FGRouteMgr()
 {
   input->removeChangeListener(listener);
   delete listener;
-  delete _departureWatcher;
-  delete _arrivalWatcher;
 }
 
 
 void FGRouteMgr::init() {
   SGPropertyNode_ptr rm(fgGetNode(RM));
   
-  lon = fgGetNode( "/position/longitude-deg", true );
-  lat = fgGetNode( "/position/latitude-deg", true );
-  alt = fgGetNode( "/position/altitude-ft", true );
   magvar = fgGetNode("/environment/magnetic-variation-deg", true);
      
   departure = fgGetNode(RM "departure", true);
   departure->tie("airport", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
     &FGRouteMgr::getDepartureICAO, &FGRouteMgr::setDepartureICAO));
+  departure->tie("runway", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+                                                                       &FGRouteMgr::getDepartureRunway, 
+                                                                      &FGRouteMgr::setDepartureRunway));
+  departure->tie("sid", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+                                                                      &FGRouteMgr::getSID, 
+                                                                      &FGRouteMgr::setSID));
+  
   departure->tie("name", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
     &FGRouteMgr::getDepartureName, NULL));
-  departure->setStringValue("runway", "");
-  
-  delete _departureWatcher;
-  _departureWatcher = createWatcher(this, &FGRouteMgr::departureChanged);
-  _departureWatcher->watch(departure->getChild("runway"));
-  
+  departure->tie("field-elevation-ft", SGRawValueMethods<FGRouteMgr, double>(*this, 
+                                                                               &FGRouteMgr::getDestinationFieldElevation, NULL));
   departure->getChild("etd", 0, true);
-  _departureWatcher->watch(departure->getChild("sid", 0, true));
   departure->getChild("takeoff-time", 0, true);
 
   destination = fgGetNode(RM "destination", true);
@@ -313,16 +261,22 @@ void FGRouteMgr::init() {
   
   destination->tie("airport", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
     &FGRouteMgr::getDestinationICAO, &FGRouteMgr::setDestinationICAO));
+  destination->tie("runway", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+                             &FGRouteMgr::getDestinationRunway, 
+                            &FGRouteMgr::setDestinationRunway));
+  destination->tie("star", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+                                                                        &FGRouteMgr::getSTAR, 
+                                                                        &FGRouteMgr::setSTAR));
+  destination->tie("approach", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
+                                                                        &FGRouteMgr::getApproach, 
+                                                                        &FGRouteMgr::setApproach));
+  
   destination->tie("name", SGRawValueMethods<FGRouteMgr, const char*>(*this, 
     &FGRouteMgr::getDestinationName, NULL));
-  
-  delete _arrivalWatcher;
-  _arrivalWatcher = createWatcher(this, &FGRouteMgr::arrivalChanged);
-  _arrivalWatcher->watch(destination->getChild("runway", 0, true));
+  destination->tie("field-elevation-ft", SGRawValueMethods<FGRouteMgr, double>(*this, 
+                                                                      &FGRouteMgr::getDestinationFieldElevation, NULL));
   
   destination->getChild("eta", 0, true);
-  _arrivalWatcher->watch(destination->getChild("star", 0, true));
-  _arrivalWatcher->watch(destination->getChild("transition", 0, true));
   destination->getChild("touchdown-time", 0, true);
 
   alternate = fgGetNode(RM "alternate", true);
@@ -379,8 +333,8 @@ void FGRouteMgr::init() {
   wpn->getChild("dist", 0, true);
   wpn->getChild("eta", 0, true);
   
-  update_mirror();
   _pathNode = fgGetNode(RM "file-path", 0, true);
+  setFlightPlan(new FlightPlan());
 }
 
 
@@ -401,11 +355,11 @@ void FGRouteMgr::postinit()
     for (it = waypoints->begin(); it != waypoints->end(); ++it) {
       WayptRef w = waypointFromString(*it);
       if (w) {
-        _route.push_back(w);
+        _plan->insertWayptAtIndex(w, -1);
       }
     }
     
-    SG_LOG(SG_AUTOPILOT, SG_INFO, "loaded initial waypoints:" << _route.size());
+    SG_LOG(SG_AUTOPILOT, SG_INFO, "loaded initial waypoints:" << numLegs());
     update_mirror();
   }
 
@@ -421,6 +375,53 @@ bool FGRouteMgr::isRouteActive() const
   return active->getBoolValue();
 }
 
+bool FGRouteMgr::saveRoute(const SGPath& p)
+{
+  if (!_plan) {
+    return false;
+  }
+  
+  return _plan->save(p);
+}
+
+bool FGRouteMgr::loadRoute(const SGPath& p)
+{
+  FlightPlan* fp = new FlightPlan;
+  if (!fp->load(p)) {
+    delete fp;
+    return false;
+  }
+  
+  setFlightPlan(fp);
+  return true;
+}
+
+FlightPlan* FGRouteMgr::flightPlan() const
+{
+  return _plan;
+}
+
+void FGRouteMgr::setFlightPlan(FlightPlan* plan)
+{
+  if (plan == _plan) {
+    return;
+  }
+  
+  if (_plan) {
+    delete _plan;
+    active->setBoolValue(false);
+  }
+  
+  _plan = plan;
+  _plan->setDelegate(this);
+  
+// fire all the callbacks!
+  departureChanged();
+  arrivalChanged();
+  waypointsChanged();
+  currentWaypointChanged();
+}
+
 void FGRouteMgr::update( double dt )
 {
   if (dt <= 0.0) {
@@ -446,17 +447,16 @@ void FGRouteMgr::update( double dt )
   }
 
 // basic course/distance information
-  SGGeod currentPos = SGGeod::fromDegFt(lon->getDoubleValue(), 
-    lat->getDoubleValue(),alt->getDoubleValue());
+  SGGeod currentPos = globals->get_aircraft_position();
 
-  Waypt* curWpt = currentWaypt();
-  if (!curWpt) {
+  FlightPlan::Leg* leg = _plan ? _plan->currentLeg() : NULL;
+  if (!leg) {
     return;
   }
   
   double courseDeg;
   double distanceM;
-  boost::tie(courseDeg, distanceM) = curWpt->courseAndDistanceFrom(currentPos);
+  boost::tie(courseDeg, distanceM) = leg->waypoint()->courseAndDistanceFrom(currentPos);
   
 // update wp0 / wp1 / wp-last
   wp0->setDoubleValue("dist", distanceM * SG_METER_TO_NM);
@@ -465,42 +465,85 @@ void FGRouteMgr::update( double dt )
   wp0->setDoubleValue("bearing-deg", courseDeg);
   setETAPropertyFromDistance(wp0->getChild("eta"), distanceM);
   
-  double totalPathDistance = totalDistance->getDoubleValue() * SG_NM_TO_METER;
-  double totalDistanceRemaining = distanceM; // distance to current waypoint
-  double pathDistance = cachedWaypointPathTotalDistance(_currentIndex);
+  double totalPathDistanceNm = _plan->totalDistanceNm();
+  double totalDistanceRemaining = distanceM * SG_METER_TO_NM; // distance to current waypoint
   
 // total distance to go, is direct distance to wp0, plus the remaining
 // path distance from wp0
-  totalDistanceRemaining += (totalPathDistance - pathDistance);
+  totalDistanceRemaining += (totalPathDistanceNm - leg->distanceAlongRoute());
   
   wp0->setDoubleValue("distance-along-route-nm", 
-                      pathDistance * SG_METER_TO_NM);
+                      leg->distanceAlongRoute());
   wp0->setDoubleValue("remaining-distance-nm", 
-                      (totalPathDistance - pathDistance) * SG_METER_TO_NM);
+                      totalPathDistanceNm - leg->distanceAlongRoute());
   
-  Waypt* nextWpt = nextWaypt();
-  if (nextWpt) {
-    boost::tie(courseDeg, distanceM) = nextWpt->courseAndDistanceFrom(currentPos);
+  FlightPlan::Leg* nextLeg = _plan->nextLeg();
+  if (nextLeg) {
+    boost::tie(courseDeg, distanceM) = nextLeg->waypoint()->courseAndDistanceFrom(currentPos);
      
     wp1->setDoubleValue("dist", distanceM * SG_METER_TO_NM);
     wp1->setDoubleValue("true-bearing-deg", courseDeg);
     courseDeg -= magvar->getDoubleValue(); // expose magnetic bearing
     wp1->setDoubleValue("bearing-deg", courseDeg);
-    setETAPropertyFromDistance(wp1->getChild("eta"), distanceM);
-    
-    double pathDistance = cachedWaypointPathTotalDistance(_currentIndex + 1);    
+    setETAPropertyFromDistance(wp1->getChild("eta"), distanceM);    
     wp1->setDoubleValue("distance-along-route-nm", 
-                        pathDistance * SG_METER_TO_NM);
+                        nextLeg->distanceAlongRoute());
     wp1->setDoubleValue("remaining-distance-nm", 
-                        (totalPathDistance - pathDistance) * SG_METER_TO_NM);
+                        totalPathDistanceNm - nextLeg->distanceAlongRoute());
   }
   
-  distanceToGo->setDoubleValue(totalDistanceRemaining * SG_METER_TO_NM);
-  wpn->setDoubleValue("dist", totalDistanceRemaining * SG_METER_TO_NM);
-  ete->setDoubleValue(totalDistanceRemaining * SG_METER_TO_NM / groundSpeed * 3600.0);
+  distanceToGo->setDoubleValue(totalDistanceRemaining);
+  wpn->setDoubleValue("dist", totalDistanceRemaining);
+  ete->setDoubleValue(totalDistanceRemaining / groundSpeed * 3600.0);
   setETAPropertyFromDistance(wpn->getChild("eta"), totalDistanceRemaining);
 }
 
+void FGRouteMgr::clearRoute()
+{
+  if (_plan) {
+    _plan->clear();
+  }
+}
+
+Waypt* FGRouteMgr::currentWaypt() const
+{
+  if (_plan && _plan->currentLeg()) {
+    return _plan->currentLeg()->waypoint();
+  }
+  
+  return NULL;
+}
+
+int FGRouteMgr::currentIndex() const
+{
+  if (!_plan) {
+    return 0;
+  }
+  
+  return _plan->currentIndex();
+}
+
+Waypt* FGRouteMgr::wayptAtIndex(int index) const
+{
+  if (_plan) {
+    FlightPlan::Leg* leg = _plan->legAtIndex(index);
+    if (leg) {
+      return leg->waypoint();
+    }
+  }
+  
+  return NULL;
+}
+
+int FGRouteMgr::numLegs() const
+{
+  if (_plan) {
+    return _plan->numLegs();
+  }
+  
+  return 0;
+}
+
 void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance)
 {
   double speed = fgGetDouble("/velocities/groundspeed-kt", 0.0);
@@ -525,62 +568,15 @@ void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDi
   aProp->setStringValue( eta_str );
 }
 
-flightgear::WayptRef FGRouteMgr::removeWayptAtIndex(int aIndex)
+void FGRouteMgr::removeLegAtIndex(int aIndex)
 {
-  int index = aIndex;
-  if (aIndex < 0) { // negative indices count the the end
-    index = _route.size() + index;
-  }
-  
-  if ((index < 0) || (index >= numWaypts())) {
-    SG_LOG(SG_AUTOPILOT, SG_WARN, "removeWayptAtIndex with invalid index:" << aIndex);
-    return NULL;
-  }
-  WayptVec::iterator it = _route.begin();
-  it += index;
-  
-  WayptRef w = *it; // hold a ref now, in case _route is the only other owner
-  _route.erase(it);
-  
-  update_mirror();
-  
-  if (_currentIndex == index) {
-    currentWaypointChanged(); // current waypoint was removed
-  }
-  else
-  if (_currentIndex > index) {
-    --_currentIndex; // shift current index down if necessary
+  if (!_plan) {
+    return;
   }
-
-  _edited->fireValueChanged();
-  checkFinished();
   
-  return w;
+  _plan->deleteIndex(aIndex);
 }
   
-struct NotGeneratedWayptPredicate : public std::unary_function<const Waypt*, bool>
-{
-  bool operator() (const Waypt* w) const
-  {
-    return (w->flag(WPT_GENERATED) == false);
-  }
-};
-
-
-void FGRouteMgr::clearRoute()
-{
-// erase all non-generated waypoints
-  WayptVec::iterator r =  
-    std::remove_if(_route.begin(), _route.end(), NotGeneratedWayptPredicate());
-  _route.erase(r, _route.end());
-  
-  _currentIndex = -1;
-  
-  update_mirror();
-  active->setBoolValue(false);
-  _edited->fireValueChanged();
-}
-
 /**
  * route between index-1 and index, using airways.
  */
@@ -590,27 +586,27 @@ bool FGRouteMgr::routeToIndex(int index, RouteType aRouteType)
   WayptRef wp2;
   
   if (index == -1) {
-    index = _route.size(); // can still be zero, of course
+    index = numLegs();
   }
   
   if (index == 0) {
-    if (!_departure) {
+    if (!_plan->departureAirport()) {
       SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no departure set");
       return false;
     }
     
-    wp1 = new NavaidWaypoint(_departure.get(), NULL);
+    wp1 = new NavaidWaypoint(_plan->departureAirport().get(), NULL);
   } else {
     wp1 = wayptAtIndex(index - 1);
   }
   
-  if (index >= numWaypts()) {
-    if (!_destination) {
+  if (index >= numLegs()) {
+    if (!_plan->destinationAirport()) {
       SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no destination set");
       return false;
     }
     
-    wp2 = new NavaidWaypoint(_destination.get(), NULL);
+    wp2 = new NavaidWaypoint(_plan->destinationAirport().get(), NULL);
   } else {
     wp2 = wayptAtIndex(index);
   }
@@ -640,374 +636,108 @@ bool FGRouteMgr::routeToIndex(int index, RouteType aRouteType)
     return false;
   }
 
-  WayptVec::iterator it = _route.begin();
-  it += index;
-  _route.insert(it, r.begin(), r.end());
-
-  update_mirror();
-  _edited->fireValueChanged();
+  _plan->insertWayptsAtIndex(r, index);
   return true;
 }
 
-void FGRouteMgr::autoRoute()
-{
-  if (!_departure || !_destination) {
-    return;
-  }
-  
-  string runwayId(departure->getStringValue("runway"));
-  FGRunway* runway = NULL;
-  if (_departure->hasRunwayWithIdent(runwayId)) {
-    runway = _departure->getRunwayByIdent(runwayId);
-  }
-  
-  FGRunway* dstRunway = NULL;
-  runwayId = destination->getStringValue("runway");
-  if (_destination->hasRunwayWithIdent(runwayId)) {
-    dstRunway = _destination->getRunwayByIdent(runwayId);
-  }
-    
-  _route.clear(); // clear out the existing, first
-// SID
-  flightgear::SID* sid;
-  WayptRef sidTrans;
-  
-  boost::tie(sid, sidTrans) = _departure->selectSID(_destination->geod(), runway);
-  if (sid) { 
-    SG_LOG(SG_AUTOPILOT, SG_INFO, "selected SID " << sid->ident());
-    if (sidTrans) {
-      SG_LOG(SG_AUTOPILOT, SG_INFO, "\tvia " << sidTrans->ident() << " transition");
-    }
-    
-    sid->route(runway, sidTrans, _route);
-    departure->setStringValue("sid", sid->ident());
-  } else {
-    // use airport location for airway search
-    sidTrans = new NavaidWaypoint(_departure.get(), NULL);
-    departure->setStringValue("sid", "");
-  }
-  
-// STAR
-  destination->setStringValue("transition", "");
-  destination->setStringValue("star", "");
-  
-  STAR* star;
-  WayptRef starTrans;
-  boost::tie(star, starTrans) = _destination->selectSTAR(_departure->geod(), dstRunway);
-  if (star) {
-    SG_LOG(SG_AUTOPILOT, SG_INFO, "selected STAR " << star->ident());
-    if (starTrans) {
-      SG_LOG(SG_AUTOPILOT, SG_INFO, "\tvia " << starTrans->ident() << " transition");
-      destination->setStringValue("transition", starTrans->ident());
-    }    
-    destination->setStringValue("star", star->ident());
-  } else {
-    // use airport location for search
-    starTrans = new NavaidWaypoint(_destination.get(), NULL);
-  }
-  
-// route between them
-  WayptVec airwayRoute;
-  if (Airway::highLevel()->route(sidTrans, starTrans, airwayRoute)) {
-    _route.insert(_route.end(), airwayRoute.begin(), airwayRoute.end());
-  }
-  
-// add the STAR if we have one
-  if (star) {
-    _destination->buildApproach(starTrans, star, dstRunway, _route);
-  }
-
-  update_mirror();
-  _edited->fireValueChanged();
-}
-
 void FGRouteMgr::departureChanged()
 {
-// remove existing departure waypoints
-  WayptVec::iterator it = _route.begin();
-  for (; it != _route.end(); ++it) {
-    if (!(*it)->flag(WPT_DEPARTURE)) {
-      break;
-    }
-  }
-  
-  // erase() invalidates iterators, so grab now
+  _plan->clearWayptsWithFlag(WPT_DEPARTURE);
   WayptRef enroute;
-  if (it == _route.end()) {
-    if (_destination) {
-      enroute = new NavaidWaypoint(_destination.get(), NULL);
-    }
-  } else {
-    enroute = *it;
-  }
-
-  _route.erase(_route.begin(), it);
-  if (!_departure) {
-    waypointsChanged();
-    return;
-  }
-  
   WayptVec wps;
   buildDeparture(enroute, wps);
-  for (it = wps.begin(); it != wps.end(); ++it) {
-    (*it)->setFlag(WPT_DEPARTURE);
-    (*it)->setFlag(WPT_GENERATED);
-  }
-  _route.insert(_route.begin(), wps.begin(), wps.end());
-  
-  update_mirror();
-  waypointsChanged();
+  _plan->insertWayptsAtIndex(wps, 0);
 }
 
 void FGRouteMgr::buildDeparture(WayptRef enroute, WayptVec& wps)
 {
-  string runwayId(departure->getStringValue("runway"));
-  if (!_departure->hasRunwayWithIdent(runwayId)) {
-// valid airport, but no runway selected, so just the airport noide itself
-    wps.push_back(new NavaidWaypoint(_departure.get(), NULL));
+  if (!_plan->departureAirport()) {
     return;
   }
   
-  FGRunway* r = _departure->getRunwayByIdent(runwayId);
-  string sidId = departure->getStringValue("sid");
-  flightgear::SID* sid = _departure->findSIDWithIdent(sidId);
-  if (!sid) {
-// valid runway, but no SID selected/found, so just the runway node for now
-    if (!sidId.empty() && (sidId != "(none)")) {
-      SG_LOG(SG_AUTOPILOT, SG_INFO, "SID not found:" << sidId);
-    }
-    
-    wps.push_back(new RunwayWaypt(r, NULL));
+  if (!_plan->departureRunway()) {
+// valid airport, but no runway selected, so just the airport _plan itself
+    WayptRef w = new NavaidWaypoint(_plan->departureAirport(), _plan);
+    w->setFlag(WPT_DEPARTURE);
+    wps.push_back(w);
     return;
   }
   
-// we have a valid SID, awesome
-  string trans(departure->getStringValue("transition"));
-  WayptRef t = sid->findTransitionByName(trans);
-  if (!t && enroute) {
-    t = sid->findBestTransition(enroute->position());
-  }
-
-  sid->route(r, t, wps);
-  if (!wps.empty() && wps.front()->flag(WPT_DYNAMIC)) {
-    // ensure first waypoint is static, to simplify other computations
-    wps.insert(wps.begin(), new RunwayWaypt(r, NULL));
+  WayptRef rwyWaypt = new RunwayWaypt(_plan->departureRunway(), _plan);
+  rwyWaypt->setFlag(WPT_DEPARTURE);
+  wps.push_back(rwyWaypt);
+  
+  if (!_plan->sid()) {
+    return;
   }
+  
+  _plan->sid()->route(_plan->departureRunway(), _plan->sidTransition(), wps);
 }
 
 void FGRouteMgr::arrivalChanged()
 {  
-  // remove existing arrival waypoints
-  WayptVec::reverse_iterator rit = _route.rbegin();
-  for (; rit != _route.rend(); ++rit) {
-    if (!(*rit)->flag(WPT_ARRIVAL)) {
-      break;
-    }
-  }
-  
-  // erase() invalidates iterators, so grab now
-  WayptRef enroute;
-  WayptVec::iterator it;
-  
-  if (rit != _route.rend()) {
-    enroute = *rit;
-    it = rit.base(); // convert to fwd iterator
-  } else {
-    it = _route.begin();
-  }
-
-  _route.erase(it, _route.end());
-  
+  _plan->clearWayptsWithFlag(WPT_ARRIVAL);
+  _plan->clearWayptsWithFlag(WPT_APPROACH);
   WayptVec wps;
+  WayptRef enroute;
   buildArrival(enroute, wps);
-  for (it = wps.begin(); it != wps.end(); ++it) {
-    (*it)->setFlag(WPT_ARRIVAL);
-    (*it)->setFlag(WPT_GENERATED);
-  }
-  _route.insert(_route.end(), wps.begin(), wps.end());
-  
-  update_mirror();
-  waypointsChanged();
+  _plan->insertWayptsAtIndex(wps, -1);
 }
 
 void FGRouteMgr::buildArrival(WayptRef enroute, WayptVec& wps)
 {
-  if (!_destination) {
+  FGAirportRef apt = _plan->departureAirport();
+  if (!apt.valid()) {
     return;
   }
   
-  string runwayId(destination->getStringValue("runway"));
-  if (!_destination->hasRunwayWithIdent(runwayId)) {
-// valid airport, but no runway selected, so just the airport node itself
-    wps.push_back(new NavaidWaypoint(_destination.get(), NULL));
+  if (!_plan->destinationRunway()) {
+    WayptRef w = new NavaidWaypoint(apt.ptr(), _plan);
+    w->setFlag(WPT_ARRIVAL);
+    wps.push_back(w);
     return;
   }
   
-  FGRunway* r = _destination->getRunwayByIdent(runwayId);
-  string starId = destination->getStringValue("star");
-  STAR* star = _destination->findSTARWithIdent(starId);
-  if (!star) {
-// valid runway, but no STAR selected/found, so just the runway node for now
-    wps.push_back(new RunwayWaypt(r, NULL));
-    return;
+  if (_plan->star()) {
+    _plan->star()->route(_plan->destinationRunway(), _plan->starTransition(), wps);
   }
   
-// we have a valid STAR
-  string trans(destination->getStringValue("transition"));
-  WayptRef t = star->findTransitionByName(trans);
-  if (!t && enroute) {
-    t = star->findBestTransition(enroute->position());
+  if (_plan->approach()) {
+    _plan->approach()->route(wps.back(), wps);
+  } else {
+    WayptRef w = new RunwayWaypt(_plan->destinationRunway(), _plan);
+    w->setFlag(WPT_APPROACH);
+    wps.push_back(w);
   }
-  
-  _destination->buildApproach(t, star, r, wps);
 }
 
 void FGRouteMgr::waypointsChanged()
 {
-
-}
-
-void FGRouteMgr::insertWayptAtIndex(Waypt* aWpt, int aIndex)
-{
-  if (!aWpt) {
-    return;
-  }
-  
-  int index = aIndex;
-  if ((aIndex == -1) || (aIndex > (int) _route.size())) {
-    index = _route.size();
-  }
-  
-  WayptVec::iterator it = _route.begin();
-  it += index;
-      
-  if (_currentIndex >= index) {
-    ++_currentIndex;
-  }
-  
-  _route.insert(it, aWpt);
-  
   update_mirror();
-  _edited->fireValueChanged();
-}
-
-WayptRef FGRouteMgr::waypointFromString(const string& tgt )
-{
-  string target(boost::to_upper_copy(tgt));
-  WayptRef wpt;
-  
-// extract altitude
-  double altFt = cruise->getDoubleValue("altitude-ft");
-  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 (_route.empty()) {
-    // route is empty, use current position
-    basePosition = SGGeod::fromDeg(lon->getDoubleValue(), lat->getDoubleValue());
-  } else {
-    basePosition = _route.back()->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;
-  }
-
-  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->getDoubleValue(); // convert to true bearing
-    wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm);
-  } else if (pieces.size() == 2) {
-    FGAirport* apt = dynamic_cast<FGAirport*>(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->getDoubleValue();
-    r2 += magvar->getDoubleValue();
-    
-    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;
+   _edited->fireValueChanged();
+  checkFinished();
 }
 
 // mirror internal route to the property system for inspection by other subsystems
 void FGRouteMgr::update_mirror()
 {
   mirror->removeChildren("wp");
+  NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
+  FGDialog* rmDlg = gui ? gui->getDialog("route-manager") : NULL;
+
+  if (!_plan) {
+    mirror->setIntValue("num", 0);
+    if (rmDlg) {
+      rmDlg->updateValues();
+    }
+    return;
+  }
   
-  int num = numWaypts();
-  double totalDistanceEnroute = 0.0;
+  int num = _plan->numLegs();
     
   for (int i = 0; i < num; i++) {
-    Waypt* wp = _route[i];
+    FlightPlan::Leg* leg = _plan->legAtIndex(i);
+    WayptRef wp = leg->waypoint();
     SGPropertyNode *prop = mirror->getChild("wp", i, 1);
 
     const SGGeod& pos(wp->position());
@@ -1016,18 +746,13 @@ void FGRouteMgr::update_mirror()
     prop->setDoubleValue("latitude-deg",pos.getLatitudeDeg());
    
     // leg course+distance
-    if (i < (num - 1)) {
-      Waypt* next = _route[i+1];
-      std::pair<double, double> crsDist =
-        next->courseAndDistanceFrom(pos);
-      prop->setDoubleValue("leg-bearing-true-deg", crsDist.first);
-      prop->setDoubleValue("leg-distance-nm", crsDist.second * SG_METER_TO_NM);
-      prop->setDoubleValue("distance-along-route-nm", totalDistanceEnroute);
-      totalDistanceEnroute += crsDist.second * SG_METER_TO_NM;
-    }
+
+    prop->setDoubleValue("leg-bearing-true-deg", leg->courseDeg());
+    prop->setDoubleValue("leg-distance-nm", leg->distanceNm());
+    prop->setDoubleValue("distance-along-route-nm", leg->distanceAlongRoute());
     
-    if (wp->altitudeRestriction() != RESTRICT_NONE) {
-      double ft = wp->altitudeFt();
+    if (leg->altitudeRestriction() != RESTRICT_NONE) {
+      double ft = leg->altitudeFt();
       prop->setDoubleValue("altitude-m", ft * SG_FEET_TO_METER);
       prop->setDoubleValue("altitude-ft", ft);
       prop->setIntValue("flight-level", static_cast<int>(ft / 1000) * 10);
@@ -1036,10 +761,10 @@ void FGRouteMgr::update_mirror()
       prop->setDoubleValue("altitude-ft", -9999.9);
     }
     
-    if (wp->speedRestriction() == SPEED_RESTRICT_MACH) {
-      prop->setDoubleValue("speed-mach", wp->speedMach());
-    } else if (wp->speedRestriction() != RESTRICT_NONE) {
-      prop->setDoubleValue("speed-kts", wp->speedKts());
+    if (leg->speedRestriction() == SPEED_RESTRICT_MACH) {
+      prop->setDoubleValue("speed-mach", leg->speedMach());
+    } else if (leg->speedRestriction() != RESTRICT_NONE) {
+      prop->setDoubleValue("speed-kts", leg->speedKts());
     }
     
     if (wp->flag(WPT_ARRIVAL)) {
@@ -1058,35 +783,13 @@ void FGRouteMgr::update_mirror()
   } // of waypoint iteration
   
   // set number as listener attachment point
-  mirror->setIntValue("num", _route.size());
+  mirror->setIntValue("num", _plan->numLegs());
     
-  NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
-  FGDialog* rmDlg = gui->getDialog("route-manager");
   if (rmDlg) {
     rmDlg->updateValues();
   }
-    
-  if (_departure) {
-    departure->setDoubleValue("field-elevation-ft", _departure->getElevation());
-  }
   
-  if (_destination) {
-    destination->setDoubleValue("field-elevation-ft", _destination->getElevation());
-  }
-  
-  totalDistance->setDoubleValue(totalDistanceEnroute);
-}
-
-double FGRouteMgr::cachedLegPathDistanceM(int index) const
-{
-  SGPropertyNode *prop = mirror->getChild("wp", index, 1);
-  return prop->getDoubleValue("leg-distance-nm") * SG_NM_TO_METER;
-}
-
-double FGRouteMgr::cachedWaypointPathTotalDistance(int index) const
-{
-  SGPropertyNode *prop = mirror->getChild("wp", index, 1);
-  return prop->getDoubleValue("distance-along-route-nm") * SG_NM_TO_METER;
+  totalDistance->setDoubleValue(_plan->totalDistanceNm());
 }
 
 // command interface /autopilot/route-manager/input:
@@ -1115,13 +818,13 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop)
       SGPath path(mgr->_pathNode->getStringValue());
       mgr->saveRoute(path);
     } else if (!strcmp(s, "@NEXT")) {
-      mgr->jumpToIndex(mgr->_currentIndex + 1);
+      mgr->jumpToIndex(mgr->currentIndex() + 1);
     } else if (!strcmp(s, "@PREVIOUS")) {
-      mgr->jumpToIndex(mgr->_currentIndex - 1);
+      mgr->jumpToIndex(mgr->currentIndex() - 1);
     } else if (!strncmp(s, "@JUMP", 5)) {
       mgr->jumpToIndex(atoi(s + 5));
     } else if (!strncmp(s, "@DELETE", 7))
-        mgr->removeWayptAtIndex(atoi(s + 7));
+        mgr->removeLegAtIndex(atoi(s + 7));
     else if (!strncmp(s, "@INSERT", 7)) {
         char *r;
         int pos = strtol(s + 7, &r, 10);
@@ -1130,18 +833,16 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop)
         while (isspace(*r))
             r++;
         if (*r)
-            mgr->insertWayptAtIndex(mgr->waypointFromString(r), pos);
+            mgr->flightPlan()->insertWayptAtIndex(mgr->waypointFromString(r), pos);
     } else if (!strncmp(s, "@ROUTE", 6)) {
       char* r;
       int endIndex = strtol(s + 6, &r, 10);
       RouteType rt = (RouteType) mgr->_routingType->getIntValue();
       mgr->routeToIndex(endIndex, rt);
-    } else if (!strcmp(s, "@AUTOROUTE")) {
-      mgr->autoRoute();
     } else if (!strcmp(s, "@POSINIT")) {
       mgr->initAtPosition();
     } else
-      mgr->insertWayptAtIndex(mgr->waypointFromString(s), -1);
+      mgr->flightPlan()->insertWayptAtIndex(mgr->waypointFromString(s), -1);
 }
 
 void FGRouteMgr::initAtPosition()
@@ -1158,63 +859,56 @@ void FGRouteMgr::initAtPosition()
   
   if (airborne->getBoolValue()) {
     SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: airborne, clearing departure info");
-    _departure = NULL;
-    departure->setStringValue("runway", "");
+    _plan->setDeparture((FGAirport*) NULL);
     return;
   }
   
 // on the ground
-  SGGeod pos = SGGeod::fromDegFt(lon->getDoubleValue(), 
-    lat->getDoubleValue(), alt->getDoubleValue());
-  if (!_departure) {
-    _departure = FGAirport::findClosest(pos, 20.0);
-    if (!_departure) {
+  SGGeod pos = globals->get_aircraft_position();
+  if (!_plan->departureAirport()) {
+    _plan->setDeparture(FGAirport::findClosest(pos, 20.0));
+    if (!_plan->departureAirport()) {
       SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: couldn't find an airport within 20nm");
-      departure->setStringValue("runway", "");
       return;
     }
   }
   
   std::string rwy = departure->getStringValue("runway");
+  FGRunway* r = NULL;
   if (!rwy.empty()) {
-    // runway already set, fine
-    return;
+    r = _plan->departureAirport()->getRunwayByIdent(rwy);
+  } else {
+    r = _plan->departureAirport()->findBestRunwayForPos(pos);
   }
   
-  FGRunway* r = _departure->findBestRunwayForPos(pos);
   if (!r) {
     return;
   }
   
-  departure->setStringValue("runway", r->ident().c_str());
+  _plan->setDeparture(r);
   SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: starting at " 
-    << _departure->ident() << " on runway " << r->ident());
+    << _plan->departureAirport()->ident() << " on runway " << r->ident());
 }
 
 bool FGRouteMgr::haveUserWaypoints() const
 {
-  return std::find_if(_route.begin(), _route.end(), NotGeneratedWayptPredicate()) != _route.end();
+  // FIXME
+  return false;
 }
 
 bool FGRouteMgr::activate()
 {
+  if (!_plan) {
+    SG_LOG(SG_AUTOPILOT, SG_WARN, "::activate, no flight plan defined");
+    return false;
+  }
+  
   if (isRouteActive()) {
     SG_LOG(SG_AUTOPILOT, SG_WARN, "duplicate route-activation, no-op");
     return false;
   }
  
-  _currentIndex = 0;
-  currentWaypointChanged();
-  
- /* double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM;
-  totalDistance->setDoubleValue(routeDistanceNm);
-  double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0);
-  if (cruiseSpeedKts > 1.0) {
-    // very very crude approximation, doesn't allow for climb / descent
-    // performance or anything else at all
-    ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0));
-  }
-  */
+  _plan->setCurrentIndex(0);
   active->setBoolValue(true);
   SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok");
   return true;
@@ -1223,7 +917,7 @@ bool FGRouteMgr::activate()
 
 void FGRouteMgr::sequence()
 {
-  if (!active->getBoolValue()) {
+  if (!_plan || !active->getBoolValue()) {
     SG_LOG(SG_AUTOPILOT, SG_ALERT, "trying to sequence waypoints with no active route");
     return;
   }
@@ -1232,13 +926,16 @@ void FGRouteMgr::sequence()
     return;
   }
   
-  _currentIndex++;
-  currentWaypointChanged();
+  _plan->setCurrentIndex(_plan->currentIndex() + 1);
 }
 
 bool FGRouteMgr::checkFinished()
 {
-  if (_currentIndex < (int) _route.size()) {
+  if (!_plan) {
+    return true;
+  }
+  
+  if (_plan->currentIndex() < _plan->numLegs()) {
     return false;
   }
   
@@ -1250,416 +947,226 @@ bool FGRouteMgr::checkFinished()
 
 void FGRouteMgr::jumpToIndex(int index)
 {
-  if ((index < 0) || (index >= (int) _route.size())) {
-    SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" << 
-      index << ") to FGRouteMgr::jumpToIndex");
+  if (!_plan) {
     return;
   }
-
-  if (_currentIndex == index) {
-    return; // no-op
-  }
   
-// all the checks out the way, go ahead and update state
-  _currentIndex = index;
-  currentWaypointChanged();
-  _currentWpt->fireValueChanged();
+  _plan->setCurrentIndex(index);
 }
 
 void FGRouteMgr::currentWaypointChanged()
 {
   Waypt* cur = currentWaypt();
-  Waypt* next = nextWaypt();
+  FlightPlan::Leg* next = _plan ? _plan->nextLeg() : NULL;
 
   wp0->getChild("id")->setStringValue(cur ? cur->ident() : "");
-  wp1->getChild("id")->setStringValue(next ? next->ident() : "");
+  wp1->getChild("id")->setStringValue(next ? next->waypoint()->ident() : "");
   
   _currentWpt->fireValueChanged();
-  SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << _currentIndex);
+  SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << currentIndex());
 }
 
-int FGRouteMgr::findWayptIndex(const SGGeod& aPos) const
-{  
-  for (int i=0; i<numWaypts(); ++i) {
-    if (_route[i]->matches(aPos)) {
-      return i;
-    }
+const char* FGRouteMgr::getDepartureICAO() const
+{
+  if (!_plan || !_plan->departureAirport()) {
+    return "";
   }
   
-  return -1;
-}
-
-Waypt* FGRouteMgr::currentWaypt() const
-{
-  if ((_currentIndex < 0) || (_currentIndex >= numWaypts()))
-      return NULL;
-  return wayptAtIndex(_currentIndex);
+  return _plan->departureAirport()->ident().c_str();
 }
 
-Waypt* FGRouteMgr::previousWaypt() const
+const char* FGRouteMgr::getDepartureName() const
 {
-  if (_currentIndex == 0) {
-    return NULL;
+  if (!_plan || !_plan->departureAirport()) {
+    return "";
   }
   
-  return wayptAtIndex(_currentIndex - 1);
+  return _plan->departureAirport()->name().c_str();
 }
 
-Waypt* FGRouteMgr::nextWaypt() const
+const char* FGRouteMgr::getDepartureRunway() const
 {
-  if ((_currentIndex < 0) || ((_currentIndex + 1) >= numWaypts())) {
-    return NULL;
+  if (_plan && _plan->departureRunway()) {
+    return _plan->departureRunway()->ident().c_str();
   }
   
-  return wayptAtIndex(_currentIndex + 1);
+  return "";
 }
 
-Waypt* FGRouteMgr::wayptAtIndex(int index) const
+void FGRouteMgr::setDepartureRunway(const char* aIdent)
 {
-  if ((index < 0) || (index >= numWaypts())) {
-    throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex");
+  FGAirport* apt = _plan->departureAirport();
+  if (!apt || (aIdent == NULL)) {
+    _plan->setDeparture(apt);
+  } else if (apt->hasRunwayWithIdent(aIdent)) {
+    _plan->setDeparture(apt->getRunwayByIdent(aIdent));
   }
-  
-  return _route[index];
 }
 
-SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const
+void FGRouteMgr::setDepartureICAO(const char* aIdent)
 {
-    if ((index < 0) || (index >= numWaypts())) {
-        throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex");
-    }
-    
-    return mirror->getChild("wp", index);
+  if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
+    _plan->setDeparture((FGAirport*) NULL);
+  } else {
+    _plan->setDeparture(FGAirport::findByIdent(aIdent));
+  }
 }
 
-bool FGRouteMgr::saveRoute(const SGPath& path)
+const char* FGRouteMgr::getSID() const
 {
-  SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str());
-  try {
-    SGPropertyNode_ptr d(new SGPropertyNode);
-    SGPath path(_pathNode->getStringValue());
-    d->setIntValue("version", 2);
-    
-    if (_departure) {
-      d->setStringValue("departure/airport", _departure->ident());
-      d->setStringValue("departure/sid", departure->getStringValue("sid"));
-      d->setStringValue("departure/runway", departure->getStringValue("runway"));
-    }
-    
-    if (_destination) {
-      d->setStringValue("destination/airport", _destination->ident());
-      d->setStringValue("destination/star", destination->getStringValue("star"));
-      d->setStringValue("destination/transition", destination->getStringValue("transition"));
-      d->setStringValue("destination/runway", destination->getStringValue("runway"));
-    }
-    
-  // route nodes
-    SGPropertyNode* routeNode = d->getChild("route", 0, true);
-    for (unsigned int i=0; i<_route.size(); ++i) {
-      Waypt* wpt = _route[i];
-      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;
+  if (_plan && _plan->sid()) {
+    return _plan->sid()->ident().c_str();
   }
+  
+  return "";
 }
 
-bool FGRouteMgr::loadRoute(const SGPath& path)
+void FGRouteMgr::setSID(const char* aIdent)
 {
-  if (!path.exists())
-  {
-      SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str()
-              << "'. The file does not exist.");
-      return false;
-  }
-    
-  // deactivate route first
-  active->setBoolValue(false);
-  
-  SGPropertyNode_ptr routeData(new SGPropertyNode);
+  FGAirport* apt = _plan->departureAirport();
+  if (!apt || (aIdent == NULL)) {
+    _plan->setSID((SID*) NULL);
+    return;
+  } 
   
-  SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str());
+  string ident(aIdent);
+  size_t hyphenPos = ident.find('-');
+  if (hyphenPos != string::npos) {
+    string sidIdent = ident.substr(0, hyphenPos);
+    string transIdent = ident.substr(hyphenPos + 1);
     
-  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;
-      }
+    SID* sid = apt->findSIDWithIdent(sidIdent);
+    Transition* trans = sid ? sid->findTransitionByName(transIdent) : NULL;
+    _plan->setSID(trans);
+  } else {
+    _plan->setSID(apt->findSIDWithIdent(aIdent));
   }
-
-  update_mirror();
-
-  return Status;
 }
 
-void FGRouteMgr::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
-{
-  // departure nodes
-  SGPropertyNode* dep = routeData->getChild("departure");
-  if (dep) {
-    string depIdent = dep->getStringValue("airport");
-    _departure = (FGAirport*) fgFindAirportID(depIdent);
-    departure->setStringValue("runway", dep->getStringValue("runway"));
-    departure->setStringValue("sid", dep->getStringValue("sid"));
-    departure->setStringValue("transition", dep->getStringValue("transition"));
-  }
-  
-// destination
-  SGPropertyNode* dst = routeData->getChild("destination");
-  if (dst) {
-    _destination = (FGAirport*) fgFindAirportID(dst->getStringValue("airport"));
-    destination->setStringValue("runway", dst->getStringValue("runway"));
-    destination->setStringValue("star", dst->getStringValue("star"));
-    destination->setStringValue("transition", dst->getStringValue("transition"));
-  }
-
-// alternate
-  SGPropertyNode* alt = routeData->getChild("alternate");
-  if (alt) {
-    alternate->setStringValue(alt->getStringValue("airport"));
-  } // of cruise data loading
-  
-// 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 FGRouteMgr::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
-{
-  loadXMLRouteHeader(routeData);
-  
-// route nodes
-  WayptVec wpts;
-  SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);    
-  for (int i=0; i<routeNode->nChildren(); ++i) {
-    SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
-    WayptRef wpt = Waypt::createFromProperties(NULL, wpNode);
-    wpts.push_back(wpt);
-  } // of route iteration
-  
-  _route = wpts;
-}
-
-void FGRouteMgr::loadVersion1XMLRoute(SGPropertyNode_ptr routeData)
+const char* FGRouteMgr::getDestinationICAO() const
 {
-  loadXMLRouteHeader(routeData);
-
-// route nodes
-  WayptVec wpts;
-  SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);    
-  for (int i=0; i<routeNode->nChildren(); ++i) {
-    SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
-    WayptRef wpt = parseVersion1XMLWaypt(wpNode);
-    wpts.push_back(wpt);
-  } // of route iteration
-  
-  _route = wpts;
+  if (!_plan || !_plan->destinationAirport()) {
+    return "";
+  }
+  
+  return _plan->destinationAirport()->ident().c_str();
 }
 
-WayptRef FGRouteMgr::parseVersion1XMLWaypt(SGPropertyNode* aWP)
+const char* FGRouteMgr::getDestinationName() const
 {
-  SGGeod lastPos;
-  if (!_route.empty()) {
-    lastPos = _route.back()->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 += magvar->getDoubleValue();
-      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);
+  if (!_plan || !_plan->destinationAirport()) {
+    return "";
   }
   
-  double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
-  if (altFt > -9990.0) {
-    w->setAltitude(altFt, RESTRICT_AT);
-  }
-
-  return w;
+  return _plan->destinationAirport()->name().c_str();
 }
 
-bool FGRouteMgr::loadPlainTextRoute(const SGPath& path)
+void FGRouteMgr::setDestinationICAO(const char* aIdent)
 {
-  try {
-    sg_gzifstream in(path.str().c_str());
-    if (!in.is_open()) {
-        throw sg_io_exception("Cannot open file for reading.");
-    }
-  
-    WayptVec wpts;
-    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 + "'.");
-      }
-      
-      wpts.push_back(w);
-    } // of line iteration
-  
-    _route = wpts;
-    return true;
-  } catch (sg_exception& e) {
-    SG_LOG(SG_IO, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage());
-    return false;
+  if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
+    _plan->setDestination((FGAirport*) NULL);
+  } else {
+    _plan->setDestination(FGAirport::findByIdent(aIdent));
   }
 }
 
-const char* FGRouteMgr::getDepartureICAO() const
+const char* FGRouteMgr::getDestinationRunway() const
 {
-  if (!_departure) {
-    return "";
+  if (_plan && _plan->destinationRunway()) {
+    return _plan->destinationRunway()->ident().c_str();
   }
   
-  return _departure->ident().c_str();
+  return "";
 }
 
-const char* FGRouteMgr::getDepartureName() const
+void FGRouteMgr::setDestinationRunway(const char* aIdent)
 {
-  if (!_departure) {
-    return "";
+  FGAirport* apt = _plan->destinationAirport();
+  if (!apt || (aIdent == NULL)) {
+    _plan->setDestination(apt);
+  } else if (apt->hasRunwayWithIdent(aIdent)) {
+    _plan->setDestination(apt->getRunwayByIdent(aIdent));
   }
-  
-  return _departure->name().c_str();
 }
 
-void FGRouteMgr::setDepartureICAO(const char* aIdent)
+const char* FGRouteMgr::getApproach() const
 {
-  if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
-    _departure = NULL;
-  } else {
-    _departure = FGAirport::findByIdent(aIdent);
+  if (_plan && _plan->approach()) {
+    return _plan->approach()->ident().c_str();
   }
   
-  departureChanged();
+  return "";
 }
 
-const char* FGRouteMgr::getDestinationICAO() const
+void FGRouteMgr::setApproach(const char* aIdent)
 {
-  if (!_destination) {
-    return "";
+  FGAirport* apt = _plan->destinationAirport();
+  if (!apt || (aIdent == NULL)) {
+    _plan->setApproach(NULL);
+  } else {
+    _plan->setApproach(apt->findApproachWithIdent(aIdent));
   }
-  
-  return _destination->ident().c_str();
 }
 
-const char* FGRouteMgr::getDestinationName() const
+const char* FGRouteMgr::getSTAR() const
 {
-  if (!_destination) {
-    return "";
+  if (_plan && _plan->star()) {
+    return _plan->star()->ident().c_str();
   }
   
-  return _destination->name().c_str();
+  return "";
 }
 
-void FGRouteMgr::setDestinationICAO(const char* aIdent)
+void FGRouteMgr::setSTAR(const char* aIdent)
 {
-  if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
-    _destination = NULL;
+  FGAirport* apt = _plan->destinationAirport();
+  if (!apt || (aIdent == NULL)) {
+    _plan->setSTAR((STAR*) NULL);
+    return;
+  } 
+  
+  string ident(aIdent);
+  size_t hyphenPos = ident.find('-');
+  if (hyphenPos != string::npos) {
+    string starIdent = ident.substr(0, hyphenPos);
+    string transIdent = ident.substr(hyphenPos + 1);
+    
+    STAR* star = apt->findSTARWithIdent(starIdent);
+    Transition* trans = star ? star->findTransitionByName(transIdent) : NULL;
+    _plan->setSTAR(trans);
   } else {
-    _destination = FGAirport::findByIdent(aIdent);
+    _plan->setSTAR(apt->findSTARWithIdent(aIdent));
   }
-  
-  arrivalChanged();
 }
 
-FGAirportRef FGRouteMgr::departureAirport() const
+WayptRef FGRouteMgr::waypointFromString(const std::string& target)
 {
-    return _departure;
+  return _plan->waypointFromString(target);
 }
 
-FGAirportRef FGRouteMgr::destinationAirport() const
+double FGRouteMgr::getDepartureFieldElevation() const
 {
-    return _destination;
+  if (!_plan || !_plan->departureAirport()) {
+    return 0.0;
+  }
+  
+  return _plan->departureAirport()->elevation();
 }
 
-FGRunway* FGRouteMgr::departureRunway() const
+double FGRouteMgr::getDestinationFieldElevation() const
 {
-    if (!_departure) {
-        return NULL;
-    }
-    
-    string runwayId(departure->getStringValue("runway"));
-    if (!_departure->hasRunwayWithIdent(runwayId)) {
-        return NULL;
-    }
-    
-    return _departure->getRunwayByIdent(runwayId);
+  if (!_plan || !_plan->destinationAirport()) {
+    return 0.0;
+  }
+  
+  return _plan->destinationAirport()->elevation();
 }
 
-FGRunway* FGRouteMgr::destinationRunway() const
+SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const
 {
-    if (!_destination) {
-        return NULL;
-    }
-    
-    string runwayId(destination->getStringValue("runway"));
-    if (!_destination->hasRunwayWithIdent(runwayId)) {
-        return NULL;
-    }
-    
-    return _destination->getRunwayByIdent(runwayId);
+  if ((index < 0) || (index >= numWaypts())) {
+    throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex");
+  }
+  
+  return mirror->getChild("wp", index);
 }
-
index b0f14ba79da47f795f5720e9eb6cd838c4d41f6d..840ec31609a44fcc9d79bd01c8f60dd172c149d6 100644 (file)
@@ -44,7 +44,8 @@ typedef SGSharedPtr<FGAirport> FGAirportRef;
  * 
  */
 
-class FGRouteMgr : public SGSubsystem
+class FGRouteMgr : public SGSubsystem, 
+                   public flightgear::FlightPlan::Delegate
 {
 public:
   FGRouteMgr();
@@ -55,11 +56,6 @@ public:
   void bind ();
   void unbind ();
   void update (double dt);
-
-  void insertWayptAtIndex(flightgear::Waypt* aWpt, int aIndex);
-  flightgear::WayptRef removeWayptAtIndex(int index);
-  
-  void clearRoute();
   
   typedef enum {
     ROUTE_HIGH_AIRWAYS, ///< high-level airways routing
@@ -74,34 +70,31 @@ public:
    * used as the final waypoint.
    */
   bool routeToIndex(int index, RouteType aRouteType);
-
-  void autoRoute();
         
   bool isRouteActive() const;
          
-  int currentIndex() const
-    { return _currentIndex; }
-    
+  int currentIndex() const;
+  
+  void setFlightPlan(flightgear::FlightPlan* plan);
+  flightgear::FlightPlan* flightPlan() const;
+  
+  void clearRoute();
+  
   flightgear::Waypt* currentWaypt() const;
-  flightgear::Waypt* nextWaypt() const;
-  flightgear::Waypt* previousWaypt() const;
   
-  const flightgear::WayptVec& waypts() const
-    { return _route; }
+  int numLegs() const;
   
+// deprecated
   int numWaypts() const
-    { return _route.size(); }
-    
+  { return numLegs(); }
+  
+// deprecated
   flightgear::Waypt* wayptAtIndex(int index) const;
-             
+  
   SGPropertyNode_ptr wayptNodeAtIndex(int index) const;
-             
-  /**
-   * Find a waypoint in the route, by position, and return its index, or
-   * -1 if no matching waypoint was found in the route.
-   */
-  int findWayptIndex(const SGGeod& aPos) const;
-        
+  
+  void removeLegAtIndex(int aIndex);
+  
   /**
    * Activate a built route. This checks for various mandatory pieces of
    * data, such as departure and destination airports, and creates waypoints
@@ -125,38 +118,20 @@ public:
   bool saveRoute(const SGPath& p);
   bool loadRoute(const SGPath& p);
   
+  flightgear::WayptRef waypointFromString(const std::string& target);
+  
   /**
    * Helper command to setup current airport/runway if necessary
    */
   void initAtPosition();
-  
-    /**
-     * 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
-     */
-    flightgear::WayptRef waypointFromString(const std::string& target);
-    
-    FGAirportRef departureAirport() const;
-    FGAirportRef destinationAirport() const;
-    
-    FGRunway* departureRunway() const;
-    FGRunway* destinationRunway() const;
+
 private:
-  flightgear::WayptVec _route;
-  int _currentIndex;
+    flightgear::FlightPlan* _plan;
   
     time_t _takeoffTime;
     time_t _touchdownTime;
-    FGAirportRef _departure;
-    FGAirportRef _destination;
-    
+
     // automatic inputs
-    SGPropertyNode_ptr lon;
-    SGPropertyNode_ptr lat;
-    SGPropertyNode_ptr alt;
     SGPropertyNode_ptr magvar;
     
     // automatic outputs    
@@ -215,11 +190,11 @@ private:
     
     InputListener *listener;
     SGPropertyNode_ptr mirror;    
-    
-    void departureChanged();
+  
+    virtual void departureChanged();
     void buildDeparture(flightgear::WayptRef enroute, flightgear::WayptVec& wps);
     
-    void arrivalChanged();
+    virtual void arrivalChanged();
     void buildArrival(flightgear::WayptRef enroute, flightgear::WayptVec& wps);
     
     /**
@@ -227,16 +202,11 @@ private:
      * modified (waypoints added, inserted, removed). Notably, this fires the
      * 'edited' signal.
      */
-    void waypointsChanged();
+    virtual void waypointsChanged();
     
     void update_mirror();
     
-    void currentWaypointChanged();
-    
-    /**
-     * Parse a route/wp node (from a saved, property-lsit formatted route)
-     */
-    void parseRouteWaypoint(SGPropertyNode* aWP);
+    virtual void currentWaypointChanged();
     
     /**
      * Check if we've reached the final waypoint. 
@@ -244,14 +214,6 @@ private:
      */
     bool checkFinished();
     
-    
-    bool loadPlainTextRoute(const SGPath& path);
-    
-    void loadVersion1XMLRoute(SGPropertyNode_ptr routeData);
-    void loadVersion2XMLRoute(SGPropertyNode_ptr routeData);
-    void loadXMLRouteHeader(SGPropertyNode_ptr routeData);
-    flightgear::WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP);
-    
     /**
      * Predicate for helping the UI - test if at least one waypoint was
      * entered by the user (as opposed to being generated by the route-manager)
@@ -263,12 +225,27 @@ private:
     const char* getDepartureName() const;
     void setDepartureICAO(const char* aIdent);
     
+    const char* getDepartureRunway() const;
+    void setDepartureRunway(const char* aIdent);
+  
+    const char* getSID() const;
+    void setSID(const char* aIdent);
+  
     const char* getDestinationICAO() const;
     const char* getDestinationName() const;
     void setDestinationICAO(const char* aIdent);
 
-    PropertyWatcher* _departureWatcher;
-    PropertyWatcher* _arrivalWatcher;
+    const char* getDestinationRunway() const;
+    void setDestinationRunway(const char* aIdent);
+  
+    const char* getApproach() const;
+    void setApproach(const char* aIdent);
+  
+    const char* getSTAR() const;
+    void setSTAR(const char* aIdent);
+  
+    double getDepartureFieldElevation() const;  
+    double getDestinationFieldElevation() const;  
 };
 
 
index a0c6d2b4cbdc0db0e83491fa98e382a59e81adf8..99e536a4f4fd8b060d1a0353d005ab6e474193ec 100644 (file)
@@ -682,7 +682,7 @@ void MapWidget::paintRoute()
     return;
   }
 
-  RoutePath path(_route->waypts());
+  RoutePath path(_route->flightPlan());
 
 // first pass, draw the actual lines
   glLineWidth(2.0);
index 235ef960f572416f7aa9e4de057c2bbb819be191..57b741169d49ff9617ac9460f85829d0b9d68c7c 100644 (file)
@@ -37,20 +37,19 @@ enum {
 static const double BLINK_TIME = 0.3;
 static const int DRAG_START_DISTANCE_PX = 5;
   
-class RouteManagerWaypointModel : 
+class FlightPlanWaypointModel : 
   public WaypointList::Model, 
   public SGPropertyChangeListener
 {
 public:
-  RouteManagerWaypointModel()
-  {
-    _rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-    
+  FlightPlanWaypointModel(flightgear::FlightPlan* fp) :
+    _fp(fp)
+  {    
     SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
     routeEdited->addChangeListener(this);
   }
   
-  virtual ~RouteManagerWaypointModel()
+  ~FlightPlanWaypointModel()
   {
     SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true);
     routeEdited->removeChangeListener(this);
@@ -59,12 +58,12 @@ public:
 // implement WaypointList::Model
   virtual unsigned int numWaypoints() const
   {
-    return _rm->numWaypts();
+    return _fp->numLegs();
   }
   
   virtual int currentWaypoint() const
   {
-    return _rm->currentIndex();
+    return _fp->currentIndex();
   }
   
   virtual flightgear::Waypt* waypointAt(unsigned int index) const
@@ -73,12 +72,12 @@ public:
       return NULL;
     }
     
-    return _rm->wayptAtIndex(index);
+    return _fp->legAtIndex(index)->waypoint();
   }
 
   virtual void deleteAt(unsigned int index)
   {
-    _rm->removeWayptAtIndex(index);
+    _fp->deleteIndex(index);
   }
   
   virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int destIndex)
@@ -89,13 +88,15 @@ public:
     }
     
     unsigned int currentWpIndex = currentWaypoint();
-    WayptRef w(_rm->removeWayptAtIndex(srcIndex));
+    WayptRef w(waypointAt(currentWpIndex));
+    _fp->deleteIndex(currentWpIndex);
+    
     SG_LOG(SG_GENERAL, SG_INFO, "wpt:" << w->ident());
-    _rm->insertWayptAtIndex(w, destIndex);
+    _fp->insertWayptAtIndex(w, destIndex);
 
     if (srcIndex == currentWpIndex) {
         // current waypoint was moved
-        _rm->jumpToIndex(destIndex);
+        _fp->setCurrentIndex(destIndex);
     }
   }
   
@@ -112,7 +113,7 @@ public:
     }
   }
 private:
-  FGRouteMgr* _rm;
+  flightgear::FlightPlan* _fp;
   SGCallback* _cb;
 };
 
@@ -152,7 +153,9 @@ WaypointList::WaypointList(int x, int y, int width, int height) :
 {
   // pretend to be a list, so fgPopup doesn't mess with our mouse events
   type |= PUCLASS_LIST;  
-  setModel(new RouteManagerWaypointModel());
+  flightgear::FlightPlan* fp = 
+    static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"))->flightPlan();
+  setModel(new FlightPlanWaypointModel(fp));
   setSize(width, height);
   setValue(-1);
   
index 015f8ff595d0f173fdef4ee6eae1876c8b6ee882..2528a7ef4c574a20da9ece748eea3734c8953424 100644 (file)
@@ -159,7 +159,6 @@ public:
     virtual void valueChanged (SGPropertyNode * prop)
     {
         _nd->invalidatePositionedCache();
-        SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache");
     }
 private:
     NavDisplay* _nd;
@@ -174,7 +173,6 @@ public:
   
   virtual void valueChanged (SGPropertyNode * prop)
   {
-    SG_LOG(SG_INSTR, SG_INFO, "forcing NavDisplay update");
     _nd->forceUpdate();
   }
 private:
@@ -683,9 +681,6 @@ NavDisplay::update (double delta_time_sec)
         SGVec3d cartNow(SGVec3d::fromGeod(_pos));
         double movedNm = dist(_cachedPos, cartNow) * SG_METER_TO_NM;
         _cachedItemsValid = (movedNm < 1.0);
-        if (!_cachedItemsValid) {
-            SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache due to moving: " << movedNm);
-        }
     }
     
   _vertices->clear();
@@ -951,7 +946,6 @@ public:
 void NavDisplay::findItems()
 {
     if (!_cachedItemsValid) {
-        SG_LOG(SG_INSTR, SG_INFO, "re-validating NavDisplay cache");
         Filter filt;
         filt.minRunwayLengthFt = 2000;
         _itemsInRange = FGPositioned::findWithinRange(_pos, _rangeNm, &filt);
@@ -967,29 +961,31 @@ void NavDisplay::findItems()
 void NavDisplay::processRoute()
 {
     _routeSources.clear();
-    RoutePath path(_route->waypts());
+    flightgear::FlightPlan* fp = _route->flightPlan();
+    RoutePath path(fp);
     int current = _route->currentIndex();
     
-    for (int w=0; w<_route->numWaypts(); ++w) {
-        flightgear::WayptRef wpt(_route->wayptAtIndex(w));
+    for (int l=0; l<fp->numLegs(); ++l) {
+        flightgear::FlightPlan::Leg* leg = fp->legAtIndex(l);
+        flightgear::WayptRef wpt(leg->waypoint());
         _routeSources.insert(wpt->source());
         
         string_set state;
         state.insert("on-active-route");
         
-        if (w < current) {
+        if (l < current) {
             state.insert("passed");
         }
         
-        if (w == current) {
+        if (l == current) {
             state.insert("current-wp");
         }
         
-        if (w > current) {
+        if (l > current) {
             state.insert("future");
         }
         
-        if (w == (current + 1)) {
+        if (l == (current + 1)) {
             state.insert("next-wp");
         }
         
@@ -999,8 +995,12 @@ void NavDisplay::processRoute()
             return; // no rules matched, we can skip this item
         }
 
-        SGGeod g = path.positionForIndex(w);
-        SGPropertyNode* vars = _route->wayptNodeAtIndex(w);
+        SGGeod g = path.positionForIndex(l);
+        SGPropertyNode* vars = _route->wayptNodeAtIndex(l);
+        if (!vars) {
+          continue; // shouldn't happen, but let's guard against it
+        }
+      
         double heading;
         computeWayptPropsAndHeading(wpt, g, vars, heading);
 
@@ -1009,7 +1009,7 @@ void NavDisplay::processRoute()
             addSymbolInstance(projected, heading, r->getDefinition(), vars);
             
             if (r->getDefinition()->drawRouteLeg) {
-                SGGeodVec gv(path.pathForIndex(w));
+                SGGeodVec gv(path.pathForIndex(l));
                 if (!gv.empty()) {
                     osg::Vec2 pr = projectGeod(gv[0]);
                     for (unsigned int i=1; i<gv.size(); ++i) {
@@ -1189,6 +1189,7 @@ void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states)
         states.insert("on-active-route");
     }
     
+    flightgear::FlightPlan* fp = _route->flightPlan();
     switch (pos->type()) {
     case FGPositioned::VOR:
     case FGPositioned::LOC:
@@ -1209,21 +1210,21 @@ void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states)
         // mark alternates!
         // once the FMS system has some way to tell us about them, of course
         
-        if (pos == _route->departureAirport()) {
+        if (pos == fp->departureAirport()) {
             states.insert("departure");
         }
         
-        if (pos == _route->destinationAirport()) {
+        if (pos == fp->destinationAirport()) {
             states.insert("destination");
         }
         break;
     
     case FGPositioned::RUNWAY:
-        if (pos == _route->departureRunway()) {
+        if (pos == fp->departureRunway()) {
             states.insert("departure");
         }
         
-        if (pos == _route->destinationRunway()) {
+        if (pos == fp->destinationRunway()) {
             states.insert("destination");
         }
         break;
index d9acf4f9fb3dfe540697460da5f37bff03a6b6d3..4d7b79b4f52a817d0cbb306808493a6bfec8c3f9 100644 (file)
@@ -724,7 +724,7 @@ void GPS::routeManagerSequenced()
   SG_LOG(SG_INSTR, SG_INFO, "GPS waypoint index is now " << index);
   
   if (index > 0) {
-    _prevWaypt = _routeMgr->previousWaypt();
+    _prevWaypt = _routeMgr->wayptAtIndex(index - 1);
     if (_prevWaypt->flag(WPT_DYNAMIC)) {
       _wp0_position = _indicated_pos;
     } else {
@@ -829,7 +829,7 @@ void GPS::updateOverflight()
     
     // check for wp1 being on active route - resume leg mode
     if (_routeMgr->isRouteActive()) {
-      int index = _routeMgr->findWayptIndex(_currentWaypt->position());
+      int index = _routeMgr->flightPlan()->findWayptIndex(_currentWaypt->position());
       if (index >= 0) {
         SG_LOG(SG_INSTR, SG_INFO, "GPS DTO, resuming LEG mode at wp:" << index);
         _mode = "leg";
@@ -881,7 +881,7 @@ void GPS::computeTurnData()
     return;
   }
   
-  WayptRef next = _routeMgr->nextWaypt();
+  WayptRef next = _routeMgr->wayptAtIndex(_routeMgr->currentIndex() + 1);
   if (!next || next->flag(WPT_DYNAMIC)) {
     _anticipateTurn = false;
     return;
@@ -1684,7 +1684,7 @@ void GPS::insertWaypointAtIndex(int aIndex)
   string ident = _scratchNode->getStringValue("ident");
 
   WayptRef wpt = new BasicWaypt(_scratchPos, ident, NULL);
-  _routeMgr->insertWayptAtIndex(wpt, aIndex);
+  _routeMgr->flightPlan()->insertWayptAtIndex(wpt, aIndex);
 }
 
 void GPS::removeWaypointAtIndex(int aIndex)
@@ -1693,7 +1693,7 @@ void GPS::removeWaypointAtIndex(int aIndex)
     throw sg_range_exception("GPS::removeWaypointAtIndex: index out of bounds");
   }
   
-  _routeMgr->removeWayptAtIndex(aIndex);
+  _routeMgr->removeLegAtIndex(aIndex);
 }
 
 void GPS::tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef, 
index e0f30e92a1edf1f20e8af537191eb97d4da5105d..f893b9413ccabdb7d6943d7024465c2c1fa90cd1 100644 (file)
@@ -317,7 +317,9 @@ public:
     double curAlt = _rnav->position().getElevationFt();
     
     switch (_waypt->altitudeRestriction()) {
-    case RESTRICT_AT: {
+    case RESTRICT_AT: 
+    case RESTRICT_COMPUTED:  
+    {
       double d = curAlt - _waypt->altitudeFt();
       if (fabs(d) < 50.0) {
         SG_LOG(SG_INSTR, SG_INFO, "ConstHdgToAltCtl, reached target altitude " << _waypt->altitudeFt());
@@ -339,11 +341,7 @@ public:
       }
       break;
     
-    case RESTRICT_NONE:
-      assert(false);
-      break;
-    case SPEED_RESTRICT_MACH:
-      assert(false);
+    default:
       break;
     }
   }
index bd5cce9b78934d07e1a4cd7cc8733451df4b0df3..cd806a65e65e75f574366536766c659fefe9d20c 100644 (file)
@@ -12,7 +12,8 @@ set(SOURCES
        route.cxx
        routePath.cxx
        waypoint.cxx
-)
+    LevelDXML.cxx
+       )
 
 set(HEADERS
     airways.hxx
@@ -26,6 +27,7 @@ set(HEADERS
        route.hxx
        routePath.hxx
        waypoint.hxx
-)
+    LevelDXML.hxx
+    )
 
 flightgear_component(Navaids "${SOURCES}" "${HEADERS}")
\ No newline at end of file
diff --git a/src/Navaids/LevelDXML.cxx b/src/Navaids/LevelDXML.cxx
new file mode 100644 (file)
index 0000000..49ab15d
--- /dev/null
@@ -0,0 +1,337 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "LevelDXML.hxx"
+
+#include <boost/algorithm/string.hpp>
+
+#include <simgear/structure/exception.hxx>
+#include <simgear/misc/sg_path.hxx>
+
+#include <Navaids/waypoint.hxx>
+#include <Airports/simple.hxx>
+
+using std::string;
+using std::vector;
+
+namespace flightgear
+{
+
+NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath):
+  _airport(aApt),
+  _path(aPath),
+  _sid(NULL),
+  _star(NULL),
+  _approach(NULL),
+  _transition(NULL),
+  _procedure(NULL)
+{
+}
+
+void NavdataVisitor::startXML()
+{
+}
+
+void NavdataVisitor::endXML()
+{
+}
+
+void NavdataVisitor::startElement(const char* name, const XMLAttributes &atts)
+{
+  _text.clear();
+  string tag(name);
+  if (tag == "Airport") {
+    string icao(atts.getValue("ICAOcode"));
+    if (_airport->ident() != icao) {
+      throw sg_format_exception("Airport and ICAO mismatch", icao, _path.str());
+    }
+  } else if (tag == "Sid") {
+    string ident(atts.getValue("Name"));
+    _sid = new SID(ident, _airport);
+    _procedure = _sid;
+    _waypoints.clear();
+    processRunways(_sid, atts);
+  } else if (tag == "Star") {
+    string ident(atts.getValue("Name"));
+    _star = new STAR(ident, _airport);
+    _procedure = _star;
+    _waypoints.clear();
+    processRunways(_star, atts);
+  } else if ((tag == "Sid_Waypoint") ||
+      (tag == "App_Waypoint") ||
+      (tag == "Star_Waypoint") ||
+      (tag == "AppTr_Waypoint") ||
+      (tag == "SidTr_Waypoint") ||
+      (tag == "RwyTr_Waypoint"))
+  {
+    // reset waypoint data
+    _speed = 0.0;
+    _altRestrict = RESTRICT_NONE;
+    _altitude = 0.0;
+  } else if (tag == "Approach") {
+    _ident = atts.getValue("Name");
+    _waypoints.clear();
+    ProcedureType ty = PROCEDURE_APPROACH_RNAV;
+    _approach = new Approach(_ident, ty);
+    _procedure = _approach;
+  } else if ((tag == "Sid_Transition") || 
+             (tag == "App_Transition") ||
+             (tag == "Star_Transition")) {
+    _transIdent = atts.getValue("Name");
+    _transition = new Transition(_transIdent, PROCEDURE_TRANSITION, _procedure);
+    _transWaypts.clear();
+  } else if (tag == "RunwayTransition") {
+    _transIdent = atts.getValue("Runway");
+    _transition = new Transition(_transIdent, PROCEDURE_RUNWAY_TRANSITION, _procedure);
+    _transWaypts.clear();
+  } else {
+    
+  }
+}
+
+void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts)
+{
+  string v("All");
+  if (atts.hasAttribute("Runways")) {
+    v = atts.getValue("Runways");
+  }
+  
+  if (v == "All") {
+    for (unsigned int r=0; r<_airport->numRunways(); ++r) {
+      aProc->addRunway(_airport->getRunwayByIndex(r));
+    }
+    return;
+  }
+  
+  vector<string> rwys;
+  boost::split(rwys, v, boost::is_any_of(" ,"));
+  for (unsigned int r=0; r<rwys.size(); ++r) {
+    FGRunway* rwy = _airport->getRunwayByIdent(rwys[r]);
+    aProc->addRunway(rwy);
+  }
+}
+
+void NavdataVisitor::endElement(const char* name)
+{
+  string tag(name);
+  if ((tag == "Sid_Waypoint") ||
+      (tag == "App_Waypoint") ||
+      (tag == "Star_Waypoint"))
+  {
+    _waypoints.push_back(buildWaypoint(_procedure));
+  } else if ((tag == "AppTr_Waypoint") || 
+             (tag == "SidTr_Waypoint") ||
+             (tag == "RwyTr_Waypoint") ||
+             (tag == "StarTr_Waypoint")) 
+  {
+    _transWaypts.push_back(buildWaypoint(_transition));
+  } else if (tag == "Sid_Transition") {
+    assert(_sid);
+    // SID waypoints are stored backwards, to share code with STARs
+    std::reverse(_transWaypts.begin(), _transWaypts.end());
+    _transition->setPrimary(_transWaypts);
+    _sid->addTransition(_transition);
+  } else if (tag == "Star_Transition") {
+    assert(_star);
+    _transition->setPrimary(_transWaypts);
+    _star->addTransition(_transition);
+  } else if (tag == "App_Transition") {
+    assert(_approach);
+    _transition->setPrimary(_transWaypts);
+    _approach->addTransition(_transition);
+  } else if (tag == "RunwayTransition") {
+    ArrivalDeparture* ad;
+    if (_sid) {
+      // SID waypoints are stored backwards, to share code with STARs
+      std::reverse(_transWaypts.begin(), _transWaypts.end());
+      ad = _sid;
+    } else {
+      ad = _star;
+    }
+
+    _transition->setPrimary(_transWaypts);
+    FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent);
+    ad->addRunwayTransition(rwy, _transition);
+  } else if (tag == "Approach") {
+    finishApproach();
+  } else if (tag == "Sid") {
+    finishSid();
+  } else if (tag == "Star") {
+    finishStar();  
+  } else if (tag == "Longitude") {
+    _longitude = atof(_text.c_str());
+  } else if (tag == "Latitude") {
+    _latitude = atof(_text.c_str());
+  } else if (tag == "Name") {
+    _wayptName = _text;
+  } else if (tag == "Type") {
+    _wayptType = _text;
+  } else if (tag == "Speed") {
+    _speed = atoi(_text.c_str());
+  } else if (tag == "Altitude") {
+    _altitude = atof(_text.c_str());
+  } else if (tag == "AltitudeRestriction") {
+    if (_text == "at") {
+      _altRestrict = RESTRICT_AT;
+    } else if (_text == "above") {
+      _altRestrict = RESTRICT_ABOVE;
+    } else if (_text == "below") {
+      _altRestrict = RESTRICT_BELOW;
+    } else {
+      throw sg_format_exception("Unrecognized altitude restriction", _text);
+    }
+  } else if (tag == "Hld_Rad_or_Inbd") {
+    if (_text == "Inbd") {
+      _holdRadial = -1.0;
+    }
+  } else if (tag == "Hld_Time_or_Dist") {
+    _holdDistance = (_text == "Dist");
+  } else if (tag == "Hld_Rad_value") {
+    _holdRadial = atof(_text.c_str());
+  } else if (tag == "Hld_Turn") {
+    _holdRighthanded = (_text == "Right");
+  } else if (tag == "Hld_td_value") {
+    _holdTD = atof(_text.c_str());
+  } else if (tag == "Hdg_Crs_value") {
+    _course = atof(_text.c_str());
+  } else if (tag == "DMEtoIntercept") {
+    _dmeDistance = atof(_text.c_str());
+  } else if (tag == "RadialtoIntercept") {
+    _radial = atof(_text.c_str());
+  } else {
+    
+  }
+}
+
+Waypt* NavdataVisitor::buildWaypoint(RouteBase* owner)
+{
+  Waypt* wp = NULL;
+  if (_wayptType == "Normal") {
+    // new LatLonWaypoint
+    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
+    wp = new BasicWaypt(pos, _wayptName, owner);
+  } else if (_wayptType == "Runway") {
+    string ident = _wayptName.substr(2);
+    FGRunwayRef rwy = _airport->getRunwayByIdent(ident);
+    wp = new RunwayWaypt(rwy, owner);
+  } else if (_wayptType == "Hold") {
+    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
+    Hold* h = new Hold(pos, _wayptName, owner);
+    wp = h;
+    if (_holdRighthanded) {
+      h->setRightHanded();
+    } else {
+      h->setLeftHanded();
+    }
+    
+    if (_holdDistance) {
+      h->setHoldDistance(_holdTD);
+    } else {
+      h->setHoldTime(_holdTD * 60.0);
+    }
+    
+    if (_holdRadial >= 0.0) {
+      h->setHoldRadial(_holdRadial);
+    }
+  } else if (_wayptType == "Vectors") {
+    wp = new ATCVectors(owner, _airport);
+  } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) {
+    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
+    wp = new RadialIntercept(owner, _wayptName, pos, _course, _radial);
+  } else if (_wayptType == "DmeIntc") {
+    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
+    wp = new DMEIntercept(owner, _wayptName, pos, _course, _dmeDistance);
+  } else if (_wayptType == "ConstHdgtoAlt") {
+    wp = new HeadingToAltitude(owner, _wayptName, _course);
+  } else if (_wayptType == "PBD") {
+    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2;
+    double az2;
+    SGGeodesy::direct(pos, _course, _dmeDistance, pos2, az2);
+    wp = new BasicWaypt(pos2, _wayptName, owner);
+  } else {
+    SG_LOG(SG_GENERAL, SG_ALERT, "implement waypoint type:" << _wayptType);
+    throw sg_format_exception("Unrecognized waypt type", _wayptType);
+  }
+  
+  assert(wp);
+  if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) {
+    wp->setAltitude(_altitude,_altRestrict);
+  }
+  
+  if (_speed > 0.0) {
+    wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW?
+  }
+  
+  return wp;
+}
+
+void NavdataVisitor::finishApproach()
+{
+  WayptVec::iterator it;
+  FGRunwayRef rwy;
+  
+// find the runway node
+  for (it = _waypoints.begin(); it != _waypoints.end(); ++it) {
+    FGPositionedRef navid = (*it)->source();
+    if (!navid) {
+      continue;
+    }
+    
+    if (navid->type() == FGPositioned::RUNWAY) {
+      rwy = (FGRunway*) navid.get();
+      break;
+    }
+  }
+  
+  if (!rwy) {
+    throw sg_format_exception("Malformed approach, no runway waypt", _ident);
+  }
+  
+  WayptVec primary(_waypoints.begin(), it);
+  // erase all points up to and including the runway, to leave only the
+  // missed segments
+  _waypoints.erase(_waypoints.begin(), ++it);
+  
+  _approach->setRunway(rwy);
+  _approach->setPrimaryAndMissed(primary, _waypoints);
+  _airport->addApproach(_approach);
+  _approach = NULL;
+}
+
+void NavdataVisitor::finishSid()
+{
+  // reverse order, because that's how we deal with commonality between
+  // STARs and SIDs. SID::route undoes  this
+  std::reverse(_waypoints.begin(), _waypoints.end());
+  _sid->setCommon(_waypoints);
+  _airport->addSID(_sid);
+  _sid = NULL;
+}
+
+void NavdataVisitor::finishStar()
+{
+  _star->setCommon(_waypoints);
+  _airport->addSTAR(_star);
+  _star = NULL;
+}
+
+void NavdataVisitor::data (const char * s, int len)
+{
+  _text += string(s, len);
+}
+
+
+void NavdataVisitor::pi (const char * target, const char * data) {
+  //cout << "Processing instruction " << target << ' ' << data << endl;
+}
+
+void NavdataVisitor::warning (const char * message, int line, int column) {
+  SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
+}
+
+void NavdataVisitor::error (const char * message, int line, int column) {
+  SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
+}
+
+}
diff --git a/src/Navaids/LevelDXML.hxx b/src/Navaids/LevelDXML.hxx
new file mode 100644 (file)
index 0000000..7d61bb8
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef FG_NAV_LEVELDXML_HXX
+#define FG_NAV_LEVELDXML_HXX
+
+class FGAirport;
+class SGPath;
+
+#include <simgear/xml/easyxml.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <Navaids/procedure.hxx>
+
+namespace flightgear
+{
+
+class NavdataVisitor : public XMLVisitor {
+public:
+  NavdataVisitor(FGAirport* aApt, const SGPath& aPath);
+
+protected:
+  virtual void startXML (); 
+  virtual void endXML   ();
+  virtual void startElement (const char * name, const XMLAttributes &atts);
+  virtual void endElement (const char * name);
+  virtual void data (const char * s, int len);
+  virtual void pi (const char * target, const char * data);
+  virtual void warning (const char * message, int line, int column);
+  virtual void error (const char * message, int line, int column);
+
+private:
+  Waypt* buildWaypoint(RouteBase* owner);
+  void processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts);
+  void finishApproach();
+  void finishSid();
+  void finishStar();
+  
+  FGAirport* _airport;
+  SGPath _path;
+  std::string _text; ///< last element text value
+  
+  SID* _sid;
+  STAR* _star;
+  Approach* _approach;
+  Transition* _transition;
+  Procedure* _procedure;
+  
+  WayptVec _waypoints; ///< waypoint list for current approach/sid/star
+  WayptVec _transWaypts; ///< waypoint list for current transition
+  
+  std::string _wayptName;
+  std::string _wayptType;
+  std::string _ident; // id of segment under construction
+  std::string _transIdent;
+  double _longitude, _latitude, _altitude, _speed;
+  RouteRestriction _altRestrict;
+  
+  double _holdRadial; // inbound hold radial, or -1 if radial is 'inbound'
+  double _holdTD; ///< hold time (seconds) or distance (nm), based on flag below
+  bool _holdRighthanded;
+  bool _holdDistance; // true, TD is distance in nm; false, TD is time in seconds
+  
+  double _course, _radial, _dmeDistance;
+};
+
+}
+
+#endif
\ No newline at end of file
index f29ecbb20557da1ed515c49f35dad5919ff37891..a2ea9d93f4b3c5fd1d0207ea33c37f20a1cdb40a 100644 (file)
@@ -35,7 +35,7 @@ struct SearchContext;
 class AdjacentWaypoint;
 class InAirwayFilter;
 
-class Airway : public Route
+class Airway : public RouteBase
 {
 public:
   virtual std::string ident() const
index 67c78c18e1664a0f040fa3c1b22476c88e7a4c4a..bff774b4d360d12eaeb3890d33a2302b113d7de0 100644 (file)
@@ -30,14 +30,22 @@ using std::string;
 
 namespace flightgear
 {
+  
+static void markWaypoints(WayptVec& wps, WayptFlag f)
+{
+  for (unsigned int i=0; i<wps.size(); ++i) {
+    wps[i]->setFlag(f, true);
+  }
+}
 
 Procedure::Procedure(const string& aIdent) :
   _ident(aIdent)
 {
 }
 
-Approach::Approach(const string& aIdent) : 
-  Procedure(aIdent)
+Approach::Approach(const string& aIdent, ProcedureType ty) : 
+  Procedure(aIdent),
+  _type(ty)
 {
 
 }
@@ -47,22 +55,32 @@ void Approach::setRunway(FGRunwayRef aRwy)
   _runway = aRwy;
 }
 
+FGAirport* Approach::airport() const
+{
+  return _runway->airport();
+}
+
+RunwayVec Approach::runways() const
+{
+  RunwayVec r;
+  r.push_back(_runway);
+  return r;
+}
+  
 void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed)
 {
   _primary = aPrimary;
   _primary[0]->setFlag(WPT_IAF, true);
   _primary[_primary.size()-1]->setFlag(WPT_FAF, true);
+  markWaypoints(_primary, WPT_APPROACH);
   
   _missed = aMissed;
   
   if (!_missed.empty()) {
     // mark the first point as the published missed-approach point
     _missed[0]->setFlag(WPT_MAP, true);
-  
-    // mark all the points as being on the missed approach route
-    for (unsigned int i=0; i<_missed.size(); ++i) {
-      _missed[i]->setFlag(WPT_MISS, true);
-    }
+    markWaypoints(_missed, WPT_MISS);
+    markWaypoints(_missed, WPT_APPROACH);
   }
 }
 
@@ -70,6 +88,7 @@ void Approach::addTransition(Transition* aTrans)
 {
   WayptRef entry = aTrans->enroute();
   _transitions[entry] = aTrans;
+  aTrans->mark(WPT_APPROACH);
 }
 
 bool Approach::route(WayptRef aIAF, WayptVec& aWps)
@@ -78,8 +97,9 @@ bool Approach::route(WayptRef aIAF, WayptVec& aWps)
   bool haveTrans = false;
   for (it = _transitions.begin(); it != _transitions.end(); ++it) {
     Transition* t= it->second;
-    if (t->route(aIAF, aWps)) {
-    haveTrans = true;
+    if (t->enroute()->matches(aIAF)) {
+      t->route(aWps);
+      haveTrans = true;
       break;
     }
   } // of transitions iteration
@@ -90,10 +110,7 @@ bool Approach::route(WayptRef aIAF, WayptVec& aWps)
     return false;
   }
   
-  aWps.insert(aWps.end(), _primary.begin(), _primary.end());
-  aWps.push_back(new RunwayWaypt(_runway, NULL));
-  aWps.insert(aWps.end(), _missed.begin(), _missed.end());
-  return true;
+  return routeFromVectors(aWps);
 }
 
 bool Approach::routeFromVectors(WayptVec& aWps)
@@ -104,34 +121,64 @@ bool Approach::routeFromVectors(WayptVec& aWps)
   return true;
 }
 
+bool Approach::isApproach(ProcedureType ty)
+{
+  return (ty >= PROCEDURE_APPROACH_ILS) && (ty <= PROCEDURE_APPROACH_RNAV);
+}
+  
 //////////////////////////////////////////////////////////////////////////////
 
-ArrivalDeparture::ArrivalDeparture(const string& aIdent) :
-    Procedure(aIdent)
+ArrivalDeparture::ArrivalDeparture(const string& aIdent, FGAirport* apt) :
+  Procedure(aIdent),
+  _airport(apt)
 {
 }
 
 void ArrivalDeparture::addRunway(FGRunwayRef aWay)
 {
+  assert(aWay->airport() == _airport);
   _runways[aWay] = NULL;
 }
 
-bool ArrivalDeparture::isForRunway(FGRunwayRef aWay) const
+bool ArrivalDeparture::isForRunway(const FGRunway* aWay) const
 {
   // null runway always passes
   if (!aWay) {
     return true;
   }
   
-  return (_runways.count(aWay));
+  FGRunwayRef r(const_cast<FGRunway*>(aWay));
+  return (_runways.count(r));
 }
 
+RunwayVec ArrivalDeparture::runways() const
+{
+  RunwayVec r;
+  RunwayTransitionMap::const_iterator it = _runways.begin();
+  for (; it != _runways.end(); ++it) {
+    r.push_back(it->first);
+  }
+  
+  return r;
+}
+    
 void ArrivalDeparture::addTransition(Transition* aTrans)
 {
   WayptRef entry = aTrans->enroute();
+  aTrans->mark(flagType());
   _enrouteTransitions[entry] = aTrans;
 }
 
+string_list ArrivalDeparture::transitionIdents() const
+{
+  string_list r;
+  WptTransitionMap::const_iterator eit;
+  for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
+    r.push_back(eit->second->ident());
+  }
+  return r;
+}
+  
 void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
 {
   assert(aWay->ident() == aTrans->ident());
@@ -139,23 +186,24 @@ void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
     throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
   }
   
+  aTrans->mark(flagType());
   _runways[aWay] = aTrans;
 }
 
 void ArrivalDeparture::setCommon(const WayptVec& aWps)
 {
   _common = aWps;
+  markWaypoints(_common, flagType());
 }
 
-bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef aRwy)
+bool ArrivalDeparture::commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy)
 {
   // assume we're routing from enroute, to the runway.
   // for departures, we'll flip the result points
 
-  Transition* t = findTransitionByEnroute(aEnroute);
   WayptVec::iterator firstCommon = _common.begin();
   if (t) {
-    t->route(aEnroute, aPath);
+    t->route(aPath);
 
     Waypt* transEnd = t->procedureEnd();
     for (; firstCommon != _common.end(); ++firstCommon) {
@@ -170,9 +218,7 @@ bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef
     // common section after the transition.
     firstCommon = _common.begin();
   } else {
-    if (aEnroute && !(*firstCommon)->matches(aEnroute)) {
-      return false;
-    }
+    // no tranasition
   } // of not using a transition
   
   // append (some) common points
@@ -193,7 +239,7 @@ bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef
   }
   
   SG_LOG(SG_GENERAL, SG_INFO, ident() << " using runway transition for " << r->first->ident());
-  r->second->route(NULL, aPath);
+  r->second->route(aPath);
   return true;
 }
 
@@ -241,13 +287,12 @@ WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
   return w;
 }
 
-WayptRef ArrivalDeparture::findTransitionByName(const string& aIdent) const
+Transition* ArrivalDeparture::findTransitionByName(const string& aIdent) const
 {
   WptTransitionMap::const_iterator eit;
   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
-    WayptRef c = eit->second->enroute();
-    if (c->ident() == aIdent) {
-      return c;
+    if (eit->second->ident() == aIdent) {
+      return eit->second;
     }
   }
   
@@ -256,12 +301,12 @@ WayptRef ArrivalDeparture::findTransitionByName(const string& aIdent) const
 
 ////////////////////////////////////////////////////////////////////////////
 
-SID::SID(const string& aIdent) :
-    ArrivalDeparture(aIdent)
+SID::SID(const string& aIdent, FGAirport* apt) :
+    ArrivalDeparture(aIdent, apt)
 {
 }
 
-bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
+bool SID::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
 {
   if (!isForRunway(aWay)) {
     SG_LOG(SG_GENERAL, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
@@ -269,7 +314,7 @@ bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
   }
   
   WayptVec path;
-  if (!commonRoute(aEnroute, path, aWay)) {
+  if (!commonRoute(trans, path, aWay)) {
     return false;
   }
   
@@ -283,24 +328,25 @@ bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
 
 ////////////////////////////////////////////////////////////////////////////
 
-STAR::STAR(const string& aIdent) :
-    ArrivalDeparture(aIdent)
+STAR::STAR(const string& aIdent, FGAirport* apt) :
+    ArrivalDeparture(aIdent, apt)
 {
 }
 
-bool STAR::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
+bool STAR::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
 {
   if (aWay && !isForRunway(aWay)) {
     return false;
   }
     
-  return commonRoute(aEnroute, aPath, aWay);
+  return commonRoute(trans, aPath, aWay);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 
-Transition::Transition(const std::string& aIdent, Procedure* aPr) :
-  _ident(aIdent),
+Transition::Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr) :
+  Procedure(aIdent),
+  _type(ty),
   _parent(aPr)
 {
   assert(aPr);
@@ -325,14 +371,20 @@ WayptRef Transition::procedureEnd() const
   return _primary[_primary.size() - 1];
 }
 
-bool Transition::route(Waypt* aEnroute, WayptVec& aPath)
+bool Transition::route(WayptVec& aPath)
 {
-  if (aEnroute && !enroute()->matches(aEnroute)) {
-    return false;
-  }
-  
   aPath.insert(aPath.end(), _primary.begin(), _primary.end());
   return true;
 }
 
+FGAirport* Transition::airport() const
+{
+  return _parent->airport();
+}
+  
+void Transition::mark(WayptFlag f)
+{
+  markWaypoints(_primary, f);
+}
+  
 } // of namespace
index f6d63e2867f22471df598c9db49bac6f588e0bdd..11bbf4908632236b80c08798a28493c1d29f45fb 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <set>
 
+#include <simgear/math/sg_types.hxx> // for string_list
+
 #include <Navaids/route.hxx>
 #include <Airports/runways.hxx>
 
@@ -32,12 +34,32 @@ namespace flightgear {
 // forward decls
 class NavdataVisitor;
 
-class Procedure : public Route
+typedef std::vector<FGRunwayRef> RunwayVec;
+  
+typedef enum {
+  PROCEDURE_INVALID,
+  PROCEDURE_APPROACH_ILS,
+  PROCEDURE_APPROACH_VOR,
+  PROCEDURE_APPROACH_NDB,
+  PROCEDURE_APPROACH_RNAV,
+  PROCEDURE_SID,
+  PROCEDURE_STAR,
+  PROCEDURE_TRANSITION,
+  PROCEDURE_RUNWAY_TRANSITION
+} ProcedureType;
+  
+class Procedure : public RouteBase
 {
-public:
-
+public:  
+  virtual ProcedureType type() const = 0;
+  
   virtual std::string ident() const
   { return _ident; }
+  
+  virtual FGAirport* airport() const = 0;
+  
+  virtual RunwayVec runways() const
+  { return RunwayVec(); }
 protected:
   Procedure(const std::string& aIdent);
   
@@ -47,14 +69,16 @@ protected:
 /**
  * Encapsulate a transition segment
  */
-class Transition : public Route
+class Transition : public Procedure
 {
 public:
-  bool route(Waypt* aEnroute, WayptVec& aPath);
+  bool route(WayptVec& aPath);
   
   Procedure* parent() const
   { return _parent; }
   
+  virtual FGAirport* airport() const;
+  
   /**
    * Return the enroute end of the transition
    */
@@ -65,16 +89,19 @@ public:
    */
   WayptRef procedureEnd() const;
   
-  virtual std::string ident() const
-  { return _ident; }
+  
+  virtual ProcedureType type() const
+  { return _type; }
+  
+  void mark(WayptFlag f);
 private:
   friend class NavdataVisitor;
 
-  Transition(const std::string& aIdent, Procedure* aPr);
+  Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr);
 
   void setPrimary(const WayptVec& aWps);
   
-  std::string _ident;
+  ProcedureType _type;
   Procedure* _parent;
   WayptVec _primary;
 };
@@ -89,6 +116,12 @@ public:
   FGRunwayRef runway() 
   { return _runway; }
 
+  static bool isApproach(ProcedureType ty);
+  
+  virtual FGAirport* airport() const;
+  
+  virtual RunwayVec runways() const;
+  
   /**
    * Build a route from a valid IAF to the runway, including the missed
    * segment. Return false if no valid transition from the specified IAF
@@ -108,16 +141,19 @@ public:
   const WayptVec& missed() const
     { return _missed; }
 
+  virtual ProcedureType type() const
+  { return _type; }
 private:
   friend class NavdataVisitor;
   
-  Approach(const std::string& aIdent);
+  Approach(const std::string& aIdent, ProcedureType ty);
   
   void setRunway(FGRunwayRef aRwy);
   void setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed);
   void addTransition(Transition* aTrans);
   
   FGRunwayRef _runway;
+  ProcedureType _type;
   
   typedef std::map<WayptRef, Transition*> WptTransitionMap;
   WptTransitionMap _transitions;
@@ -129,20 +165,27 @@ private:
 class ArrivalDeparture : public Procedure
 {
 public:
+  virtual FGAirport* airport() const
+  { return _airport; }
+  
   /**
    * Predicate, test if this procedure applies to the requested runway
    */
-  virtual bool isForRunway(FGRunwayRef aWay) const;
+  virtual bool isForRunway(const FGRunway* aWay) const;
+
+  virtual RunwayVec runways() const;
 
   /**
    * Find a path between the runway and enroute structure. Waypoints 
    * corresponding to the appropriate transitions and segments will be created.
    */
-  virtual bool route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath) = 0;
+  virtual bool route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath) = 0;
 
   const WayptVec& common() const
     { return _common; }
 
+  string_list transitionIdents() const;
+  
   /**
    * Given an enroute location, find the best enroute transition point for 
    * this arrival/departure. Best is currently determined as 'closest to the
@@ -155,14 +198,14 @@ public:
    * for the route-manager and similar code that that needs to talk about
    * transitions in a human-meaningful way (including persistence).
    */
-  WayptRef findTransitionByName(const std::string& aIdent) const;
+  Transition* findTransitionByName(const std::string& aIdent) const;
   
   Transition* findTransitionByEnroute(Waypt* aEnroute) const;
 protected:
     
-  bool commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef aRwy);
+  bool commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy);
   
-  ArrivalDeparture(const std::string& aIdent);
+  ArrivalDeparture(const std::string& aIdent, FGAirport* apt);
   
   
   void addRunway(FGRunwayRef aRwy);
@@ -170,6 +213,7 @@ protected:
   typedef std::map<FGRunwayRef, Transition*> RunwayTransitionMap;
   RunwayTransitionMap _runways;
   
+  virtual WayptFlag flagType() const = 0;
 private:
   friend class NavdataVisitor;
   
@@ -179,6 +223,7 @@ private:
 
   void addRunwayTransition(FGRunwayRef aRwy, Transition* aTrans);
   
+  FGAirport* _airport;
   WayptVec _common;
   
   typedef std::map<WayptRef, Transition*> WptTransitionMap;
@@ -190,23 +235,37 @@ private:
 class SID : public ArrivalDeparture
 {
 public:  
-  virtual bool route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath);
+  virtual bool route(FGRunwayRef aWay, Transition* aTrans, WayptVec& aPath);
+  
+  virtual ProcedureType type() const
+  { return PROCEDURE_SID; }
+  
+protected:
+  virtual WayptFlag flagType() const
+  { return WPT_DEPARTURE; }
   
 private:
   friend class NavdataVisitor;
     
-  SID(const std::string& aIdent);
+  SID(const std::string& aIdent, FGAirport* apt);
 };
 
 class STAR : public ArrivalDeparture
 {
 public:  
-  virtual bool route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath);
+  virtual bool route(FGRunwayRef aWay, Transition* aTrans, WayptVec& aPath);
+  
+  virtual ProcedureType type() const
+  { return PROCEDURE_STAR; }
+  
+protected:
+  virtual WayptFlag flagType() const
+  { return WPT_ARRIVAL; }
   
 private:
   friend class NavdataVisitor;
   
-  STAR(const std::string& aIdent);
+  STAR(const std::string& aIdent, FGAirport* apt);
 };
 
 } // of namespace
index 4afed518407f35bfe2068871aed173cc7adf8895..88d950821208501c885e3b118bb83e9e1ad01d66 100644 (file)
 // Boost
 #include <boost/algorithm/string/case_conv.hpp>
 #include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
 
 // SimGear
 #include <simgear/structure/exception.hxx>
-#include <simgear/xml/easyxml.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/magvar/magvar.hxx>
 #include <simgear/timing/sg_time.hxx>
+#include <simgear/misc/sgstream.hxx>
+#include <simgear/misc/strutils.hxx>
+#include <simgear/props/props_io.hxx>
 
 // FlightGear
 #include <Main/globals.hxx>
+#include "Main/fg_props.hxx"
 #include <Navaids/procedure.hxx>
 #include <Navaids/waypoint.hxx>
+#include <Navaids/LevelDXML.hxx>
 #include <Airports/simple.hxx>
 
 using std::string;
@@ -54,7 +59,12 @@ namespace flightgear {
 
 const double NO_MAG_VAR = -1000.0; // an impossible mag-var value
 
-Waypt::Waypt(Route* aOwner) :
+bool isMachRestrict(RouteRestriction rr)
+{
+  return (rr == SPEED_RESTRICT_MACH) || (rr == SPEED_COMPUTED_MACH);
+}
+  
+Waypt::Waypt(RouteBase* aOwner) :
   _altitudeFt(0.0),
   _speed(0.0),
   _altRestrict(RESTRICT_NONE),
@@ -189,7 +199,7 @@ static const char* restrictionToString(RouteRestriction aRestrict)
   }
 }
 
-Waypt* Waypt::createInstance(Route* aOwner, const std::string& aTypeName)
+Waypt* Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName)
 {
   Waypt* r = NULL;
   if (aTypeName == "basic") {
@@ -220,7 +230,7 @@ Waypt* Waypt::createInstance(Route* aOwner, const std::string& aTypeName)
   return r;
 }
 
-WayptRef Waypt::createFromProperties(Route* aOwner, SGPropertyNode_ptr aProp)
+WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp)
 {
   if (!aProp->hasChild("type")) {
     throw sg_io_exception("bad props node, no type provided", 
@@ -252,6 +262,10 @@ void Waypt::initFromProperties(SGPropertyNode_ptr aProp)
     setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival")); 
   }
   
+  if (aProp->hasChild("approach")) {
+    setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
+  }
+  
   if (aProp->hasChild("departure")) {
     setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure")); 
   }
@@ -287,6 +301,10 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
     aProp->setBoolValue("arrival", true);
   }
   
+  if (flag(WPT_APPROACH)) {
+    aProp->setBoolValue("approach", true);
+  }
+  
   if (flag(WPT_MISS)) {
     aProp->setBoolValue("miss", true);
   }
@@ -306,7 +324,7 @@ void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
   }
 }
 
-void Route::dumpRouteToFile(const WayptVec& aRoute, const std::string& aName)
+void RouteBase::dumpRouteToKML(const WayptVec& aRoute, const std::string& aName)
 {
   SGPath p = "/Users/jmt/Desktop/" + aName + ".kml";
   std::fstream f;
@@ -321,7 +339,7 @@ void Route::dumpRouteToFile(const WayptVec& aRoute, const std::string& aName)
       "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
     "<Document>\n";
 
-  dumpRouteToLineString(aName, aRoute, f);
+  dumpRouteToKMLLineString(aName, aRoute, f);
   
 // post-amble
   f << "</Document>\n" 
@@ -329,7 +347,7 @@ void Route::dumpRouteToFile(const WayptVec& aRoute, const std::string& aName)
   f.close();
 }
 
-void Route::dumpRouteToLineString(const std::string& aIdent,
+void RouteBase::dumpRouteToKMLLineString(const std::string& aIdent,
   const WayptVec& aRoute, std::ostream& aStream)
 {
   // preamble
@@ -351,59 +369,7 @@ void Route::dumpRouteToLineString(const std::string& aIdent,
     "</Placemark>\n" << endl;
 }
 
-///////////////////////////////////////////////////////////////////////////
-
-class NavdataVisitor : public XMLVisitor {
-public:
-  NavdataVisitor(FGAirport* aApt, const SGPath& aPath);
-
-protected:
-  virtual void startXML (); 
-  virtual void endXML   ();
-  virtual void startElement (const char * name, const XMLAttributes &atts);
-  virtual void endElement (const char * name);
-  virtual void data (const char * s, int len);
-  virtual void pi (const char * target, const char * data);
-  virtual void warning (const char * message, int line, int column);
-  virtual void error (const char * message, int line, int column);
-
-private:
-  Waypt* buildWaypoint(Route* owner);
-  void processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts);
-  void finishApproach();
-  void finishSid();
-  void finishStar();
-  
-  FGAirport* _airport;
-  SGPath _path;
-  string _text; ///< last element text value
-  
-  SID* _sid;
-  STAR* _star;
-  Approach* _approach;
-  Transition* _transition;
-  Procedure* _procedure;
-  
-  WayptVec _waypoints; ///< waypoint list for current approach/sid/star
-  WayptVec _transWaypts; ///< waypoint list for current transition
-  
-  string _wayptName;
-  string _wayptType;
-  string _ident; // id of segment under construction
-  string _transIdent;
-  double _longitude, _latitude, _altitude, _speed;
-  RouteRestriction _altRestrict;
-  
-  double _holdRadial; // inbound hold radial, or -1 if radial is 'inbound'
-  double _holdTD; ///< hold time (seconds) or distance (nm), based on flag below
-  bool _holdRighthanded;
-  bool _holdDistance; // true, TD is distance in nm; false, TD is time in seconds
-  
-  double _course, _radial, _dmeDistance;
-};
-
-void Route::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt)
+void RouteBase::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt)
 {
   assert(aApt);
   try {
@@ -418,319 +384,1002 @@ void Route::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt)
   }
 }
 
-NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath):
-  _airport(aApt),
-  _path(aPath),
+////////////////////////////////////////////////////////////////////////////
+
+FlightPlan::FlightPlan() :
+  _currentIndex(-1),
+  _departureRunway(NULL),
+  _destinationRunway(NULL),
   _sid(NULL),
   _star(NULL),
   _approach(NULL),
-  _transition(NULL),
-  _procedure(NULL)
+  _delegate(NULL)
 {
+  
 }
-
-void NavdataVisitor::startXML()
+  
+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 NavdataVisitor::endXML()
+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 NavdataVisitor::startElement(const char* name, const XMLAttributes &atts)
+void FlightPlan::deleteIndex(int aIndex)
 {
-  _text.clear();
-  string tag(name);
-  if (tag == "Airport") {
-    string icao(atts.getValue("ICAOcode"));
-    if (_airport->ident() != icao) {
-      throw sg_format_exception("Airport and ICAO mismatch", icao, _path.str());
+  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;
     }
-  } else if (tag == "Sid") {
-    string ident(atts.getValue("Name"));
-    _sid = new SID(ident);
-    _procedure = _sid;
-    _waypoints.clear();
-    processRunways(_sid, atts);
-  } else if (tag == "Star") {
-    string ident(atts.getValue("Name"));
-    _star = new STAR(ident);
-    _procedure = _star;
-    _waypoints.clear();
-    processRunways(_star, atts);
-  } else if ((tag == "Sid_Waypoint") ||
-      (tag == "App_Waypoint") ||
-      (tag == "Star_Waypoint") ||
-      (tag == "AppTr_Waypoint") ||
-      (tag == "SidTr_Waypoint") ||
-      (tag == "RwyTr_Waypoint"))
-  {
-    // reset waypoint data
-    _speed = 0.0;
-    _altRestrict = RESTRICT_NONE;
-    _altitude = 0.0;
-  } else if (tag == "Approach") {
-    _ident = atts.getValue("Name");
-    _waypoints.clear();
-    _approach = new Approach(_ident);
-    _procedure = _approach;
-  } else if ((tag == "Sid_Transition") || 
-             (tag == "App_Transition") ||
-             (tag == "Star_Transition")) {
-    _transIdent = atts.getValue("Name");
-    _transition = new Transition(_transIdent, _procedure);
-    _transWaypts.clear();
-  } else if (tag == "RunwayTransition") {
-    _transIdent = atts.getValue("Runway");
-    _transition = new Transition(_transIdent, _procedure);
-    _transWaypts.clear();
-  } else {
     
+  // 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 < 0) || (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; i<numLegs(); ++i) {
+    if (_legs[i]->waypoint()->matches(aPos)) {
+      return i;
+    }
+  }
+  
+  return -1;
+}
+
+FlightPlan::Leg* FlightPlan::currentLeg() const
+{
+  if ((_currentIndex < 0) || (_currentIndex >= numLegs()))
+    return NULL;
+  return legAtIndex(_currentIndex);
 }
 
-void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts)
+FlightPlan::Leg* FlightPlan::previousLeg() const
 {
-  string v("All");
-  if (atts.hasAttribute("Runways")) {
-    v = atts.getValue("Runways");
+  if (_currentIndex == 0) {
+    return NULL;
   }
   
-  if (v == "All") {
-    for (unsigned int r=0; r<_airport->numRunways(); ++r) {
-      aProc->addRunway(_airport->getRunwayByIndex(r));
+  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;
   }
   
-  vector<string> rwys;
-  boost::split(rwys, v, boost::is_any_of(" ,"));
-  for (unsigned int r=0; r<rwys.size(); ++r) {
-    FGRunway* rwy = _airport->getRunwayByIdent(rwys[r]);
-    aProc->addRunway(rwy);
+  _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();
+    }
+  }
 
-void NavdataVisitor::endElement(const char* name)
+  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)
 {
-  string tag(name);
-  if ((tag == "Sid_Waypoint") ||
-      (tag == "App_Waypoint") ||
-      (tag == "Star_Waypoint"))
+  if (!path.exists())
   {
-    _waypoints.push_back(buildWaypoint(_procedure));
-  } else if ((tag == "AppTr_Waypoint") || 
-             (tag == "SidTr_Waypoint") ||
-             (tag == "RwyTr_Waypoint") ||
-             (tag == "StarTr_Waypoint")) 
+    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())
   {
-    _transWaypts.push_back(buildWaypoint(_transition));
-  } else if (tag == "Sid_Transition") {
-    assert(_sid);
-    // SID waypoints are stored backwards, to share code with STARs
-    std::reverse(_transWaypts.begin(), _transWaypts.end());
-    _transition->setPrimary(_transWaypts);
-    _sid->addTransition(_transition);
-  } else if (tag == "Star_Transition") {
-    assert(_star);
-    _transition->setPrimary(_transWaypts);
-    _star->addTransition(_transition);
-  } else if (tag == "App_Transition") {
-    assert(_approach);
-    _transition->setPrimary(_transWaypts);
-    _approach->addTransition(_transition);
-  } else if (tag == "RunwayTransition") {
-    ArrivalDeparture* ad;
-    if (_sid) {
-      // SID waypoints are stored backwards, to share code with STARs
-      std::reverse(_transWaypts.begin(), _transWaypts.end());
-      ad = _sid;
-    } else {
-      ad = _star;
+    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")));
+      }
     
-    _transition->setPrimary(_transWaypts);
-    FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent);
-    ad->addRunwayTransition(rwy, _transition);
-  } else if (tag == "Approach") {
-    finishApproach();
-  } else if (tag == "Sid") {
-    finishSid();
-  } else if (tag == "Star") {
-    finishStar();  
-  } else if (tag == "Longitude") {
-    _longitude = atof(_text.c_str());
-  } else if (tag == "Latitude") {
-    _latitude = atof(_text.c_str());
-  } else if (tag == "Name") {
-    _wayptName = _text;
-  } else if (tag == "Type") {
-    _wayptType = _text;
-  } else if (tag == "Speed") {
-    _speed = atoi(_text.c_str());
-  } else if (tag == "Altitude") {
-    _altitude = atof(_text.c_str());
-  } else if (tag == "AltitudeRestriction") {
-    if (_text == "at") {
-      _altRestrict = RESTRICT_AT;
-    } else if (_text == "above") {
-      _altRestrict = RESTRICT_ABOVE;
-    } else if (_text == "below") {
-      _altRestrict = RESTRICT_BELOW;
-    } else {
-      throw sg_format_exception("Unrecognized altitude restriction", _text);
+      if (dep->hasChild("sid")) {
+        setSID(_departure->findSIDWithIdent(dep->getStringValue("sid")));
+      }
+   // departure->setStringValue("transition", dep->getStringValue("transition"));
     }
-  } else if (tag == "Hld_Rad_or_Inbd") {
-    if (_text == "Inbd") {
-      _holdRadial = -1.0;
+  }
+  
+  // 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")));
+      }
     }
-  } else if (tag == "Hld_Time_or_Dist") {
-    _holdDistance = (_text == "Dist");
-  } else if (tag == "Hld_Rad_value") {
-    _holdRadial = atof(_text.c_str());
-  } else if (tag == "Hld_Turn") {
-    _holdRighthanded = (_text == "Right");
-  } else if (tag == "Hld_td_value") {
-    _holdTD = atof(_text.c_str());
-  } else if (tag == "Hdg_Crs_value") {
-    _course = atof(_text.c_str());
-  } else if (tag == "DMEtoIntercept") {
-    _dmeDistance = atof(_text.c_str());
-  } else if (tag == "RadialtoIntercept") {
-    _radial = atof(_text.c_str());
-  } else {
     
+   // 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
+  
 }
 
-Waypt* NavdataVisitor::buildWaypoint(Route* owner)
-{
-  Waypt* wp = NULL;
-  if (_wayptType == "Normal") {
-    // new LatLonWaypoint
-    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
-    wp = new BasicWaypt(pos, _wayptName, owner);
-  } else if (_wayptType == "Runway") {
-    string ident = _wayptName.substr(2);
-    FGRunwayRef rwy = _airport->getRunwayByIdent(ident);
-    wp = new RunwayWaypt(rwy, owner);
-  } else if (_wayptType == "Hold") {
-    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
-    Hold* h = new Hold(pos, _wayptName, owner);
-    wp = h;
-    if (_holdRighthanded) {
-      h->setRightHanded();
-    } else {
-      h->setLeftHanded();
-    }
+void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
+{
+  loadXMLRouteHeader(routeData);
+  
+  // route nodes
+  _legs.clear();
+  SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);    
+  for (int i=0; i<routeNode->nChildren(); ++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; i<routeNode->nChildren(); ++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);
     
-    if (_holdDistance) {
-      h->setHoldDistance(_holdTD);
-    } else {
-      h->setHoldTime(_holdTD * 60.0);
+  } 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);
     }
     
-    if (_holdRadial >= 0.0) {
-      h->setHoldRadial(_holdRadial);
+    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);
     }
-  } else if (_wayptType == "Vectors") {
-    wp = new ATCVectors(owner, _airport);
-  } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) {
-    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
-    wp = new RadialIntercept(owner, _wayptName, pos, _course, _radial);
-  } else if (_wayptType == "DmeIntc") {
-    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
-    wp = new DMEIntercept(owner, _wayptName, pos, _course, _dmeDistance);
-  } else if (_wayptType == "ConstHdgtoAlt") {
-    wp = new HeadingToAltitude(owner, _wayptName, _course);
-  } else if (_wayptType == "PBD") {
-    SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2;
-    double az2;
-    SGGeodesy::direct(pos, _course, _dmeDistance, pos2, az2);
-    wp = new BasicWaypt(pos2, _wayptName, owner);
-  } else {
-    SG_LOG(SG_GENERAL, SG_ALERT, "implement waypoint type:" << _wayptType);
-    throw sg_format_exception("Unrecognized waypt type", _wayptType);
+    
+    w = new BasicWaypt(pos, ident, NULL);
   }
   
-  assert(wp);
-  if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) {
-    wp->setAltitude(_altitude,_altRestrict);
+  double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
+  if (altFt > -9990.0) {
+    w->setAltitude(altFt, RESTRICT_AT);
   }
   
-  if (_speed > 0.0) {
-    wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW?
+  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 wp;
-}
+  return true;
+}  
 
-void NavdataVisitor::finishApproach()
+double FlightPlan::magvarDegAt(const SGGeod& pos) const
 {
-  WayptVec::iterator it;
-  FGRunwayRef rwy;
+  double jd = globals->get_time_params()->getJD();
+  return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
+}
   
-// find the runway node
-  for (it = _waypoints.begin(); it != _waypoints.end(); ++it) {
-    FGPositionedRef navid = (*it)->source();
-    if (!navid) {
-      continue;
+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<FGAirport*>(p.ptr());
+    if (!apt) {
+      SG_LOG(SG_AUTOPILOT, SG_INFO, "Waypoint is not an airport:" << pieces.front());
+      return NULL;
     }
     
-    if (navid->type() == FGPositioned::RUNWAY) {
-      rwy = (FGRunway*) navid.get();
-      break;
+    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 (!rwy) {
-    throw sg_format_exception("Malformed approach, no runway waypt", _ident);
+  if (!wpt) {
+    SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target);
+    return NULL;
   }
   
-  WayptVec primary(_waypoints.begin(), it);
-  // erase all points up to and including the runway, to leave only the
-  // missed segments
-  _waypoints.erase(_waypoints.begin(), ++it);
+  if (altSetting != RESTRICT_NONE) {
+    wpt->setAltitude(altFt, altSetting);
+  }
+  return wpt;
+}
   
-  _approach->setRunway(rwy);
-  _approach->setPrimaryAndMissed(primary, _waypoints);
-  _airport->addApproach(_approach);
-  _approach = NULL;
+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;
 }
 
-void NavdataVisitor::finishSid()
+FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const
 {
-  // reverse order, because that's how we deal with commonality between
-  // STARs and SIDs. SID::route undoes  this
-  std::reverse(_waypoints.begin(), _waypoints.end());
-  _sid->setCommon(_waypoints);
-  _airport->addSID(_sid);
-  _sid = NULL;
+  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);
 }
 
-void NavdataVisitor::finishStar()
+unsigned int FlightPlan::Leg::index() const
 {
-  _star->setCommon(_waypoints);
-  _airport->addSTAR(_star);
-  _star = NULL;
+  return _parent->findLegIndex(this);
 }
 
-void NavdataVisitor::data (const char * s, int len)
+int FlightPlan::Leg::altitudeFt() const
 {
-  _text += string(s, len);
+  if (_altRestrict != RESTRICT_NONE) {
+    return _altitudeFt;
+  }
+  
+  return _waypt->altitudeFt();
 }
 
+int FlightPlan::Leg::speed() const
+{
+  if (_speedRestrict != RESTRICT_NONE) {
+    return _speed;
+  }
+  
+  return _waypt->speed();
+}
 
-void NavdataVisitor::pi (const char * target, const char * data) {
-  //cout << "Processing instruction " << target << ' ' << data << endl;
+int FlightPlan::Leg::speedKts() const
+{
+  return speed();
+}
+  
+double FlightPlan::Leg::speedMach() const
+{
+  if (!isMachRestrict(_speedRestrict)) {
+    return 0.0;
+  }
+  
+  return -(_speed / 100.0);
 }
 
-void NavdataVisitor::warning (const char * message, int line, int column) {
-  SG_LOG(SG_IO, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')');
+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;
 }
 
-void NavdataVisitor::error (const char * message, int line, int column) {
-  SG_LOG(SG_IO, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')');
+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<int>(_legs.size()) - 1;
+  for (int l=0; l<lastLeg; ++l) {
+    Leg* cur = _legs[l];
+    Leg* next = _legs[l + 1];
+    
+    std::pair<double, double> 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
index 9c35c7c60309372e1702a39002426a842d7205b4..79ccfd1e749bb295b1b20a2bb1288ebb0c86f5a3 100644 (file)
 // forward decls
 class FGPositioned;
 class SGPath;
-class FGAirport;
+class FGRunway;
+
+#include <Airports/simple.hxx>
+typedef SGSharedPtr<FGAirport> FGAirportRef;
 
 namespace flightgear
 {
 
 // forward decls
-class Route;
+class RouteBase;
 class Waypt;
 class NavdataVisitor;
-
+class SID;
+class STAR;
+class Transition;
+  
 typedef SGSharedPtr<Waypt> WayptRef;
 
 typedef enum {
@@ -66,7 +72,12 @@ typedef enum {
   WPT_GENERATED     = 1 << 7,
   
   WPT_DEPARTURE     = 1 << 8,
-  WPT_ARRIVAL       = 1 << 9
+  WPT_ARRIVAL       = 1 << 9,
+  
+  /// waypoint generated by VNAV / speed management profile,
+  /// for step climbs or top of descent
+  WPT_PSEUDO        = 1 << 10,
+  WPT_APPROACH      = 1 << 11
 } WayptFlag;
 
 typedef enum {
@@ -74,9 +85,14 @@ typedef enum {
        RESTRICT_AT,
        RESTRICT_ABOVE,
        RESTRICT_BELOW,
-  SPEED_RESTRICT_MACH
+  SPEED_RESTRICT_MACH,  ///< encode an 'AT' restriction in Mach, not IAS
+  RESTRICT_DELETE,      ///< ignore underlying restriction (on a leg)
+  RESTRICT_COMPUTED,    ///< data is computed, not a real restriction
+  SPEED_COMPUTED_MACH   ///< variant on above to encode a Mach value
 } RouteRestriction;
 
+bool isMachRestrict(RouteRestriction rr);
+  
 /**
  * Abstract base class for waypoints (and things that are treated similarly
  * by navigation systems)
@@ -86,7 +102,7 @@ class Waypt : public SGReferenced
 public:
   virtual ~Waypt();
   
-       Route* owner() const 
+       RouteBase* owner() const 
                { return _owner; }
   
   /**
@@ -140,7 +156,7 @@ public:
   /**
    * Factory method
    */
-  static WayptRef createFromProperties(Route* aOwner, SGPropertyNode_ptr aProp);
+  static WayptRef createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp);
   
   void saveAsNode(SGPropertyNode* node) const;
   
@@ -173,7 +189,7 @@ public:
 protected:
   friend class NavdataVisitor;
   
-       Waypt(Route* aOwner);
+       Waypt(RouteBase* aOwner);
   
   /**
    * Persistence helper - read node properties from a file
@@ -185,7 +201,7 @@ protected:
    */
   virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
   
-  typedef Waypt* (FactoryFunction)(Route* aOwner) ;
+  typedef Waypt* (FactoryFunction)(RouteBase* aOwner) ;
   static void registerFactory(const std::string aNodeType, FactoryFunction* aFactory);
   
   double _altitudeFt;
@@ -197,16 +213,16 @@ private:
   /**
    * Create an instance of a concrete subclass, or throw an exception
    */
-  static Waypt* createInstance(Route* aOwner, const std::string& aTypeName);
+  static Waypt* createInstance(RouteBase* aOwner, const std::string& aTypeName);
 
-       Route* _owner;
+       RouteBase* _owner;
        unsigned short _flags;
   mutable double _magVarDeg; 
 };
 
 typedef std::vector<WayptRef> WayptVec;
   
-class Route
+class RouteBase
 {
 public:
   /**
@@ -216,14 +232,219 @@ public:
   
   static void loadAirportProcedures(const SGPath& aPath, FGAirport* aApt);
   
-  static void dumpRouteToFile(const WayptVec& aRoute, const std::string& aName);
+  static void dumpRouteToKML(const WayptVec& aRoute, const std::string& aName);
   
-  static void dumpRouteToLineString(const std::string& aIdent,
+  static void dumpRouteToKMLLineString(const std::string& aIdent,
     const WayptVec& aRoute, std::ostream& aStream);
 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<Leg*> LegVec;
+  LegVec _legs;
+  
+  Delegate* _delegate;
+};
+  
 } // of namespace flightgear
 
 #endif // of FG_ROUTE_HXX
index ee67effb52c1c58c23ad1c9bae52bd5814b28d66..85fae3026d40493ef13a5c4c0f13a769365e4c31 100644 (file)
@@ -53,11 +53,24 @@ double pointsKnownDistanceFromGC(const SGGeoc& a, const SGGeoc&b, const SGGeoc&
 
 RoutePath::RoutePath(const flightgear::WayptVec& wpts) :
   _waypts(wpts)
+{
+  commonInit();
+}
+
+RoutePath::RoutePath(const flightgear::FlightPlan* fp)
+{
+  for (int l=0; l<fp->numLegs(); ++l) {
+    _waypts.push_back(fp->legAtIndex(l)->waypoint());
+  }
+  commonInit();
+}
+
+void RoutePath::commonInit()
 {
   _pathClimbFPM = 1200;
   _pathDescentFPM = 800;
   _pathIAS = 190; 
-  _pathTurnRate = 3.0; // 3 deg/sec = 180def/min = standard rate turn
+  _pathTurnRate = 3.0; // 3 deg/sec = 180def/min = standard rate turn  
 }
 
 SGGeodVec RoutePath::pathForIndex(int index) const
index d04c03d08198996db56199bf7e54bdbec44c72fb..cd35642a831a26fd96377aedadfbcb63cd825073 100644 (file)
@@ -37,12 +37,15 @@ class RoutePath
 {
 public:
   RoutePath(const flightgear::WayptVec& wpts);
-
+  RoutePath(const flightgear::FlightPlan* fp);
+  
   SGGeodVec pathForIndex(int index) const;
   
   SGGeod positionForIndex(int index) const;
   
 private:
+  void commonInit();
+  
   class PathCtx;
   
   SGGeodVec pathForHold(flightgear::Hold* hold) const;
index 7327107b0e1074b264db9c37fa8909865630c7f4..dd1ae7a96c4afd14838eb97d2a93b4bcd899fdcc 100644 (file)
@@ -34,7 +34,7 @@ using std::string;
 namespace flightgear
 {
 
-BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, Route* aOwner) :
+BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
   Waypt(aOwner),
   _pos(aPos),
   _ident(aIdent)
@@ -44,14 +44,14 @@ BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, Route* aOwner)
   }
 }
 
-BasicWaypt::BasicWaypt(const SGWayPoint& aWP, Route* aOwner) :
+BasicWaypt::BasicWaypt(const SGWayPoint& aWP, RouteBase* aOwner) :
   Waypt(aOwner),
   _pos(aWP.get_target()),
   _ident(aWP.get_id())
 {
 }
 
-BasicWaypt::BasicWaypt(Route* aOwner) :
+BasicWaypt::BasicWaypt(RouteBase* aOwner) :
   Waypt(aOwner)
 {
 }
@@ -81,7 +81,7 @@ void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
 
 //////////////////////////////////////////////////////////////////////////////
 
-NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, Route* aOwner) :
+NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner) :
   Waypt(aOwner),
   _navaid(aPos)
 {
@@ -90,7 +90,7 @@ NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, Route* aOwner) :
   }
 }
 
-NavaidWaypoint::NavaidWaypoint(Route* aOwner) :
+NavaidWaypoint::NavaidWaypoint(RouteBase* aOwner) :
   Waypt(aOwner)
 {
 }
@@ -142,7 +142,7 @@ void NavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
   aProp->setDoubleValue("lat", _navaid->geod().getLatitudeDeg());
 }
 
-OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, Route* aOwner,
+OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner,
   double aRadial, double aDistNm) :
   NavaidWaypoint(aPos, aOwner),
   _radial(aRadial),
@@ -151,7 +151,7 @@ OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, Route* aOwner,
   init();
 }
 
-OffsetNavaidWaypoint::OffsetNavaidWaypoint(Route* aOwner) :
+OffsetNavaidWaypoint::OffsetNavaidWaypoint(RouteBase* aOwner) :
   NavaidWaypoint(aOwner)
 {
 }
@@ -186,13 +186,13 @@ void OffsetNavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
 
 /////////////////////////////////////////////////////////////////////////////
 
-RunwayWaypt::RunwayWaypt(FGRunway* aPos, Route* aOwner) :
+RunwayWaypt::RunwayWaypt(FGRunway* aPos, RouteBase* aOwner) :
   Waypt(aOwner),
   _runway(aPos)
 {
 }
 
-RunwayWaypt::RunwayWaypt(Route* aOwner) :
+RunwayWaypt::RunwayWaypt(RouteBase* aOwner) :
   Waypt(aOwner)
 {
 }
@@ -239,7 +239,7 @@ void RunwayWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
 
 /////////////////////////////////////////////////////////////////////////////
 
-Hold::Hold(const SGGeod& aPos, const string& aIdent, Route* aOwner) :
+Hold::Hold(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
   BasicWaypt(aPos, aIdent, aOwner),
   _righthanded(true),
   _isDistance(false)
@@ -247,7 +247,7 @@ Hold::Hold(const SGGeod& aPos, const string& aIdent, Route* aOwner) :
   setFlag(WPT_DYNAMIC);
 }
 
-Hold::Hold(Route* aOwner) :
+Hold::Hold(RouteBase* aOwner) :
   BasicWaypt(aOwner),
   _righthanded(true),
   _isDistance(false)
@@ -308,7 +308,7 @@ void Hold::writeToProperties(SGPropertyNode_ptr aProp) const
 
 /////////////////////////////////////////////////////////////////////////////
 
-HeadingToAltitude::HeadingToAltitude(Route* aOwner, const string& aIdent, 
+HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner, const string& aIdent, 
   double aMagHdg) :
   Waypt(aOwner),
   _ident(aIdent),
@@ -317,7 +317,7 @@ HeadingToAltitude::HeadingToAltitude(Route* aOwner, const string& aIdent,
   setFlag(WPT_DYNAMIC);
 }
 
-HeadingToAltitude::HeadingToAltitude(Route* aOwner) :
+HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner) :
   Waypt(aOwner)
 {
 }
@@ -343,7 +343,7 @@ void HeadingToAltitude::writeToProperties(SGPropertyNode_ptr aProp) const
 
 /////////////////////////////////////////////////////////////////////////////
 
-DMEIntercept::DMEIntercept(Route* aOwner, const string& aIdent, const SGGeod& aPos,
+DMEIntercept::DMEIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
     double aCourseDeg, double aDistanceNm) :
   Waypt(aOwner),
   _ident(aIdent),
@@ -354,7 +354,7 @@ DMEIntercept::DMEIntercept(Route* aOwner, const string& aIdent, const SGGeod& aP
   setFlag(WPT_DYNAMIC);
 }
 
-DMEIntercept::DMEIntercept(Route* aOwner) :
+DMEIntercept::DMEIntercept(RouteBase* aOwner) :
   Waypt(aOwner)
 {
 }
@@ -388,7 +388,7 @@ void DMEIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
 
 /////////////////////////////////////////////////////////////////////////////
 
-RadialIntercept::RadialIntercept(Route* aOwner, const string& aIdent, const SGGeod& aPos,
+RadialIntercept::RadialIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
     double aCourseDeg, double aRadial) :
   Waypt(aOwner),
   _ident(aIdent),
@@ -399,7 +399,7 @@ RadialIntercept::RadialIntercept(Route* aOwner, const string& aIdent, const SGGe
   setFlag(WPT_DYNAMIC);
 }
 
-RadialIntercept::RadialIntercept(Route* aOwner) :
+RadialIntercept::RadialIntercept(RouteBase* aOwner) :
   Waypt(aOwner)
 {
 }
@@ -433,7 +433,7 @@ void RadialIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
 
 /////////////////////////////////////////////////////////////////////////////
 
-ATCVectors::ATCVectors(Route* aOwner, FGAirport* aFacility) :
+ATCVectors::ATCVectors(RouteBase* aOwner, FGAirport* aFacility) :
   Waypt(aOwner),
   _facility(aFacility)
 {
@@ -444,7 +444,7 @@ ATCVectors::~ATCVectors()
 {
 }
 
-ATCVectors::ATCVectors(Route* aOwner) :
+ATCVectors::ATCVectors(RouteBase* aOwner) :
   Waypt(aOwner)
 {
 }
index ece8cadf58b9b30cc7febc762d78a6501bda286e..47dd7810502885fe6dbe10f21a9f6fd0453923e5 100644 (file)
@@ -36,11 +36,11 @@ class BasicWaypt : public Waypt
 {
 public:
   
-  BasicWaypt(const SGGeod& aPos, const std::string& aIdent, Route* aOwner);
+  BasicWaypt(const SGGeod& aPos, const std::string& aIdent, RouteBase* aOwner);
   
-  BasicWaypt(const SGWayPoint& aWP, Route* aOwner);
+  BasicWaypt(const SGWayPoint& aWP, RouteBase* aOwner);
   
-  BasicWaypt(Route* aOwner);
+  BasicWaypt(RouteBase* aOwner);
   
   virtual SGGeod position() const
     { return _pos; }
@@ -67,9 +67,9 @@ protected:
 class NavaidWaypoint : public Waypt
 {
 public:
-  NavaidWaypoint(FGPositioned* aPos, Route* aOwner);
+  NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner);
   
-  NavaidWaypoint(Route* aOwner);
+  NavaidWaypoint(RouteBase* aOwner);
   
   virtual SGGeod position() const;
   
@@ -90,9 +90,9 @@ protected:
 class OffsetNavaidWaypoint : public NavaidWaypoint
 {
 public:        
-  OffsetNavaidWaypoint(FGPositioned* aPos, Route* aOwner, double aRadial, double aDistNm);
+  OffsetNavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner, double aRadial, double aDistNm);
 
-  OffsetNavaidWaypoint(Route* aOwner);
+  OffsetNavaidWaypoint(RouteBase* aOwner);
   
   virtual SGGeod position() const
     { return _geod; }
@@ -120,9 +120,9 @@ private:
 class RunwayWaypt : public Waypt
 {
 public:
-  RunwayWaypt(FGRunway* aPos, Route* aOwner);
+  RunwayWaypt(FGRunway* aPos, RouteBase* aOwner);
   
-  RunwayWaypt(Route* aOwner);
+  RunwayWaypt(RouteBase* aOwner);
   
   virtual SGGeod position() const;
   
@@ -148,9 +148,9 @@ private:
 class Hold : public BasicWaypt
 {
 public:
-  Hold(const SGGeod& aPos, const std::string& aIdent, Route* aOwner);
+  Hold(const SGGeod& aPos, const std::string& aIdent, RouteBase* aOwner);
   
-  Hold(Route* aOwner);
+  Hold(RouteBase* aOwner);
   
   void setHoldRadial(double aInboundRadial);
   void setHoldDistance(double aDistanceNm);
@@ -190,9 +190,9 @@ private:
 class HeadingToAltitude : public Waypt
 {
 public:
-  HeadingToAltitude(Route* aOwner, const std::string& aIdent, double aMagHdg);
+  HeadingToAltitude(RouteBase* aOwner, const std::string& aIdent, double aMagHdg);
   
-  HeadingToAltitude(Route* aOwner);
+  HeadingToAltitude(RouteBase* aOwner);
   
   virtual void initFromProperties(SGPropertyNode_ptr aProp);
   virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
@@ -222,10 +222,10 @@ private:
 class DMEIntercept : public Waypt
 {
 public:
-  DMEIntercept(Route* aOwner, const std::string& aIdent, const SGGeod& aPos,
+  DMEIntercept(RouteBase* aOwner, const std::string& aIdent, const SGGeod& aPos,
     double aCourseDeg, double aDistanceNm);
   
-  DMEIntercept(Route* aOwner);
+  DMEIntercept(RouteBase* aOwner);
   
   virtual void initFromProperties(SGPropertyNode_ptr aProp);
   virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
@@ -257,10 +257,10 @@ private:
 class RadialIntercept : public Waypt
 {
 public:
-  RadialIntercept(Route* aOwner, const std::string& aIdent, const SGGeod& aPos,
+  RadialIntercept(RouteBase* aOwner, const std::string& aIdent, const SGGeod& aPos,
     double aCourseDeg, double aRadialDeg);
   
-  RadialIntercept(Route* aOwner);
+  RadialIntercept(RouteBase* aOwner);
   
   virtual void initFromProperties(SGPropertyNode_ptr aProp);
   virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
@@ -295,10 +295,10 @@ private:
 class ATCVectors : public Waypt
 {
 public:
-  ATCVectors(Route* aOwner, FGAirport* aFacility);
+  ATCVectors(RouteBase* aOwner, FGAirport* aFacility);
   virtual ~ATCVectors();
   
-  ATCVectors(Route* aOwner);
+  ATCVectors(RouteBase* aOwner);
   
   virtual void initFromProperties(SGPropertyNode_ptr aProp);
   virtual void writeToProperties(SGPropertyNode_ptr aProp) const;
index 8842c6899d46eda6f581149b6c56ca9dea6d6665..208c34e8fef66fb6075fb562fe8a93d9578f34d2 100644 (file)
@@ -27,6 +27,7 @@
 #include "NasalPositioned.hxx"
 
 #include <boost/foreach.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
 
 #include <simgear/sg_inlines.h>
 #include <simgear/scene/material/mat.hxx>
 #include <Autopilot/route_mgr.hxx>
 #include <Navaids/procedure.hxx>
 
+using namespace flightgear;
+
 static void positionedGhostDestroy(void* g);
 static void wayptGhostDestroy(void* g);
+static void legGhostDestroy(void* g);
+static void routeBaseGhostDestroy(void* g);
+
 naGhostType PositionedGhostType = { positionedGhostDestroy, "positioned" };
 
 static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out);
@@ -62,12 +68,32 @@ static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef
 naGhostType RunwayGhostType = { positionedGhostDestroy, "runway", runwayGhostGetMember, 0 };
 
 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out);
-
 naGhostType WayptGhostType = { wayptGhostDestroy, 
   "waypoint",
   wayptGhostGetMember,
   0};
 
+static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out);
+naGhostType FPLegGhostType = { legGhostDestroy, 
+  "flightplan-leg",
+  legGhostGetMember,
+  0};
+
+static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out);
+static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value);
+
+naGhostType FlightPlanGhostType = { routeBaseGhostDestroy, 
+  "flightplan",
+  flightplanGhostGetMember,
+  flightplanGhostSetMember
+};
+
+static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out);
+naGhostType ProcedureGhostType = { routeBaseGhostDestroy, 
+  "procedure",
+  procedureGhostGetMember,
+  0};
+
 static void hashset(naContext c, naRef hash, const char* key, naRef val)
 {
   naRef s = naNewString(c);
@@ -117,24 +143,58 @@ static void positionedGhostDestroy(void* g)
         delete pos;
 }
 
-static flightgear::Waypt* wayptGhost(naRef r)
+static Waypt* wayptGhost(naRef r)
 {
   if (naGhost_type(r) == &WayptGhostType)
-    return (flightgear::Waypt*) naGhost_ptr(r);
+    return (Waypt*) naGhost_ptr(r);
   return 0;
 }
 
 static void wayptGhostDestroy(void* g)
 {
-  flightgear::Waypt* wpt = (flightgear::Waypt*)g;
-  if (!flightgear::Waypt::put(wpt)) // unref
+  Waypt* wpt = (Waypt*)g;
+  if (!Waypt::put(wpt)) // unref
     delete wpt;
 }
 
+static void legGhostDestroy(void* g)
+{
+  // nothing for now
+}
+
+
+static FlightPlan::Leg* fpLegGhost(naRef r)
+{
+  if (naGhost_type(r) == &FPLegGhostType)
+    return (FlightPlan::Leg*) naGhost_ptr(r);
+  return 0;
+}
+
+static Procedure* procedureGhost(naRef r)
+{
+  if (naGhost_type(r) == &ProcedureGhostType)
+    return (Procedure*) naGhost_ptr(r);
+  return 0;
+}
+
+static FlightPlan* flightplanGhost(naRef r)
+{
+  if (naGhost_type(r) == &FlightPlanGhostType)
+    return (FlightPlan*) naGhost_ptr(r);
+  return 0;
+}
+
+static void routeBaseGhostDestroy(void* g)
+{
+  // nothing for now
+}
+
 static naRef airportPrototype;
-static naRef routePrototype;
+static naRef flightplanPrototype;
 static naRef waypointPrototype;
 static naRef geoCoordClass;
+static naRef fpLegPrototype;
+static naRef procedurePrototype;
 
 naRef ghostForPositioned(naContext c, const FGPositioned* pos)
 {
@@ -176,16 +236,43 @@ naRef ghostForRunway(naContext c, const FGRunway* r)
   return naNewGhost2(c, &RunwayGhostType, (void*) r);
 }
 
-naRef ghostForWaypt(naContext c, const flightgear::Waypt* wpt)
+naRef ghostForWaypt(naContext c, const Waypt* wpt)
 {
   if (!wpt) {
     return naNil();
   }
   
-  flightgear::Waypt::get(wpt); // take a ref
+  Waypt::get(wpt); // take a ref
   return naNewGhost2(c, &WayptGhostType, (void*) wpt);
 }
 
+naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg)
+{
+  if (!leg) {
+    return naNil();
+  }
+  
+  return naNewGhost2(c, &FPLegGhostType, (void*) leg);
+}
+
+naRef ghostForFlightPlan(naContext c, const FlightPlan* fp)
+{
+  if (!fp) {
+    return naNil();
+  }
+  
+  return naNewGhost2(c, &FlightPlanGhostType, (void*) fp);
+}
+
+naRef ghostForProcedure(naContext c, const Procedure* proc)
+{
+  if (!proc) {
+    return naNil();
+  }
+  
+  return naNewGhost2(c, &ProcedureGhostType, (void*) proc);
+}
+
 static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out)
 {
   const char* fieldName = naStr_data(field);
@@ -218,43 +305,299 @@ static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRe
   return "";
 }
 
-static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out)
+static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char* fieldName, naRef* out)
 {
-  const char* fieldName = naStr_data(field);
-  flightgear::Waypt* wpt = (flightgear::Waypt*) g;
-
-  if (!strcmp(fieldName, "parents")) {
-    *out = naNewVector(c);
-    naVec_append(*out, waypointPrototype);
-  } else if (!strcmp(fieldName, "wp_name")) *out =stringToNasal(c, wpt->ident());
+  if (!strcmp(fieldName, "wp_name")) *out = stringToNasal(c, wpt->ident());
   else if (!strcmp(fieldName, "wp_type")) *out = stringToNasal(c, wpt->type());
   else if (!strcmp(fieldName, "wp_lat")) *out = naNum(wpt->position().getLatitudeDeg());
   else if (!strcmp(fieldName, "wp_lon")) *out = naNum(wpt->position().getLongitudeDeg());
   else if (!strcmp(fieldName, "wp_parent_name")) {
-    flightgear::Procedure* proc = dynamic_cast<flightgear::Procedure*>(wpt->owner());
+    Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
     *out = proc ? stringToNasal(c, proc->ident()) : naNil();
+  } else if (!strcmp(fieldName, "wp_parent")) {
+    Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
+    *out = ghostForProcedure(c, proc);
   } else if (!strcmp(fieldName, "fly_type")) {
     if (wpt->type() == "hold") {
       *out = stringToNasal(c, "Hold");
     } else {
-      *out = stringToNasal(c, wpt->flag(flightgear::WPT_OVERFLIGHT) ? "flyOver" : "flyBy");
+      *out = stringToNasal(c, wpt->flag(WPT_OVERFLIGHT) ? "flyOver" : "flyBy");
     }
-  } else if (!strcmp(fieldName, "alt_cstr")) *out = naNum(wpt->altitudeFt());
-  else if (!strcmp(fieldName, "speed_cstr")) {
-    double s = (wpt->speedRestriction() == flightgear::SPEED_RESTRICT_MACH) 
-      ? wpt->speedMach() : wpt->speedKts();
+  } else {
+    return NULL; // member not found
+  }
+
+  return "";
+}
+
+static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out)
+{
+  const char* fieldName = naStr_data(field);
+  Waypt* wpt = (flightgear::Waypt*) g;
+  return waypointCommonGetMember(c, wpt, fieldName, out);
+}
+
+static RouteRestriction routeRestrictionFromString(const char* s)
+{
+  string u(s);
+  boost::to_lower(u);
+  if (u == "computed") return RESTRICT_COMPUTED;
+  if (u == "at") return RESTRICT_AT;
+  if (u == "mach") return SPEED_RESTRICT_MACH;
+  if (u == "computed-mach") return SPEED_COMPUTED_MACH;
+  if (u == "delete") return RESTRICT_DELETE;
+  return RESTRICT_NONE;
+};
+
+naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
+{
+  switch (rr) {
+    case RESTRICT_NONE: return naNil();
+    case RESTRICT_AT: return stringToNasal(c, "at");
+    case RESTRICT_ABOVE: return stringToNasal(c, "above");
+    case RESTRICT_BELOW: return stringToNasal(c, "below");
+    case SPEED_RESTRICT_MACH: return stringToNasal(c, "mach");
+    case RESTRICT_COMPUTED: return stringToNasal(c, "computed");
+    case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach");
+    case RESTRICT_DELETE: return stringToNasal(c, "delete");
+  }
+  
+  return naNil();
+}
+
+static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out)
+{
+  const char* fieldName = naStr_data(field);
+  FlightPlan::Leg* leg = (FlightPlan::Leg*) g;
+  Waypt* wpt = leg->waypoint();
+  
+  if (!strcmp(fieldName, "parents")) {
+    *out = naNewVector(c);
+    naVec_append(*out, fpLegPrototype);
+  } else if (!strcmp(fieldName, "alt_cstr")) {
+    *out = naNum(leg->altitudeFt());
+  } else if (!strcmp(fieldName, "alt_cstr_type")) {
+    *out = routeRestrictionToNasal(c, leg->altitudeRestriction());
+  } else if (!strcmp(fieldName, "speed_cstr")) {
+    double s = isMachRestrict(leg->speedRestriction()) ? leg->speedMach() : leg->speedKts();
     *out = naNum(s);
+  } else if (!strcmp(fieldName, "speed_cstr_type")) {
+    *out = routeRestrictionToNasal(c, leg->speedRestriction());  
   } else if (!strcmp(fieldName, "leg_distance")) {
-    return "please implement me";
+    *out = naNum(leg->distanceNm());
   } else if (!strcmp(fieldName, "leg_bearing")) {
-    return "please implement me";
-  } else {
-    return NULL; // member not found
+    *out = naNum(leg->courseDeg());
+  } else if (!strcmp(fieldName, "distance_along_route")) {
+    *out = naNum(leg->distanceAlongRoute());
+  } else { // check for fields defined on the underlying waypoint
+    return waypointCommonGetMember(c, wpt, fieldName, out);
   }
   
   return ""; // success
 }
 
+static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out)
+{
+  const char* fieldName = naStr_data(field);
+  FlightPlan* fp = (FlightPlan*) g;
+  
+  if (!strcmp(fieldName, "parents")) {
+    *out = naNewVector(c);
+    naVec_append(*out, flightplanPrototype);
+  } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, fp->ident());
+  else if (!strcmp(fieldName, "departure")) *out = ghostForAirport(c, fp->departureAirport());
+  else if (!strcmp(fieldName, "destination")) *out = ghostForAirport(c, fp->destinationAirport());
+  else if (!strcmp(fieldName, "departure_runway")) *out = ghostForRunway(c, fp->departureRunway());
+  else if (!strcmp(fieldName, "destination_runway")) *out = ghostForRunway(c, fp->destinationRunway());
+  else if (!strcmp(fieldName, "sid")) *out = ghostForProcedure(c, fp->sid());
+  else if (!strcmp(fieldName, "sid_trans")) *out = ghostForProcedure(c, fp->sidTransition());
+  else if (!strcmp(fieldName, "star")) *out = ghostForProcedure(c, fp->star());
+  else if (!strcmp(fieldName, "star_trans")) *out = ghostForProcedure(c, fp->starTransition());
+  else if (!strcmp(fieldName, "approach")) *out = ghostForProcedure(c, fp->approach());
+  else if (!strcmp(fieldName, "current")) *out = naNum(fp->currentIndex());
+  else {
+    return 0;
+  }
+  
+  return "";
+}
+
+static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value)
+{
+  const char* fieldName = naStr_data(field);
+  FlightPlan* fp = (FlightPlan*) g;
+  
+  if (!strcmp(fieldName, "id")) {
+    if (!naIsString(value)) naRuntimeError(c, "flightplan.id must be a string");
+    fp->setIdent(naStr_data(value));
+  } else if (!strcmp(fieldName, "current")) {
+    int index = value.num;
+    if ((index < 0) || (index >= fp->numLegs())) {
+      return;
+    }
+    fp->setCurrentIndex(index);
+  } else if (!strcmp(fieldName, "departure")) {
+    FGAirport* apt = airportGhost(value);
+    if (apt) {
+      fp->setDeparture(apt);
+      return;
+    }
+    
+    FGRunway* rwy = runwayGhost(value);
+    if (rwy){
+      fp->setDeparture(rwy);
+      return;
+    }
+    
+    naRuntimeError(c, "bad argument type setting departure");
+  } else if (!strcmp(fieldName, "destination")) {
+    FGAirport* apt = airportGhost(value);
+    if (apt) {
+      fp->setDestination(apt);
+      return;
+    }
+    
+    FGRunway* rwy = runwayGhost(value);
+    if (rwy){
+      fp->setDestination(rwy);
+      return;
+    }
+    
+    naRuntimeError(c, "bad argument type setting destination");
+  } else if (!strcmp(fieldName, "departure_runway")) {
+    FGRunway* rwy = runwayGhost(value);
+    if (rwy){
+      fp->setDeparture(rwy);
+      return;
+    }
+    
+    naRuntimeError(c, "bad argument type setting departure");
+  } else if (!strcmp(fieldName, "destination_runway")) {
+    FGRunway* rwy = runwayGhost(value);
+    if (rwy){
+      fp->setDestination(rwy);
+      return;
+    }
+    
+    naRuntimeError(c, "bad argument type setting departure");
+  } else if (!strcmp(fieldName, "sid")) {
+    Procedure* proc = procedureGhost(value);
+    if (proc && (proc->type() == PROCEDURE_SID)) {
+      fp->setSID((SID*) proc);
+      return;
+    }
+    // allow a SID transition to be set, implicitly include the SID itself
+    if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
+      fp->setSID((Transition*) proc);
+      return;
+    }
+        
+    if (naIsString(value)) {
+      FGAirport* apt = fp->departureAirport();
+      fp->setSID(apt->findSIDWithIdent(naStr_data(value)));
+      return;
+    }
+    
+    naRuntimeError(c, "bad argument type setting SID");
+  } else if (!strcmp(fieldName, "star")) {
+    Procedure* proc = procedureGhost(value);
+    if (proc && (proc->type() == PROCEDURE_STAR)) {
+      fp->setSTAR((STAR*) proc);
+      return;
+    }
+    
+    if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
+      fp->setSTAR((Transition*) proc);
+      return;
+    }
+    
+    if (naIsString(value)) {
+      FGAirport* apt = fp->destinationAirport();
+      fp->setSTAR(apt->findSTARWithIdent(naStr_data(value)));
+      return;
+    }
+    
+    naRuntimeError(c, "bad argument type setting STAR");
+  } else if (!strcmp(fieldName, "approach")) {
+    Procedure* proc = procedureGhost(value);
+    if (proc && Approach::isApproach(proc->type())) {
+      fp->setApproach((Approach*) proc);
+      return;
+    }
+    
+    if (naIsString(value)) {
+      FGAirport* apt = fp->destinationAirport();
+      fp->setApproach(apt->findApproachWithIdent(naStr_data(value)));
+      return;
+    }
+    
+    naRuntimeError(c, "bad argument type setting approach");
+  }
+}
+
+
+static naRef procedureTpType(naContext c, ProcedureType ty)
+{
+  switch (ty) {
+    case PROCEDURE_SID: return stringToNasal(c, "sid");
+    case PROCEDURE_STAR: return stringToNasal(c, "star");
+    case PROCEDURE_APPROACH_VOR: 
+    case PROCEDURE_APPROACH_ILS: 
+    case PROCEDURE_APPROACH_RNAV: 
+    case PROCEDURE_APPROACH_NDB:
+      return stringToNasal(c, "IAP");
+    default:
+      return naNil();
+  }
+}
+
+static naRef procedureRadioType(naContext c, ProcedureType ty)
+{
+  switch (ty) {
+    case PROCEDURE_APPROACH_VOR: return stringToNasal(c, "VOR");
+    case PROCEDURE_APPROACH_ILS: return stringToNasal(c, "ILS");
+    case PROCEDURE_APPROACH_RNAV: return stringToNasal(c, "RNAV");
+    case PROCEDURE_APPROACH_NDB: return stringToNasal(c, "NDB");
+    default:
+      return naNil();
+  }
+}
+
+static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out)
+{
+  const char* fieldName = naStr_data(field);
+  Procedure* proc = (Procedure*) g;
+  
+  if (!strcmp(fieldName, "parents")) {
+    *out = naNewVector(c);
+    naVec_append(*out, procedurePrototype);
+  } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, proc->ident());
+  else if (!strcmp(fieldName, "airport")) *out = ghostForAirport(c, proc->airport());
+  else if (!strcmp(fieldName, "tp_type")) *out = procedureTpType(c, proc->type());
+  else if (!strcmp(fieldName, "radio")) *out = procedureRadioType(c, proc->type());
+  else if (!strcmp(fieldName, "runways")) {
+    *out = naNewVector(c);
+    BOOST_FOREACH(FGRunwayPtr rwy, proc->runways()) {
+      naVec_append(*out, stringToNasal(c, rwy->ident()));
+    }
+  } else if (!strcmp(fieldName, "transitions")) {
+    if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
+      *out = naNil();
+      return "";
+    }
+        
+    ArrivalDeparture* ad = static_cast<ArrivalDeparture*>(proc);
+    *out = naNewVector(c);
+    BOOST_FOREACH(string id, ad->transitionIdents()) {
+      naVec_append(*out, stringToNasal(c, id));
+    }
+  } else {
+    return 0;
+  }
+  
+  return "";
+}
+
 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out)
 {
   const char* fieldName = naStr_data(field);
@@ -664,6 +1007,7 @@ static naRef f_airport_runway(naContext c, naRef me, int argc, naRef* args)
   }
   
   std::string ident(naStr_data(args[0]));
+  boost::to_upper(ident);
   if (!apt->hasRunwayWithIdent(ident)) {
     return naNil();
   }
@@ -680,12 +1024,18 @@ static naRef f_airport_sids(naContext c, naRef me, int argc, naRef* args)
   
   naRef sids = naNewVector(c);
   
+  FGRunway* rwy = NULL;
   if (argc > 0 && naIsString(args[0])) {
     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
       return naNil();
     }
 
-    FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0]));
+    rwy = apt->getRunwayByIdent(naStr_data(args[0]));
+  } else if (argc > 0) {
+    rwy = runwayGhost(args[0]);
+  }
+
+  if (rwy) {
     BOOST_FOREACH(flightgear::SID* sid, rwy->getSIDs()) {
       naRef procId = stringToNasal(c, sid->ident());
       naVec_append(sids, procId);
@@ -710,12 +1060,18 @@ static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args)
   
   naRef stars = naNewVector(c);
   
+  FGRunway* rwy = NULL;
   if (argc > 0 && naIsString(args[0])) {
     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
       return naNil();
     }
         
-    FGRunway* rwy = apt->getRunwayByIdent(naStr_data(args[0]));
+    rwy = apt->getRunwayByIdent(naStr_data(args[0]));
+  } else if (argc > 0) {
+    rwy = runwayGhost(args[0]);
+  }
+  
+  if (rwy) {
     BOOST_FOREACH(flightgear::STAR* s, rwy->getSTARs()) {
       naRef procId = stringToNasal(c, s->ident());
       naVec_append(stars, procId);
@@ -731,6 +1087,61 @@ static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args)
   return stars;
 }
 
+static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.getApproachList called on non-airport object");
+  }
+  
+  naRef approaches = naNewVector(c);
+  
+  ProcedureType ty = PROCEDURE_INVALID;
+  if ((argc > 1) && naIsString(args[1])) {
+    std::string u(naStr_data(args[1]));
+    boost::to_upper(u);
+    if (u == "NDB") ty = PROCEDURE_APPROACH_NDB;
+    if (u == "VOR") ty = PROCEDURE_APPROACH_VOR;
+    if (u == "ILS") ty = PROCEDURE_APPROACH_ILS;
+    if (u == "RNAV") ty = PROCEDURE_APPROACH_RNAV;
+  }
+  
+  FGRunway* rwy = NULL;
+  if (argc > 0 && (rwy = runwayGhost(args[0]))) {
+    // ok
+  } else if (argc > 0 && naIsString(args[0])) {
+    if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
+      return naNil();
+    }
+    
+    rwy = apt->getRunwayByIdent(naStr_data(args[0]));
+  }
+  
+  if (rwy) {
+    BOOST_FOREACH(Approach* s, rwy->getApproaches()) {
+      if ((ty != PROCEDURE_INVALID) && (s->type() != ty)) {
+        continue;
+      }
+      
+      naRef procId = stringToNasal(c, s->ident());
+      naVec_append(approaches, procId);
+    }
+  } else {
+    // no runway specified, report them all
+    for (unsigned int s=0; s<apt->numApproaches(); ++s) {
+      Approach* app = apt->getApproachByIndex(s);
+      if ((ty != PROCEDURE_INVALID) && (app->type() != ty)) {
+        continue;
+      }
+      
+      naRef procId = stringToNasal(c, app->ident());
+      naVec_append(approaches, procId);
+    }
+  }
+  
+  return approaches;
+}
+
 static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
 {
   FGAirport* apt = airportGhost(me);
@@ -769,6 +1180,51 @@ static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
   return r;
 }
 
+static naRef f_airport_getSid(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.getSid called on non-airport object");
+  }
+  
+  if ((argc != 1) || !naIsString(args[0])) {
+    naRuntimeError(c, "airport.getSid passed invalid argument");
+  }
+  
+  string ident = naStr_data(args[0]);
+  return ghostForProcedure(c, apt->findSIDWithIdent(ident));
+}
+
+static naRef f_airport_getStar(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.getStar called on non-airport object");
+  }
+  
+  if ((argc != 1) || !naIsString(args[0])) {
+    naRuntimeError(c, "airport.getStar passed invalid argument");
+  }
+  
+  string ident = naStr_data(args[0]);
+  return ghostForProcedure(c, apt->findSTARWithIdent(ident));
+}
+
+static naRef f_airport_getApproach(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.getIAP called on non-airport object");
+  }
+  
+  if ((argc != 1) || !naIsString(args[0])) {
+    naRuntimeError(c, "airport.getIAP passed invalid argument");
+  }
+  
+  string ident = naStr_data(args[0]);
+  return ghostForProcedure(c, apt->findApproachWithIdent(ident));
+}
+
 // Returns vector of data hash for navaid of a <type>, nil on error
 // navaids sorted by ascending distance 
 // navinfo([<lat>,<lon>],[<type>],[<id>])
@@ -1030,62 +1486,233 @@ static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args)
 
 static naRef f_route(naContext c, naRef me, int argc, naRef* args)
 {
-  naRef route = naNewHash(c);
-  
-  // return active route hash by default,
-  // other routes in the future
+  if (argc == 0) {
+    FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));  
+    return ghostForFlightPlan(c, rm->flightPlan());
+  }
   
-  naRef parents = naNewVector(c);
-  naVec_append(parents, routePrototype);
-  hashset(c, route, "parents", parents);
+  if ((argc > 0) && naIsString(args[0])) {
+    flightgear::FlightPlan* fp = new flightgear::FlightPlan;
+    SGPath path(naStr_data(args[0]));
+    if (!path.exists()) {
+      naRuntimeError(c, "flightplan, no file at path %s", path.c_str());
+    }
+    
+    if (!fp->load(path)) {
+      SG_LOG(SG_NASAL, SG_WARN, "failed to load flight-plan from " << path);
+      delete fp;
+      return naNil();
+    }
+    
+    return ghostForFlightPlan(c, fp);
+  }
   
-  return route;
+  naRuntimeError(c, "bad arguments to flightplan()");
+  return naNil();
 }
 
-static naRef f_route_getWP(naContext c, naRef me, int argc, naRef* args)
+static naRef f_flightplan_getWP(naContext c, naRef me, int argc, naRef* args)
 {
-  FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-  
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.getWP called on non-flightplan object");
+  }
+
   int index;
   if (argc == 0) {
-    index = rm->currentIndex();
+    index = fp->currentIndex();
   } else {
     index = (int) naNumValue(args[0]).num;
   }
   
-  if ((index < 0) || (index >= rm->numWaypts())) {
+  if ((index < 0) || (index >= fp->numLegs())) {
     return naNil();
   }
   
-  return ghostForWaypt(c, rm->wayptAtIndex(index));
+  return ghostForLeg(c, fp->legAtIndex(index));
 }
 
-static naRef f_route_currentWP(naContext c, naRef me, int argc, naRef* args)
+static naRef f_flightplan_currentWP(naContext c, naRef me, int argc, naRef* args)
 {
-  FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-  return ghostForWaypt(c, rm->currentWaypt());
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.currentWP called on non-flightplan object");
+  }
+  return ghostForLeg(c, fp->currentLeg());
 }
 
-static naRef f_route_nextWP(naContext c, naRef me, int argc, naRef* args)
+static naRef f_flightplan_nextWP(naContext c, naRef me, int argc, naRef* args)
 {
-  FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-  flightgear::WayptRef wp = rm->nextWaypt();
-  if (!wp) {
-    return naNil();
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.nextWP called on non-flightplan object");
+  }
+  return ghostForLeg(c, fp->nextLeg());
+}
+
+static naRef f_flightplan_numWaypoints(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.numWaypoints called on non-flightplan object");
+  }
+  return naNum(fp->numLegs());
+}
+
+static naRef f_flightplan_appendWP(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.appendWP called on non-flightplan object");
   }
-  return ghostForWaypt(c, wp);
+  
+  WayptRef wp = wayptGhost(args[0]);
+  int index = fp->numLegs();
+  fp->insertWayptAtIndex(wp.get(), index);
+  return naNum(index);
+}
+
+static naRef f_flightplan_insertWP(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.insertWP called on non-flightplan object");
+  }
+  
+  WayptRef wp = wayptGhost(args[0]);
+  int index = -1; // append
+  if ((argc > 1) && naIsNum(args[1])) {
+    index = (int) args[1].num;
+  }
+  
+  fp->insertWayptAtIndex(wp.get(), index);
+  return naNil();
+}
+
+static naRef f_flightplan_insertWPAfter(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.insertWPAfter called on non-flightplan object");
+  }
+  
+  WayptRef wp = wayptGhost(args[0]);
+  int index = -1; // append
+  if ((argc > 1) && naIsNum(args[1])) {
+    index = (int) args[1].num;
+  }
+  
+  fp->insertWayptAtIndex(wp.get(), index + 1);
+  return naNil();
+}
+
+static naRef f_flightplan_insertWaypoints(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.insertWaypoints called on non-flightplan object");
+  }
+  
+  WayptVec wps;
+  if (!naIsVector(args[0])) {
+    naRuntimeError(c, "flightplan.insertWaypoints expects vector as first arg");
+  }
+
+  int count = naVec_size(args[0]);
+  for (int i=0; i<count; ++i) {
+    Waypt* wp = wayptGhost(naVec_get(args[0], i));
+    if (wp) {
+      wps.push_back(wp);
+    }
+  }
+  
+  int index = -1; // append
+  if ((argc > 1) && naIsNum(args[1])) {
+    index = (int) args[1].num;
+  }
+
+  fp->insertWayptsAtIndex(wps, index);
+  return naNil();
+}
+
+static naRef f_flightplan_clearPlan(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.clearPlan called on non-flightplan object");
+  }
+  
+  fp->clear();
+  return naNil();
 }
 
-static naRef f_route_currentIndex(naContext c, naRef me, int argc, naRef* args)
+static WayptFlag wayptFlagFromString(const char* s)
 {
-  FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-  return naNum(rm->currentIndex());
+  if (!strcmp(s, "sid")) return WPT_DEPARTURE;
+  if (!strcmp(s, "star")) return WPT_ARRIVAL;
+  if (!strcmp(s, "approach")) return WPT_APPROACH;
+  if (!strcmp(s, "missed")) return WPT_MISS;
+  if (!strcmp(s, "pseudo")) return WPT_PSEUDO;
+  
+  return (WayptFlag) 0;
+}
+
+static naRef f_flightplan_clearWPType(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.clearWPType called on non-flightplan object");
+  }
+  
+  if (argc < 1) {
+    naRuntimeError(c, "insufficent args to flightplan.clearWPType");
+  }
+  
+  WayptFlag flag = wayptFlagFromString(naStr_data(args[0]));
+  fp->clearWayptsWithFlag(flag);
+  return naNil();
+}
+
+static naRef f_flightplan_clone(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.clone called on non-flightplan object");
+  }
+  
+  return ghostForFlightPlan(c, fp->clone());
 }
 
-static naRef f_route_numWaypoints(naContext c, naRef me, int argc, naRef* args)
+static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
 {
-  FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
-  return naNum(rm->numWaypts());
+  FlightPlan::Leg* leg = fpLegGhost(me);
+  if (!leg) {
+    naRuntimeError(c, "leg.setSpeed called on non-flightplan-leg object");
+  }
+  
+  if (argc < 2) {
+    naRuntimeError(c, "bad arguments to leg.setSpeed");
+  }
+  
+  RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1]));
+  leg->setSpeed(rr, args[0].num);
+  return naNil();
+}
+
+static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan::Leg* leg = fpLegGhost(me);
+  if (!leg) {
+    naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object");
+  }
+  
+  if (argc < 2) {
+    naRuntimeError(c, "bad arguments to leg.setAltitude");
+  }
+  
+  RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1]));
+  leg->setAltitude(rr, args[0].num);
+  return naNil();
 }
 
 static naRef f_waypoint_navaid(naContext c, naRef me, int argc, naRef* args)
@@ -1147,6 +1774,23 @@ static naRef f_waypoint_runway(naContext c, naRef me, int argc, naRef* args)
   return ghostForRunway(c, (FGRunway*) pos);
 }
 
+static naRef f_procedure_transition(naContext c, naRef me, int argc, naRef* args)
+{
+  Procedure* proc = procedureGhost(me);
+  if (!proc) {
+    naRuntimeError(c, "procedure.transition called on non-procedure object");
+  }
+  
+  if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
+    naRuntimeError(c, "procedure.transition called on non-SID or -STAR");
+  }
+  
+  ArrivalDeparture* ad = (ArrivalDeparture*) proc;
+  Transition* trans = ad->findTransitionByName(naStr_data(args[0]));
+  
+  return ghostForProcedure(c, trans);
+}
+
 // Table of extension functions.  Terminate with zeros.
 static struct { const char* name; naCFunction func; } funcs[] = {
   { "carttogeod", f_carttogeod },
@@ -1160,7 +1804,7 @@ static struct { const char* name; naCFunction func; } funcs[] = {
   { "findNavaidByFrequency", f_findNavaidByFrequency },
   { "findNavaidsByFrequency", f_findNavaidsByFrequency },
   { "findNavaidsByID", f_findNavaidsByIdent },
-  { "route", f_route },
+  { "flightplan", f_route },
   { "magvar", f_magvar },
   { "courseAndDistance", f_courseAndDistance },
   { "greatCircleMove", f_greatCircleMove },
@@ -1180,17 +1824,27 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
     hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms)));
     hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids)));
     hashset(c, airportPrototype, "stars", naNewFunc(c, naNewCCode(c, f_airport_stars)));
+    hashset(c, airportPrototype, "getApproachList", naNewFunc(c, naNewCCode(c, f_airport_approaches)));
     hashset(c, airportPrototype, "parking", naNewFunc(c, naNewCCode(c, f_airport_parking)));
+    hashset(c, airportPrototype, "getSid", naNewFunc(c, naNewCCode(c, f_airport_getSid)));
+    hashset(c, airportPrototype, "getStar", naNewFunc(c, naNewCCode(c, f_airport_getStar)));
+    hashset(c, airportPrototype, "getIAP", naNewFunc(c, naNewCCode(c, f_airport_getApproach)));
   
-    routePrototype = naNewHash(c);
-    hashset(c, gcSave, "routeProto", routePrototype);
+    flightplanPrototype = naNewHash(c);
+    hashset(c, gcSave, "flightplanProto", flightplanPrototype);
       
-    hashset(c, routePrototype, "getWP", naNewFunc(c, naNewCCode(c, f_route_getWP)));
-    hashset(c, routePrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_route_currentWP))); 
-    hashset(c, routePrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_route_nextWP))); 
-    hashset(c, routePrototype, "currentIndex", naNewFunc(c, naNewCCode(c, f_route_currentIndex)));
-    hashset(c, routePrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_route_numWaypoints)));
-    
+    hashset(c, flightplanPrototype, "getWP", naNewFunc(c, naNewCCode(c, f_flightplan_getWP)));
+    hashset(c, flightplanPrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_flightplan_currentWP))); 
+    hashset(c, flightplanPrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_flightplan_nextWP))); 
+    hashset(c, flightplanPrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_flightplan_numWaypoints)));
+    hashset(c, flightplanPrototype, "appendWP", naNewFunc(c, naNewCCode(c, f_flightplan_appendWP))); 
+    hashset(c, flightplanPrototype, "insertWP", naNewFunc(c, naNewCCode(c, f_flightplan_insertWP))); 
+    hashset(c, flightplanPrototype, "insertWPAfter", naNewFunc(c, naNewCCode(c, f_flightplan_insertWPAfter))); 
+    hashset(c, flightplanPrototype, "insertWaypoints", naNewFunc(c, naNewCCode(c, f_flightplan_insertWaypoints))); 
+    hashset(c, flightplanPrototype, "cleanPlan", naNewFunc(c, naNewCCode(c, f_flightplan_clearPlan))); 
+    hashset(c, flightplanPrototype, "clearWPType", naNewFunc(c, naNewCCode(c, f_flightplan_clearWPType))); 
+    hashset(c, flightplanPrototype, "clone", naNewFunc(c, naNewCCode(c, f_flightplan_clone))); 
+  
     waypointPrototype = naNewHash(c);
     hashset(c, gcSave, "wayptProto", waypointPrototype);
     
@@ -1198,6 +1852,17 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
     hashset(c, waypointPrototype, "runway", naNewFunc(c, naNewCCode(c, f_waypoint_runway)));
     hashset(c, waypointPrototype, "airport", naNewFunc(c, naNewCCode(c, f_waypoint_airport)));
   
+    procedurePrototype = naNewHash(c);
+    hashset(c, gcSave, "procedureProto", procedurePrototype);
+ //   hashset(c, procedurePrototype, "runwayTransition", naNewFunc(c, naNewCCode(c, f_procedure_runwayTransition)));
+    hashset(c, procedurePrototype, "transition", naNewFunc(c, naNewCCode(c, f_procedure_transition)));
+   // hashset(c, procedurePrototype, "buildPath", naNewFunc(c, naNewCCode(c, f_procedure_build)));
+  
+    fpLegPrototype = naNewHash(c);
+    hashset(c, gcSave, "fpLegProto", fpLegPrototype);
+    hashset(c, fpLegPrototype, "setSpeed", naNewFunc(c, naNewCCode(c, f_leg_setSpeed)));
+    hashset(c, fpLegPrototype, "setAltitude", naNewFunc(c, naNewCCode(c, f_leg_setAltitude)));
+  
     for(int i=0; funcs[i].name; i++) {
       hashset(c, globals, funcs[i].name,
       naNewFunc(c, naNewCCode(c, funcs[i].func)));