]> git.mxchange.org Git - flightgear.git/blobdiff - src/Navaids/FlightPlan.cxx
Fix distance-along-path computation
[flightgear.git] / src / Navaids / FlightPlan.cxx
index 5630bea67eb12ce6b5b6a1448ad96a3e9499ce58..83edf5c78aded2b7b566f55fbaf73da7d0692ff0 100644 (file)
@@ -27,6 +27,7 @@
 // std
 #include <map>
 #include <fstream>
+#include <cassert>
 
 // Boost
 #include <boost/algorithm/string/case_conv.hpp>
 #include <simgear/misc/sgstream.hxx>
 #include <simgear/misc/strutils.hxx>
 #include <simgear/props/props_io.hxx>
+#include <simgear/xml/easyxml.hxx>
 
 // FlightGear
 #include <Main/globals.hxx>
 #include "Main/fg_props.hxx"
 #include <Navaids/procedure.hxx>
 #include <Navaids/waypoint.hxx>
+#include <Navaids/routePath.hxx>
 
 using std::string;
 using std::vector;
@@ -59,14 +62,18 @@ typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
 static FPDelegateFactoryVec static_delegateFactories;
   
 FlightPlan::FlightPlan() :
+  _delegateLock(0),
   _currentIndex(-1),
   _departureRunway(NULL),
   _destinationRunway(NULL),
   _sid(NULL),
   _star(NULL),
   _approach(NULL),
+  _totalDistance(0.0),
   _delegate(NULL)
 {
+  _departureChanged = _arrivalChanged = _waypointsChanged = _currentWaypointChanged = false;
+  
   BOOST_FOREACH(DelegateFactory* factory, static_delegateFactories) {
     Delegate* d = factory->createFlightPlanDelegate(this);
     if (d) { // factory might not always create a delegate
@@ -87,12 +94,18 @@ FlightPlan::~FlightPlan()
       delete cur;
     }
   }
+    
+// delete legs
+    BOOST_FOREACH(Leg* l, _legs) {
+        delete l;
+    }
 }
   
 FlightPlan* FlightPlan::clone(const string& newIdent) const
 {
   FlightPlan* c = new FlightPlan();
   c->_ident = newIdent.empty() ? _ident : newIdent;
+  c->lockDelegate();
   
 // copy destination / departure data.
   c->setDeparture(_departure);
@@ -110,10 +123,11 @@ FlightPlan* FlightPlan::clone(const string& newIdent) const
   c->setSID(_sid);
   
 // copy legs
+  c->_waypointsChanged = true;
   for (int l=0; l < numLegs(); ++l) {
     c->_legs.push_back(_legs[l]->cloneFor(c));
   }
-  
+  c->unlockDelegate();
   return c;
 }
 
@@ -142,7 +156,7 @@ FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex)
   }
   
   insertWayptsAtIndex(wps, index);
-  return legAtIndex(aIndex);
+  return legAtIndex(index);
 }
   
 void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex)
@@ -169,12 +183,10 @@ void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex)
     newLegs.push_back(new Leg(this, wp));
   }
   
+  lockDelegate();
+  _waypointsChanged = true;
   _legs.insert(it, newLegs.begin(), newLegs.end());
-  rebuildLegData();
-  
-  if (_delegate) {
-    _delegate->runWaypointsChanged();
-  }
+  unlockDelegate();
 }
 
 void FlightPlan::deleteIndex(int aIndex)
@@ -185,80 +197,123 @@ void FlightPlan::deleteIndex(int aIndex)
   }
   
   if ((index < 0) || (index >= numLegs())) {
-    SG_LOG(SG_AUTOPILOT, SG_WARN, "removeAtIndex with invalid index:" << aIndex);
+    SG_LOG(SG_NAVAID, SG_WARN, "removeAtIndex with invalid index:" << aIndex);
     return;
   }
+  
+  lockDelegate();
+  _waypointsChanged = true;
+  
   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;
+    _currentWaypointChanged = true;
   } else if (_currentIndex > index) {
     --_currentIndex; // shift current index down if necessary
   }
-
-  rebuildLegData();
-  if (_delegate) {
-    _delegate->runWaypointsChanged();
-    if (curChanged) {
-      _delegate->runCurrentWaypointChanged();
-    }
-  }
+  
+  unlockDelegate();
 }
   
 void FlightPlan::clear()
 {
+  lockDelegate();
+  _waypointsChanged = true;
+  _currentWaypointChanged = true;
+  _arrivalChanged = true;
+  _departureChanged = true;
+  
   _currentIndex = -1;
   BOOST_FOREACH(Leg* l, _legs) {
     delete l;
   }
   _legs.clear();  
-  rebuildLegData();
+  
   if (_delegate) {
-    _delegate->runDepartureChanged();
-    _delegate->runArrivalChanged();
-    _delegate->runWaypointsChanged();
-    _delegate->runCurrentWaypointChanged();
+    _delegate->runCleared();
   }
+  unlockDelegate();
 }
   
+class RemoveWithFlag
+{
+public:
+  RemoveWithFlag(WayptFlag f) : flag(f), delCount(0) { }
+  
+  int numDeleted() const { return delCount; }
+  
+  bool operator()(FlightPlan::Leg* leg) const
+  {
+    if (leg->waypoint()->flag(flag)) {
+      delete leg;
+      ++delCount;
+      return true;
+    }
+    
+    return false;
+  }
+private:
+  WayptFlag flag;
+  mutable int delCount;
+};
+  
 int FlightPlan::clearWayptsWithFlag(WayptFlag flag)
 {
   int count = 0;
-  for (unsigned int i=0; i<_legs.size(); ++i) {
+// first pass, fix up currentIndex
+  for (int i=0; i<_currentIndex; ++i) {
     Leg* l = _legs[i];
-    if (!l->waypoint()->flag(flag)) {
-      continue;
+    if (l->waypoint()->flag(flag)) {
+      ++count;
     }
-    
-  // 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) {
+  // test if the current leg will be removed
+  bool currentIsBeingCleared = false;
+  Leg* curLeg = currentLeg();
+  if (curLeg) {
+    currentIsBeingCleared = curLeg->waypoint()->flag(flag);
+  }
+  
+  _currentIndex -= count;
+    
+    // if we're clearing the current waypoint, what shall we do with the
+    // index? there's various options, but safest is to select no waypoint
+    // and let the use re-activate.
+    // http://code.google.com/p/flightgear-bugs/issues/detail?id=1134
+    if (currentIsBeingCleared) {
+        SG_LOG(SG_GENERAL, SG_INFO, "currentIsBeingCleared:" << currentIsBeingCleared);
+        _currentIndex = -1;
+    }
+  
+// now delete and remove
+  RemoveWithFlag rf(flag);
+  LegVec::iterator it = std::remove_if(_legs.begin(), _legs.end(), rf);
+  if (it == _legs.end()) {
     return 0; // nothing was cleared, don't fire the delegate
   }
   
-  rebuildLegData();
-  if (_delegate) {
-    _delegate->runWaypointsChanged();
-    _delegate->runCurrentWaypointChanged();
+  lockDelegate();
+  _waypointsChanged = true;
+  if ((count > 0) || currentIsBeingCleared) {
+    _currentWaypointChanged = true;
+  }
+  
+  _legs.erase(it, _legs.end());
+    
+  if (_legs.empty()) { // maybe all legs were deleted
+    if (_delegate) {
+      _delegate->runCleared();
+    }
   }
   
-  return count;
+  unlockDelegate();
+  return rf.numDeleted();
 }
   
 void FlightPlan::setCurrentIndex(int index)
@@ -271,10 +326,27 @@ void FlightPlan::setCurrentIndex(int index)
     return;
   }
   
+  lockDelegate();
   _currentIndex = index;
-  if (_delegate) {
-    _delegate->runCurrentWaypointChanged();
-  }
+  _currentWaypointChanged = true;
+  unlockDelegate();
+}
+    
+void FlightPlan::finish()
+{
+    if (_currentIndex == -1) {
+        return;
+    }
+    
+    lockDelegate();
+    _currentIndex = -1;
+    _currentWaypointChanged = true;
+    
+    if (_delegate) {
+        _delegate->runFinished();
+    }
+    
+    unlockDelegate();
 }
   
 int FlightPlan::findWayptIndex(const SGGeod& aPos) const
@@ -287,6 +359,17 @@ int FlightPlan::findWayptIndex(const SGGeod& aPos) const
   
   return -1;
 }
+  
+int FlightPlan::findWayptIndex(const FGPositionedRef aPos) const
+{
+  for (int i=0; i<numLegs(); ++i) {
+    if (_legs[i]->waypoint()->source() == aPos) {
+      return i;
+    }
+  }
+  
+  return -1;
+}
 
 FlightPlan::Leg* FlightPlan::currentLeg() const
 {
@@ -297,7 +380,7 @@ FlightPlan::Leg* FlightPlan::currentLeg() const
 
 FlightPlan::Leg* FlightPlan::previousLeg() const
 {
-  if (_currentIndex == 0) {
+  if (_currentIndex <= 0) {
     return NULL;
   }
   
@@ -339,13 +422,12 @@ void FlightPlan::setDeparture(FGAirport* apt)
     return;
   }
   
+  lockDelegate();
+  _departureChanged = true;
   _departure = apt;
   _departureRunway = NULL;
   setSID((SID*)NULL);
-  
-  if (_delegate) {
-    _delegate->runDepartureChanged();
-  }
+  unlockDelegate();
 }
   
 void FlightPlan::setDeparture(FGRunway* rwy)
@@ -354,15 +436,15 @@ void FlightPlan::setDeparture(FGRunway* rwy)
     return;
   }
   
+  lockDelegate();
+  _departureChanged = true;
+
   _departureRunway = rwy;
   if (rwy->airport() != _departure) {
     _departure = rwy->airport();
     setSID((SID*)NULL);
   }
-  
-  if (_delegate) {
-    _delegate->runDepartureChanged();
-  }
+  unlockDelegate();
 }
   
 void FlightPlan::setSID(SID* sid, const std::string& transition)
@@ -371,12 +453,11 @@ void FlightPlan::setSID(SID* sid, const std::string& transition)
     return;
   }
   
+  lockDelegate();
+  _departureChanged = true;
   _sid = sid;
   _sidTransition = transition;
-  
-  if (_delegate) {
-    _delegate->runDepartureChanged();
-  }
+  unlockDelegate();
 }
   
 void FlightPlan::setSID(Transition* trans)
@@ -407,13 +488,13 @@ void FlightPlan::setDestination(FGAirport* apt)
     return;
   }
   
+  lockDelegate();
+  _arrivalChanged = true;
   _destination = apt;
   _destinationRunway = NULL;
   setSTAR((STAR*)NULL);
-
-  if (_delegate) {
-    _delegate->runArrivalChanged();
-  }
+  setApproach(NULL);
+  unlockDelegate();
 }
     
 void FlightPlan::setDestination(FGRunway* rwy)
@@ -422,15 +503,15 @@ void FlightPlan::setDestination(FGRunway* rwy)
     return;
   }
   
+  lockDelegate();
+  _arrivalChanged = true;
   _destinationRunway = rwy;
   if (_destination != rwy->airport()) {
     _destination = rwy->airport();
     setSTAR((STAR*)NULL);
   }
   
-  if (_delegate) {
-    _delegate->runArrivalChanged();
-  }
+  unlockDelegate();
 }
   
 void FlightPlan::setSTAR(STAR* star, const std::string& transition)
@@ -439,12 +520,11 @@ void FlightPlan::setSTAR(STAR* star, const std::string& transition)
     return;
   }
   
+  lockDelegate();
+  _arrivalChanged = true;
   _star = star;
   _starTransition = transition;
-  
-  if (_delegate) {
-    _delegate->runArrivalChanged();
-  }
+  unlockDelegate();
 }
   
 void FlightPlan::setSTAR(Transition* trans)
@@ -475,6 +555,8 @@ void FlightPlan::setApproach(flightgear::Approach *app)
     return;
   }
   
+  lockDelegate();
+  _arrivalChanged = true;
   _approach = app;
   if (app) {
     // keep runway + airport in sync
@@ -486,15 +568,12 @@ void FlightPlan::setApproach(flightgear::Approach *app)
       _destination = _destinationRunway->airport();
     }
   }
-
-  if (_delegate) {
-    _delegate->runArrivalChanged();
-  }
+  unlockDelegate();
 }
   
 bool FlightPlan::save(const SGPath& path)
 {
-  SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str());
+  SG_LOG(SG_NAVAID, SG_INFO, "Saving route to " << path.str());
   try {
     SGPropertyNode_ptr d(new SGPropertyNode);
     d->setIntValue("version", 2);
@@ -536,32 +615,146 @@ bool FlightPlan::save(const SGPath& path)
     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());
+    SG_LOG(SG_NAVAID, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage());
     return false;
   }
 }
-  
+
 bool FlightPlan::load(const SGPath& path)
 {
   if (!path.exists())
   {
-    SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str()
+    SG_LOG(SG_NAVAID, 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());
+
+  SG_LOG(SG_NAVAID, SG_INFO, "going to read flight-plan from:" << path.str());
   
   bool Status = false;
+  lockDelegate();
+
+  // try different file formats
+  if (loadGpxFormat(path)) // GPX format
+      Status = true;
+  else
+  if (loadXmlFormat(path)) // XML property data
+      Status = true;
+  else
+  if (loadPlainTextFormat(path)) // simple textual list of waypoints
+      Status = true;
+
+  _waypointsChanged = true;
+  unlockDelegate();
+
+  return Status;
+}
+
+/** XML loader for GPX file format */
+class GpxXmlVisitor : public XMLVisitor
+{
+public:
+    GpxXmlVisitor(FlightPlan* fp) : _fp(fp), _lat(-9999), _lon(-9999) {}
+
+    virtual void startElement (const char * name, const XMLAttributes &atts);
+    virtual void endElement (const char * name);
+    virtual void data (const char * s, int length);
+
+private:
+    FlightPlan* _fp;
+    double      _lat, _lon;
+    string      _element;
+    string      _waypoint;
+};
+
+void GpxXmlVisitor::startElement(const char * name, const XMLAttributes &atts)
+{
+    _element = name;
+    if (strcmp(name, "rtept")==0)
+    {
+        _waypoint = "";
+        _lat = _lon = -9999;
+
+        const char* slat = atts.getValue("lat");
+        const char* slon = atts.getValue("lon");
+        if (slat && slon)
+        {
+            _lat = atof(slat);
+            _lon = atof(slon);
+        }
+    }
+}
+
+void GpxXmlVisitor::data(const char * s, int length)
+{
+    // use "name" when given, otherwise use "cmt" (comment) as ID
+    if ((_element == "name")||
+        ((_waypoint == "")&&(_element == "cmt")))
+    {
+        char* buf = (char*) malloc(length+1);
+        memcpy(buf, s, length);
+        buf[length] = 0;
+        _waypoint = buf;
+        free(buf);
+    }
+}
+
+void GpxXmlVisitor::endElement(const char * name)
+{
+    _element = "";
+    if (strcmp(name, "rtept") == 0)
+    {
+        if (_lon > -9990.0)
+        {
+            _fp->insertWayptAtIndex(new BasicWaypt(SGGeod::fromDeg(_lon, _lat), _waypoint.c_str(), NULL), -1);
+        }
+    }
+}
+
+/** Load a flightplan in GPX format */
+bool FlightPlan::loadGpxFormat(const SGPath& path)
+{
+    if (path.lower_extension() != "gpx")
+    {
+        // not a valid GPX file
+        return false;
+    }
+
+    _legs.clear();
+    GpxXmlVisitor gpxVistor(this);
+    try
+    {
+        readXML(path.str(), gpxVistor);
+    } catch (sg_exception& e)
+    {
+        // XML parsing fails => not a GPX XML file
+        SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format: '" << e.getOrigin()
+                     << "'. " << e.getMessage());
+        return false;
+    }
+
+    if (numLegs() == 0)
+    {
+        SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan in GPX format. No route found.");
+        return false;
+    }
+
+    return true;
+}
+
+/** Load a flightplan in FlightGear XML property format */
+bool FlightPlan::loadXmlFormat(const SGPath& path)
+{
+  SGPropertyNode_ptr routeData(new SGPropertyNode);
   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;
+  } catch (sg_exception& e) {
+     SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
+             << "'. " << e.getMessage());
+    // XML parsing fails => not a property XML file
+    return false;
   }
-  
+
   if (routeData.valid())
   {
     try {
@@ -573,20 +766,14 @@ bool FlightPlan::load(const SGPath& path)
       } else {
         throw sg_io_exception("unsupported XML route version");
       }
-      Status = true;
+      return true;
     } catch (sg_exception& e) {
-      SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
+      SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
              << "'. " << e.getMessage());
-      Status = false;
     }
   }
-  
-  rebuildLegData();
-  if (_delegate) {
-    _delegate->runWaypointsChanged();
-  }
-  
-  return Status;
+
+  return false;
 }
 
 void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
@@ -597,8 +784,9 @@ void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
     string depIdent = dep->getStringValue("airport");
     setDeparture((FGAirport*) fgFindAirportID(depIdent));
     if (_departure) {
-      if (dep->hasChild("runway")) {
-        setDeparture(_departure->getRunwayByIdent(dep->getStringValue("runway")));
+      string rwy(dep->getStringValue("runway"));
+      if (_departure->hasRunwayWithIdent(rwy)) {
+        setDeparture(_departure->getRunwayByIdent(rwy));
       }
     
       if (dep->hasChild("sid")) {
@@ -613,8 +801,9 @@ void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
   if (dst) {
     setDestination((FGAirport*) fgFindAirportID(dst->getStringValue("airport")));
     if (_destination) {
-      if (dst->hasChild("runway")) {
-        setDestination(_destination->getRunwayByIdent(dst->getStringValue("runway")));
+      string rwy(dst->getStringValue("runway"));
+      if (_destination->hasRunwayWithIdent(rwy)) {
+        setDestination(_destination->getRunwayByIdent(rwy));
       }
       
       if (dst->hasChild("star")) {
@@ -651,12 +840,15 @@ void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr 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
+  SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);
+  if (routeNode.valid()) {
+    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
+  }
+  _waypointsChanged = true;
 }
 
 void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData)
@@ -671,7 +863,7 @@ void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData)
     Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode));
     _legs.push_back(l);
   } // of route iteration
-
+  _waypointsChanged = true;
 }
 
 WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
@@ -693,18 +885,23 @@ WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
   } 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;
+    
+    if (p) {
+      pos = p->geod();
+    } else {
+      SG_LOG(SG_GENERAL, SG_WARN, "unknown navaid in flightplan:" << nid);
+      pos = SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"),
+                            aWP->getDoubleValue("latitude-deg"));
     }
     
-    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);
+      SGGeodesy::direct(pos, radialDeg, offsetNm * SG_NM_TO_METER, pos, az2);
     }
     
     w = new BasicWaypt(pos, ident, NULL);
@@ -718,7 +915,8 @@ WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
   return w;
 }
 
-bool FlightPlan::loadPlainTextRoute(const SGPath& path)
+/** Load a flightplan in FlightGear plain-text format */
+bool FlightPlan::loadPlainTextFormat(const SGPath& path)
 {
   try {
     sg_gzifstream in(path.str().c_str());
@@ -748,7 +946,7 @@ bool FlightPlan::loadPlainTextRoute(const SGPath& path)
       _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());
+    SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage());
     _legs.clear();
     return false;
   }
@@ -808,7 +1006,7 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
   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());
+    SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front());
     return NULL;
   }
   
@@ -825,12 +1023,12 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
   } 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());
+      SG_LOG(SG_NAVAID, 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]);
+      SG_LOG(SG_NAVAID, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]);
       return NULL;
     }
     
@@ -840,7 +1038,7 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
     // 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]);
+      SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]);
       return NULL;
     }
     
@@ -852,7 +1050,7 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
     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);
+      SG_LOG(SG_NAVAID, SG_INFO, "no valid intersection for:" << target);
       return NULL;
     }
     
@@ -861,7 +1059,7 @@ WayptRef FlightPlan::waypointFromString(const string& tgt )
   }
   
   if (!wpt) {
-    SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target);
+    SG_LOG(SG_NAVAID, SG_INFO, "Unable to parse waypoint:" << target);
     return NULL;
   }
   
@@ -989,18 +1187,79 @@ double FlightPlan::Leg::distanceAlongRoute() const
 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];
+  double totalDistanceIncludingMissed = 0.0;
+  RoutePath path(this);
+  
+  for (unsigned int l=0; l<_legs.size(); ++l) {
+    _legs[l]->_courseDeg = path.trackForIndex(l);
+    _legs[l]->_pathDistance = path.distanceForIndex(l) * SG_METER_TO_NM;
+
+    totalDistanceIncludingMissed += _legs[l]->_pathDistance;
+    // distance along path includes our own leg distance
+    _legs[l]->_distanceAlongPath = totalDistanceIncludingMissed;
+    
+    // omit missed-approach waypoints from total distance calculation
+    if (!_legs[l]->waypoint()->flag(WPT_MISS)) {
+      _totalDistance += _legs[l]->_pathDistance;
+    }
+} // of legs iteration
+  
+}
+  
+SGGeod FlightPlan::pointAlongRoute(int aIndex, double aOffsetNm) const
+{
+    RoutePath rp(this);
+    return rp.positionForDistanceFrom(aIndex, aOffsetNm * SG_NM_TO_METER);
+}
     
-    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::lockDelegate()
+{
+  if (_delegateLock == 0) {
+    assert(!_departureChanged && !_arrivalChanged && 
+           !_waypointsChanged && !_currentWaypointChanged);
+  }
+  
+  ++_delegateLock;
+}
+
+void FlightPlan::unlockDelegate()
+{
+  assert(_delegateLock > 0);
+  if (_delegateLock > 1) {
+    --_delegateLock;
+    return;
+  }
+  
+  if (_departureChanged) {
+    _departureChanged = false;
+    if (_delegate) {
+      _delegate->runDepartureChanged();
+    }
+  }
+  
+  if (_arrivalChanged) {
+    _arrivalChanged = false;
+    if (_delegate) {
+      _delegate->runArrivalChanged();
+    }
+  }
+  
+  if (_waypointsChanged) {
+    _waypointsChanged = false;
+    rebuildLegData();
+    if (_delegate) {
+      _delegate->runWaypointsChanged();
+    }
+  }
+  
+  if (_currentWaypointChanged) {
+    _currentWaypointChanged = false;
+    if (_delegate) {
+      _delegate->runCurrentWaypointChanged();
+    }
+  }
+
+  --_delegateLock;
 }
   
 void FlightPlan::registerDelegateFactory(DelegateFactory* df)
@@ -1045,18 +1304,16 @@ FlightPlan::Delegate::Delegate() :
   _deleteWithPlan(false),
   _inner(NULL)
 {
-  
 }
 
 FlightPlan::Delegate::~Delegate()
-{
-  
+{  
 }
 
 void FlightPlan::Delegate::removeInner(Delegate* d)
 {
   if (!_inner) {
-    return;
+      throw sg_exception("FlightPlan delegate not found");
   }
   
   if (_inner == d) {
@@ -1090,5 +1347,17 @@ void FlightPlan::Delegate::runCurrentWaypointChanged()
   if (_inner) _inner->runCurrentWaypointChanged();
   currentWaypointChanged();
 }
-  
+
+void FlightPlan::Delegate::runCleared()
+{
+  if (_inner) _inner->runCleared();
+  cleared();
+}  
+
+void FlightPlan::Delegate::runFinished()
+{
+    if (_inner) _inner->runFinished();
+    endOfFlightPlan();
+}
+    
 } // of namespace flightgear