]> git.mxchange.org Git - flightgear.git/blobdiff - src/Scripting/NasalPositioned.cxx
Expose more runway methods to Nasal
[flightgear.git] / src / Scripting / NasalPositioned.cxx
index ff7546bfc2db6cfbab434cda9d17c9decd3a7de2..1f10e5baf73bd213c5a1740d1a1189b863cfd838 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <boost/foreach.hpp>
 #include <boost/algorithm/string/case_conv.hpp>
+#include <boost/tuple/tuple.hpp> // for boost::tie
 
 #include <simgear/sg_inlines.h>
 #include <simgear/scene/material/mat.hxx>
@@ -36,7 +37,7 @@
 #include <simgear/bucket/newbucket.hxx>
 
 #include <Airports/runways.hxx>
-#include <Airports/simple.hxx>
+#include <Airports/airport.hxx>
 #include <Airports/dynamics.hxx>
 #include <Airports/parking.hxx>
 #include <Scripting/NasalSys.hxx>
 #include <Navaids/waypoint.hxx>
 #include <Navaids/fix.hxx>
 #include <Autopilot/route_mgr.hxx>
+#include <Navaids/routePath.hxx>
 #include <Navaids/procedure.hxx>
 #include <Navaids/airways.hxx>
+#include <Navaids/NavDataCache.hxx>
 
 using namespace flightgear;
 
@@ -70,15 +73,19 @@ naGhostType NavaidGhostType = { positionedGhostDestroy, "navaid", navaidGhostGet
 
 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out);
 naGhostType RunwayGhostType = { positionedGhostDestroy, "runway", runwayGhostGetMember, 0 };
+naGhostType HelipadGhostType = { positionedGhostDestroy, "helipad", runwayGhostGetMember, 0 };
+naGhostType TaxiwayGhostType = { positionedGhostDestroy, "taxiway", runwayGhostGetMember, 0 };
 
 static const char* fixGhostGetMember(naContext c, void* g, naRef field, naRef* out);
 naGhostType FixGhostType = { positionedGhostDestroy, "fix", fixGhostGetMember, 0 };
 
 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out);
+static void waypointGhostSetMember(naContext c, void* g, naRef field, naRef value);
+
 naGhostType WayptGhostType = { wayptGhostDestroy, 
   "waypoint",
   wayptGhostGetMember,
-  0};
+  waypointGhostSetMember};
 
 static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out);
 static void legGhostSetMember(naContext c, void* g, naRef field, naRef value);
@@ -172,6 +179,13 @@ static FGRunway* runwayGhost(naRef r)
   return 0;
 }
 
+static FGTaxiway* taxiwayGhost(naRef r)
+{
+  if (naGhost_type(r) == &TaxiwayGhostType)
+    return (FGTaxiway*) naGhost_ptr(r);
+  return 0;
+}
+
 static FGFix* fixGhost(naRef r)
 {
   if (naGhost_type(r) == &FixGhostType)
@@ -276,6 +290,26 @@ naRef ghostForRunway(naContext c, const FGRunway* r)
   return naNewGhost2(c, &RunwayGhostType, (void*) r);
 }
 
+naRef ghostForHelipad(naContext c, const FGHelipad* r)
+{
+  if (!r) {
+    return naNil();
+  }
+
+  FGPositioned::get(r); // take a ref
+  return naNewGhost2(c, &HelipadGhostType, (void*) r);
+}
+
+naRef ghostForTaxiway(naContext c, const FGTaxiway* r)
+{
+  if (!r) {
+    return naNil();
+  }
+  
+  FGPositioned::get(r); // take a ref
+  return naNewGhost2(c, &TaxiwayGhostType, (void*) r);
+}
+
 naRef ghostForFix(naContext c, const FGFix* r)
 {
   if (!r) {
@@ -292,7 +326,7 @@ naRef ghostForWaypt(naContext c, const Waypt* wpt)
   if (!wpt) {
     return naNil();
   }
-  
+
   Waypt::get(wpt); // take a ref
   return naNewGhost2(c, &WayptGhostType, (void*) wpt);
 }
@@ -342,12 +376,35 @@ static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRe
     *out = naNum(apt->getMetar());
   } else if (!strcmp(fieldName, "runways")) {
     *out = naNewHash(c);
+    double minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft");
     for(unsigned int r=0; r<apt->numRunways(); ++r) {
       FGRunway* rwy(apt->getRunwayByIndex(r));
+      // ignore unusably short runways
+      if (rwy->lengthFt() < minLengthFt) {
+        continue;
+      }
       naRef rwyid = stringToNasal(c, rwy->ident());
       naRef rwydata = ghostForRunway(c, rwy);
       naHash_set(*out, rwyid, rwydata);
     }
+  } else if (!strcmp(fieldName, "helipads")) {
+    *out = naNewHash(c);
+
+    for(unsigned int r=0; r<apt->numHelipads(); ++r) {
+      FGHelipad* hp(apt->getHelipadByIndex(r));
+
+      naRef rwyid = stringToNasal(c, hp->ident());
+      naRef rwydata = ghostForHelipad(c, hp);
+      naHash_set(*out, rwyid, rwydata);
+    }
+
+  } else if (!strcmp(fieldName, "taxiways")) {
+    *out = naNewVector(c);
+    for(unsigned int r=0; r<apt->numTaxiways(); ++r) {
+      FGTaxiway* taxi(apt->getTaxiwayByIndex(r));
+      naRef taxidata = ghostForTaxiway(c, taxi);
+      naVec_append(*out, taxidata);
+    }
 
   } else {
     return 0;
@@ -382,6 +439,20 @@ static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char*
   return "";
 }
 
+static void waypointCommonSetMember(naContext c, Waypt* wpt, const char* fieldName, naRef value)
+{
+  if (!strcmp(fieldName, "wp_role")) {
+    if (!naIsString(value)) naRuntimeError(c, "wp_role must be a string");
+    if (wpt->owner() != NULL) naRuntimeError(c, "cannot override wp_role on waypoint with parent");
+    WayptFlag f = wayptFlagFromString(naStr_data(value));
+    if (f == 0) {
+      naRuntimeError(c, "unrecognized wp_role value %s", naStr_data(value));
+    }
+    
+    wpt->setFlag(f, true);
+  }
+}
+
 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out)
 {
   const char* fieldName = naStr_data(field);
@@ -450,21 +521,19 @@ static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* o
   return ""; // success
 }
 
+static void waypointGhostSetMember(naContext c, void* g, naRef field, naRef value)
+{
+  const char* fieldName = naStr_data(field);
+  Waypt* wpt = (Waypt*) g;
+  waypointCommonSetMember(c, wpt, fieldName, value);
+}
+
 static void legGhostSetMember(naContext c, void* g, naRef field, naRef value)
 {
   const char* fieldName = naStr_data(field);
   FlightPlan::Leg* leg = (FlightPlan::Leg*) g;
-  
-  if (!strcmp(fieldName, "wp_role")) {
-    if (!naIsString(value)) naRuntimeError(c, "wp_role must be a string");
-    if (leg->waypoint()->owner() != NULL) naRuntimeError(c, "cannot override wp_role on waypoint with parent");
-    WayptFlag f = wayptFlagFromString(naStr_data(value));
-    if (f == 0) {
-      naRuntimeError(c, "unrecognized wp_role value %s", naStr_data(value));
-    }
     
-    leg->waypoint()->setFlag(f, true);
-  }
+  waypointCommonSetMember(c, leg->waypoint(), fieldName, value);
 }
 
 static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out)
@@ -648,7 +717,7 @@ static const char* procedureGhostGetMember(naContext c, void* g, naRef field, na
   else if (!strcmp(fieldName, "radio")) *out = procedureRadioType(c, proc->type());
   else if (!strcmp(fieldName, "runways")) {
     *out = naNewVector(c);
-    BOOST_FOREACH(FGRunwayPtr rwy, proc->runways()) {
+    BOOST_FOREACH(FGRunwayRef rwy, proc->runways()) {
       naVec_append(*out, stringToNasal(c, rwy->ident()));
     }
   } else if (!strcmp(fieldName, "transitions")) {
@@ -672,22 +741,30 @@ static const char* procedureGhostGetMember(naContext c, void* g, naRef field, na
 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out)
 {
   const char* fieldName = naStr_data(field);
-  FGRunway* rwy = (FGRunway*) g;
-  
-  if (!strcmp(fieldName, "id")) *out = stringToNasal(c, rwy->ident());
-  else if (!strcmp(fieldName, "lat")) *out = naNum(rwy->latitude());
-  else if (!strcmp(fieldName, "lon")) *out = naNum(rwy->longitude());
-  else if (!strcmp(fieldName, "heading")) *out = naNum(rwy->headingDeg());
-  else if (!strcmp(fieldName, "length")) *out = naNum(rwy->lengthM());
-  else if (!strcmp(fieldName, "width")) *out = naNum(rwy->widthM());
-  else if (!strcmp(fieldName, "threshold")) *out = naNum(rwy->displacedThresholdM());
-  else if (!strcmp(fieldName, "stopway")) *out = naNum(rwy->stopwayM());
-  else if (!strcmp(fieldName, "ils_frequency_mhz")) {
-    *out = rwy->ILS() ? naNum(rwy->ILS()->get_freq() / 100.0) : naNil();
-  } else if (!strcmp(fieldName, "ils")) {
-    *out = ghostForNavaid(c, rwy->ILS());
+  FGRunwayBase* base = (FGRunwayBase*) g;
+  
+  if (!strcmp(fieldName, "id")) *out = stringToNasal(c, base->ident());
+  else if (!strcmp(fieldName, "lat")) *out = naNum(base->latitude());
+  else if (!strcmp(fieldName, "lon")) *out = naNum(base->longitude());
+  else if (!strcmp(fieldName, "heading")) *out = naNum(base->headingDeg());
+  else if (!strcmp(fieldName, "length")) *out = naNum(base->lengthM());
+  else if (!strcmp(fieldName, "width")) *out = naNum(base->widthM());
+  else if (!strcmp(fieldName, "surface")) *out = naNum(base->surface());
+  else if (base->type() == FGRunwayBase::RUNWAY) {  
+    FGRunway* rwy = (FGRunway*) g;
+    if (!strcmp(fieldName, "threshold")) *out = naNum(rwy->displacedThresholdM());
+    else if (!strcmp(fieldName, "stopway")) *out = naNum(rwy->stopwayM());
+    else if (!strcmp(fieldName, "reciprocal")) {
+      *out = ghostForRunway(c, rwy->reciprocalRunway());
+    } else if (!strcmp(fieldName, "ils_frequency_mhz")) {
+      *out = rwy->ILS() ? naNum(rwy->ILS()->get_freq() / 100.0) : naNil();
+    } else if (!strcmp(fieldName, "ils")) {
+      *out = ghostForNavaid(c, rwy->ILS());
+    } else {
+      return 0;
+    }
   } else {
-    return 0;
+    return 0;    
   }
   
   return "";
@@ -747,7 +824,7 @@ static bool hashIsCoord(naRef h)
     return false;
   }
   
-  return naEqual(naVec_get(parents, 0), geoCoordClass);
+  return naEqual(naVec_get(parents, 0), geoCoordClass) != 0;
 }
 
 bool geodFromHash(naRef ref, SGGeod& result)
@@ -803,6 +880,11 @@ static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
       return 1;
     }
     
+    if (gt == &TaxiwayGhostType) {
+      result = taxiwayGhost(args[offset])->geod();
+      return 1;
+    }
+    
     if (gt == &FixGhostType) {
       result = fixGhost(args[offset])->geod();
       return 1;
@@ -873,10 +955,11 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
   double lat = naNumValue(args[0]).num;
   double lon = naNumValue(args[1]).num;
   double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
-  const SGMaterial *mat;
+  const simgear::BVHMaterial *material;
   SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
-  if(!globals->get_scenery()->get_elevation_m(geod, elev, &mat))
+  if(!globals->get_scenery()->get_elevation_m(geod, elev, &material))
     return naNil();
+  const SGMaterial *mat = dynamic_cast<const SGMaterial *>(material);
   naRef vec = naNewVector(c);
   naVec_append(vec, naNum(elev));
   naRef matdata = naNil();
@@ -904,6 +987,7 @@ class AirportInfoFilter : public FGAirport::AirportFilter
 {
 public:
   AirportInfoFilter() : type(FGPositioned::AIRPORT) {
+    minRunwayLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0);
   }
   
   bool fromArg(naRef arg)
@@ -925,8 +1009,21 @@ public:
   virtual FGPositioned::Type maxType() const {
     return type;
   }
+    
+  virtual bool pass(FGPositioned* aPos) const
+  {
+    FGAirport* apt = (FGAirport*) aPos;
+    if ((apt->type() == FGPositioned::AIRPORT) && 
+        !apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) 
+    {
+      return false;
+    }
+
+    return true;
+  }
   
   FGPositioned::Type type;
+  double minRunwayLengthFt;
 };
 
 // Returns data hash for particular or nearest airport of a <type>, or nil
@@ -1084,18 +1181,56 @@ static naRef f_airport_runway(naContext c, naRef me, int argc, naRef* args)
   if (!apt) {
     naRuntimeError(c, "airport.runway called on non-airport object");
   }
-  
+
   if ((argc < 1) || !naIsString(args[0])) {
     naRuntimeError(c, "airport.runway expects a runway ident argument");
   }
-  
+
   std::string ident(naStr_data(args[0]));
   boost::to_upper(ident);
-  if (!apt->hasRunwayWithIdent(ident)) {
-    return naNil();
+
+  if (apt->hasRunwayWithIdent(ident)) {
+    return ghostForRunway(c, apt->getRunwayByIdent(ident));
+  } else if (apt->hasHelipadWithIdent(ident)) {
+    return ghostForHelipad(c, apt->getHelipadByIdent(ident));
   }
+  return naNil();
+}
+
+static naRef f_airport_runwaysWithoutReciprocals(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.runwaysWithoutReciprocals called on non-airport object");
+  }
+
+  FGRunwayList rwylist(apt->getRunwaysWithoutReciprocals());
+  naRef runways = naNewVector(c);
+  for (unsigned int r=0; r<rwylist.size(); ++r) {
+    FGRunway* rwy(rwylist[r]);
+    naVec_append(runways, ghostForRunway(c, apt->getRunwayByIdent(rwy->ident())));
+  }
+  return runways;
+}
+
+static naRef f_airport_taxiway(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.taxiway called on non-airport object");
+  }
+  
+  if ((argc < 1) || !naIsString(args[0])) {
+    naRuntimeError(c, "airport.taxiway expects a taxiway ident argument");
+  }
+  
+  naRef taxiways = naNewVector(c);
   
-  return ghostForRunway(c, apt->getRunwayByIdent(ident));
+  for (unsigned int i = 0; i < apt->numTaxiways(); i++) {
+    naVec_append(taxiways, ghostForTaxiway(c, apt->getTaxiwayByIndex(i)));
+  }
+  
+  return taxiways;  
 }
 
 static naRef f_airport_sids(naContext c, naRef me, int argc, naRef* args)
@@ -1245,19 +1380,27 @@ static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
   }
   
   FGAirportDynamics* dynamics = apt->getDynamics();
-  for (int i=0; i<dynamics->getNrOfParkings(); ++i) {
-    FGParking* park = dynamics->getParking(i);
-  // filter out based on availability and type
-    if (onlyAvailable && !park->isAvailable()) {
+  PositionedIDVec parkings = flightgear::NavDataCache::instance()->airportItemsOfType(apt->guid(),
+                                                                                      FGPositioned::PARKING);
+  
+  BOOST_FOREACH(PositionedID parking, parkings) {
+    // filter out based on availability and type
+    if (onlyAvailable && !dynamics->isParkingAvailable(parking)) {
       continue;
     }
     
+    FGParking* park = dynamics->getParking(parking);
     if (!type.empty() && (park->getType() != type)) {
       continue;
     }
     
-    naRef nm = stringToNasal(c, park->getName());
-    naVec_append(r, nm);
+    const SGGeod& parkLoc = park->geod();
+    naRef ph = naNewHash(c);
+    hashset(c, ph, "name", stringToNasal(c, park->getName()));
+    hashset(c, ph, "lat", naNum(parkLoc.getLatitudeDeg()));
+    hashset(c, ph, "lon", naNum(parkLoc.getLongitudeDeg()));
+    hashset(c, ph, "elevation", naNum(parkLoc.getElevationM()));
+    naVec_append(r, ph);
   }
   
   return r;
@@ -1308,6 +1451,16 @@ static naRef f_airport_getApproach(naContext c, naRef me, int argc, naRef* args)
   return ghostForProcedure(c, apt->findApproachWithIdent(ident));
 }
 
+static naRef f_airport_toString(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.tostring called on non-airport object");
+  }
+  
+  return stringToNasal(c, "an airport " + apt->ident());
+}
+
 // Returns vector of data hash for navaid of a <type>, nil on error
 // navaids sorted by ascending distance 
 // navinfo([<lat>,<lon>],[<type>],[<id>])
@@ -1365,7 +1518,8 @@ static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
     return naNil();
   }
   
-  navlist = globals->get_navlist()->findByIdentAndFreq( pos, id, 0.0, type );
+  FGNavList::TypeFilter filter(type);
+  navlist = FGNavList::findByIdentAndFreq( pos, id, 0.0, &filter );
   
   naRef reply = naNewVector(c);
   for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
@@ -1419,7 +1573,8 @@ static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* arg
     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
   }
   
-  nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
+  FGNavList::TypeFilter filter(type);
+  nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
   if (navs.empty()) {
     return naNil();
   }
@@ -1444,7 +1599,9 @@ static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* ar
   }
   
   naRef r = naNewVector(c);
-  nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
+  
+  FGNavList::TypeFilter filter(type);
+  nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
   
   BOOST_FOREACH(nav_rec_ptr a, navs) {
     naVec_append(r, ghostForNavaid(c, a.ptr()));
@@ -1469,8 +1626,9 @@ static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
   }
   
+  FGNavList::TypeFilter filter(type);
   naRef r = naNewVector(c);
-  nav_list_type navs = globals->get_navlist()->findByIdentAndFreq(pos, ident, 0.0, type);
+  nav_list_type navs = FGNavList::findByIdentAndFreq(pos, ident, 0.0, &filter);
   
   BOOST_FOREACH(nav_rec_ptr a, navs) {
     naVec_append(r, ghostForNavaid(c, a.ptr()));
@@ -1617,6 +1775,113 @@ static naRef f_route(naContext c, naRef me, int argc, naRef* args)
   return naNil();
 }
 
+class NasalFPDelegate : public FlightPlan::Delegate
+{
+public:
+  NasalFPDelegate(FlightPlan* fp, FGNasalSys* sys, naRef ins) :
+    _nasal(sys),
+    _plan(fp),
+    _instance(ins)
+  {
+    SG_LOG(SG_NASAL, SG_INFO, "created Nasal delegate for " << fp);
+    _gcSaveKey = _nasal->gcSave(ins);
+  }
+  
+  virtual ~NasalFPDelegate()
+  {
+    SG_LOG(SG_NASAL, SG_INFO, "destroying Nasal delegate for " << _plan);
+    _nasal->gcRelease(_gcSaveKey);
+  }
+  
+  virtual void departureChanged()
+  {
+    callDelegateMethod("departureChanged");
+  }
+  
+  virtual void arrivalChanged()
+  {
+    callDelegateMethod("arrivalChanged");
+  }
+  
+  virtual void waypointsChanged()
+  {
+    callDelegateMethod("waypointsChanged");
+  }
+  
+  virtual void currentWaypointChanged()
+  {
+    callDelegateMethod("currentWaypointChanged");
+  }
+    
+  virtual void cleared()
+  {
+    callDelegateMethod("cleared");
+  }
+private:
+  
+  void callDelegateMethod(const char* method)
+  {
+    naRef f;
+    naMember_cget(_nasal->context(), _instance, method, &f);
+    if (naIsNil(f)) {
+      return; // no method on the delegate
+    }
+    
+    naRef arg[1];
+    arg[0] = ghostForFlightPlan(_nasal->context(), _plan);
+    _nasal->callMethod(f, _instance, 1, arg, naNil());
+  }
+  
+  FGNasalSys* _nasal;
+  FlightPlan* _plan;
+  naRef _instance;
+  int _gcSaveKey;
+};
+
+class NasalFPDelegateFactory : public FlightPlan::DelegateFactory
+{
+public:
+  NasalFPDelegateFactory(naRef code)
+  {
+    _nasal = (FGNasalSys*) globals->get_subsystem("nasal");
+    _func = code;
+    _gcSaveKey = _nasal->gcSave(_func);
+  }
+  
+  ~NasalFPDelegateFactory()
+  {
+    _nasal->gcRelease(_gcSaveKey);
+  }
+  
+  virtual FlightPlan::Delegate* createFlightPlanDelegate(FlightPlan* fp)
+  {
+    naRef args[1];
+    args[0] = ghostForFlightPlan(_nasal->context(), fp);
+    naRef instance = _nasal->call(_func, 1, args, naNil());
+    if (naIsNil(instance)) {
+      return NULL;
+    }
+    
+    return new NasalFPDelegate(fp, _nasal, instance);
+  }
+private:
+  FGNasalSys* _nasal;
+  naRef _func;
+  int _gcSaveKey;
+};
+
+static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef* args)
+{
+  if ((argc < 1) || !naIsFunc(args[0])) {
+    naRuntimeError(c, "non-function argument to registerFlightPlanDelegate");
+  }
+  
+  NasalFPDelegateFactory* factory = new NasalFPDelegateFactory(args[0]);
+  FlightPlan::registerDelegateFactory(factory);
+  
+  return naNil();
+}
+
 static WayptRef wayptFromArg(naRef arg)
 {
   WayptRef r = wayptGhost(arg);
@@ -1905,6 +2170,31 @@ static naRef f_flightplan_clone(naContext c, naRef me, int argc, naRef* args)
   return ghostForFlightPlan(c, fp->clone());
 }
 
+static naRef f_flightplan_pathGeod(naContext c, naRef me, int argc, naRef* args)
+{
+  FlightPlan* fp = flightplanGhost(me);
+  if (!fp) {
+    naRuntimeError(c, "flightplan.clone called on non-flightplan object");
+  }
+
+  if ((argc < 1) || !naIsNum(args[0])) {
+    naRuntimeError(c, "bad argument to flightplan.pathGeod");
+  }
+
+  if ((argc > 1) && !naIsNum(args[1])) {
+    naRuntimeError(c, "bad argument to flightplan.pathGeod");
+  }
+
+  int index = (int) args[0].num;
+  double offset = (argc > 1) ? args[1].num : 0.0;
+  naRef result = naNewHash(c);
+  SGGeod g = fp->pointAlongRoute(index, offset);
+  hashset(c, result, "lat", naNum(g.getLatitudeDeg()));
+  hashset(c, result, "lon", naNum(g.getLongitudeDeg()));
+  return result;
+}
+
+
 static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
 {
   FlightPlan::Leg* leg = fpLegGhost(me);
@@ -1937,6 +2227,48 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
   return naNil();
 }
 
+static naRef f_leg_path(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");
+  }
+  
+  RoutePath path(leg->owner());
+  SGGeodVec gv(path.pathForIndex(leg->index()));
+
+  naRef result = naNewVector(c);
+  BOOST_FOREACH(SGGeod p, gv) {
+    // construct a geo.Coord!
+    naRef coord = naNewHash(c);
+    hashset(c, coord, "lat", naNum(p.getLatitudeDeg()));
+    hashset(c, coord, "lon", naNum(p.getLongitudeDeg()));
+    naVec_append(result, coord);
+  }
+
+  return result;
+}
+
+static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef* args)
+{
+    FlightPlan::Leg* leg = fpLegGhost(me);
+    if (!leg) {
+        naRuntimeError(c, "leg.courseAndDistanceFrom called on non-flightplan-leg object");
+    }
+    
+    SGGeod pos;
+    geodFromArgs(args, 0, argc, pos);
+    
+    double courseDeg;
+    double distanceM;
+    boost::tie(courseDeg, distanceM) = leg->waypoint()->courseAndDistanceFrom(pos);
+    
+    naRef result = naNewVector(c);
+    naVec_append(result, naNum(courseDeg));
+    naVec_append(result, naNum(distanceM * SG_METER_TO_NM));
+    return result;
+}
+
 static naRef f_waypoint_navaid(naContext c, naRef me, int argc, naRef* args)
 {
   flightgear::Waypt* w = wayptGhost(me);
@@ -2077,6 +2409,7 @@ static struct { const char* name; naCFunction func; } funcs[] = {
   { "findNavaidsByID", f_findNavaidsByIdent },
   { "findFixesByID", f_findFixesByIdent },
   { "flightplan", f_route },
+  { "registerFlightPlanDelegate", f_registerFPDelegate },
   { "createWP", f_createWP },
   { "createWPFrom", f_createWPFrom },
   { "airwaysRoute", f_airwaySearch },
@@ -2095,6 +2428,9 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
     hashset(c, gcSave, "airportProto", airportPrototype);
   
     hashset(c, airportPrototype, "runway", naNewFunc(c, naNewCCode(c, f_airport_runway)));
+    hashset(c, airportPrototype, "runwaysWithoutReciprocals", naNewFunc(c, naNewCCode(c, f_airport_runwaysWithoutReciprocals)));
+    hashset(c, airportPrototype, "helipad", naNewFunc(c, naNewCCode(c, f_airport_runway)));
+    hashset(c, airportPrototype, "taxiway", naNewFunc(c, naNewCCode(c, f_airport_taxiway)));
     hashset(c, airportPrototype, "tower", naNewFunc(c, naNewCCode(c, f_airport_tower)));
     hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms)));
     hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids)));
@@ -2104,6 +2440,7 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
     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)));
+    hashset(c, airportPrototype, "tostring", naNewFunc(c, naNewCCode(c, f_airport_toString)));
   
     flightplanPrototype = naNewHash(c);
     hashset(c, gcSave, "flightplanProto", flightplanPrototype);
@@ -2120,7 +2457,8 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
     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))); 
-  
+    hashset(c, flightplanPrototype, "pathGeod", naNewFunc(c, naNewCCode(c, f_flightplan_pathGeod)));
+    
     waypointPrototype = naNewHash(c);
     hashset(c, gcSave, "wayptProto", waypointPrototype);
     
@@ -2137,6 +2475,8 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
     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)));
+    hashset(c, fpLegPrototype, "path", naNewFunc(c, naNewCCode(c, f_leg_path)));
+    hashset(c, fpLegPrototype, "courseAndDistanceFrom", naNewFunc(c, naNewCCode(c, f_leg_courseAndDistanceFrom)));
   
     for(int i=0; funcs[i].name; i++) {
       hashset(c, globals, funcs[i].name,