]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/airport.cxx
commradio: improvements for atis speech
[flightgear.git] / src / Airports / airport.cxx
index 2f8dc11976d14c533128a3a51c62e2cd0350ec4e..750ccac8221613bfc756e41eeb2042ca9845635a 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "airport.hxx"
 
+#include <algorithm>
 #include <cassert>
 #include <boost/foreach.hpp>
 
 #include <Navaids/waypoint.hxx>
 #include <ATC/CommStation.hxx>
 #include <Navaids/NavDataCache.hxx>
+#include <Navaids/navrecord.hxx>
 
 using std::vector;
 using std::pair;
 
 using namespace flightgear;
 
-
 /***************************************************************************
  * FGAirport
  ***************************************************************************/
 
 AirportCache FGAirport::airportCache;
 
-FGAirport::FGAirport(PositionedID aGuid, const string &id, const SGGeod& location,
-        const string &name, bool has_metar, Type aType) :
+FGAirport::FGAirport( PositionedID aGuid,
+                      const std::string &id,
+                      const SGGeod& location,
+                      const std::string &name,
+                      bool has_metar,
+                      Type aType ):
     FGPositioned(aGuid, aType, id, location),
     _name(name),
     _has_metar(has_metar),
     _dynamics(0),
     mTowerDataLoaded(false),
     mRunwaysLoaded(false),
+    mHelipadsLoaded(false),
     mTaxiwaysLoaded(false),
     mProceduresLoaded(false),
+    mThresholdDataLoaded(false),
     mILSDataLoaded(false)
 {
 }
@@ -125,56 +132,104 @@ FGAirportDynamics * FGAirport::getDynamics()
     return _dynamics;
 }
 
+//------------------------------------------------------------------------------
 unsigned int FGAirport::numRunways() const
 {
   loadRunways();
   return mRunways.size();
 }
 
+//------------------------------------------------------------------------------
 unsigned int FGAirport::numHelipads() const
 {
   loadHelipads();
   return mHelipads.size();
 }
 
-FGRunway* FGAirport::getRunwayByIndex(unsigned int aIndex) const
+//------------------------------------------------------------------------------
+FGRunwayRef FGAirport::getRunwayByIndex(unsigned int aIndex) const
 {
   loadRunways();
-  
-  assert(aIndex >= 0 && aIndex < mRunways.size());
-  return (FGRunway*) flightgear::NavDataCache::instance()->loadById(mRunways[aIndex]);
+  return mRunways.at(aIndex);
 }
 
-FGHelipad* FGAirport::getHelipadByIndex(unsigned int aIndex) const
+//------------------------------------------------------------------------------
+FGHelipadRef FGAirport::getHelipadByIndex(unsigned int aIndex) const
 {
   loadHelipads();
+  return loadById<FGHelipad>(mHelipads, aIndex);
+}
+
+//------------------------------------------------------------------------------
+FGRunwayMap FGAirport::getRunwayMap() const
+{
+  loadRunways();
+  FGRunwayMap map;
 
-  assert(aIndex >= 0 && aIndex < mHelipads.size());
-  return (FGHelipad*) flightgear::NavDataCache::instance()->loadById(mHelipads[aIndex]);
+  double minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft");
+
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways)
+  {
+    // ignore unusably short runways
+    // TODO other methods don't check this...
+    if( rwy->lengthFt() >= minLengthFt )
+      map[ rwy->ident() ] = rwy;
+  }
+
+  return map;
 }
 
-bool FGAirport::hasRunwayWithIdent(const string& aIdent) const
+//------------------------------------------------------------------------------
+FGHelipadMap FGAirport::getHelipadMap() const
 {
-  return flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent) != 0;
+  loadHelipads();
+  FGHelipadMap map;
+
+  BOOST_FOREACH(PositionedID id, mHelipads)
+  {
+    FGHelipad* rwy = loadById<FGHelipad>(id);
+    map[ rwy->ident() ] = rwy;
+  }
+
+  return map;
 }
 
-bool FGAirport::hasHelipadWithIdent(const string& aIdent) const
+//------------------------------------------------------------------------------
+bool FGAirport::hasRunwayWithIdent(const std::string& aIdent) const
 {
-  return flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::HELIPAD, aIdent) != 0;
+  loadRunways();
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
+    if (rwy->ident() == aIdent) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
-FGRunway* FGAirport::getRunwayByIdent(const string& aIdent) const
+//------------------------------------------------------------------------------
+bool FGAirport::hasHelipadWithIdent(const std::string& aIdent) const
 {
-  PositionedID id = flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent);  
-  if (id == 0) {
-    SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident());
-    throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
+  return flightgear::NavDataCache::instance()
+    ->airportItemWithIdent(guid(), FGPositioned::HELIPAD, aIdent) != 0;
+}
+
+//------------------------------------------------------------------------------
+FGRunwayRef FGAirport::getRunwayByIdent(const std::string& aIdent) const
+{
+  loadRunways();
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
+    if (rwy->ident() == aIdent) {
+      return rwy;
+    }
   }
   
-  return (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
+  SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident());
+  throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
 }
 
-FGHelipad* FGAirport::getHelipadByIdent(const string& aIdent) const
+//------------------------------------------------------------------------------
+FGHelipadRef FGAirport::getHelipadByIdent(const std::string& aIdent) const
 {
   PositionedID id = flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::HELIPAD, aIdent);
   if (id == 0) {
@@ -182,29 +237,34 @@ FGHelipad* FGAirport::getHelipadByIdent(const string& aIdent) const
     throw sg_range_exception("unknown helipad " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
   }
 
-  return (FGHelipad*) flightgear::NavDataCache::instance()->loadById(id);
+  return loadById<FGHelipad>(id);
 }
 
-
-FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
+//------------------------------------------------------------------------------
+FGRunwayRef FGAirport::findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams * parms ) const
 {
   loadRunways();
   
   FGRunway* result = NULL;
   double currentBestQuality = 0.0;
   
-  SGPropertyNode *param = fgGetNode("/sim/airport/runways/search", true);
-  double lengthWeight = param->getDoubleValue("length-weight", 0.01);
-  double widthWeight = param->getDoubleValue("width-weight", 0.01);
-  double surfaceWeight = param->getDoubleValue("surface-weight", 10);
-  double deviationWeight = param->getDoubleValue("deviation-weight", 1);
+  struct FindBestRunwayForHeadingParams fbrfhp;
+  if( NULL != parms ) fbrfhp = *parms;
+
+  SGPropertyNode_ptr searchNode = fgGetNode("/sim/airport/runways/search");
+  if( searchNode.valid() ) {
+    fbrfhp.lengthWeight = searchNode->getDoubleValue("length-weight", fbrfhp.lengthWeight );
+    fbrfhp.widthWeight = searchNode->getDoubleValue("width-weight", fbrfhp.widthWeight );
+    fbrfhp.surfaceWeight = searchNode->getDoubleValue("surface-weight", fbrfhp.surfaceWeight );
+    fbrfhp.deviationWeight = searchNode->getDoubleValue("deviation-weight", fbrfhp.deviationWeight );
+    fbrfhp.ilsWeight = searchNode->getDoubleValue("ils-weight", fbrfhp.ilsWeight );
+  }
     
-  BOOST_FOREACH(PositionedID id, mRunways) {
-    FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
-    double good = rwy->score(lengthWeight, widthWeight, surfaceWeight);
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
+    double good = rwy->score( fbrfhp.lengthWeight,  fbrfhp.widthWeight,  fbrfhp.surfaceWeight,  fbrfhp.ilsWeight );
     double dev = aHeading - rwy->headingDeg();
     SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
-    double bad = fabs(deviationWeight * dev) + 1e-20;
+    double bad = fabs( fbrfhp.deviationWeight * dev) + 1e-20;
     double quality = good / bad;
     
     if (quality > currentBestQuality) {
@@ -216,16 +276,15 @@ FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
   return result;
 }
 
-FGRunway* FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
+//------------------------------------------------------------------------------
+FGRunwayRef FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
 {
   loadRunways();
   
   FGRunway* result = NULL;
   double currentLowestDev = 180.0;
   
-  BOOST_FOREACH(PositionedID id, mRunways) {
-    FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
-
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
     double inboundCourse = SGGeodesy::courseDeg(aPos, rwy->end());
     double dev = inboundCourse - rwy->headingDeg();
     SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
@@ -241,17 +300,12 @@ FGRunway* FGAirport::findBestRunwayForPos(const SGGeod& aPos) const
 
 }
 
+//------------------------------------------------------------------------------
 bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
 {
   loadRunways();
   
-  BOOST_FOREACH(PositionedID id, mRunways) {
-    FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
-
-    if (rwy->isReciprocal()) {
-      continue; // we only care about lengths, so don't do work twice
-    }
-
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
     if (rwy->isHardSurface() && (rwy->lengthFt() >= aLengthFt)) {
       return true; // we're done!
     }
@@ -260,34 +314,72 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
   return false;
 }
 
+//------------------------------------------------------------------------------
+FGRunwayList FGAirport::getRunwaysWithoutReciprocals() const
+{
+  loadRunways();
+  
+  FGRunwayList r;
+  
+  BOOST_FOREACH(FGRunwayRef rwy, mRunways) {
+    FGRunway* recip = rwy->reciprocalRunway();
+    if (recip) {
+      FGRunwayList::iterator it = std::find(r.begin(), r.end(), recip);
+      if (it != r.end()) {
+        continue; // reciprocal already in result set, don't include us
+      }
+    }
+    
+    r.push_back(rwy);
+  }
+  
+  return r;
+}
+
+//------------------------------------------------------------------------------
 unsigned int FGAirport::numTaxiways() const
 {
   loadTaxiways();
   return mTaxiways.size();
 }
 
-FGTaxiway* FGAirport::getTaxiwayByIndex(unsigned int aIndex) const
+//------------------------------------------------------------------------------
+FGTaxiwayRef FGAirport::getTaxiwayByIndex(unsigned int aIndex) const
 {
   loadTaxiways();
-  
-  assert(aIndex >= 0 && aIndex < mTaxiways.size());
-  return (FGTaxiway*) flightgear::NavDataCache::instance()->loadById(mTaxiways[aIndex]);
+  return loadById<FGTaxiway>(mTaxiways, aIndex);
 }
 
+//------------------------------------------------------------------------------
+FGTaxiwayList FGAirport::getTaxiways() const
+{
+  loadTaxiways();
+  return loadAllById<FGTaxiway>(mTaxiways);
+}
+
+//------------------------------------------------------------------------------
 unsigned int FGAirport::numPavements() const
 {
   loadTaxiways();
   return mPavements.size();
 }
 
-FGPavement* FGAirport::getPavementByIndex(unsigned int aIndex) const
+//------------------------------------------------------------------------------
+FGPavementRef FGAirport::getPavementByIndex(unsigned int aIndex) const
 {
   loadTaxiways();
-  assert(aIndex >= 0 && aIndex < mPavements.size());
-  return (FGPavement*) flightgear::NavDataCache::instance()->loadById(mPavements[aIndex]);
+  return loadById<FGPavement>(mPavements, aIndex);
 }
 
-FGRunway* FGAirport::getActiveRunwayForUsage() const
+//------------------------------------------------------------------------------
+FGPavementList FGAirport::getPavements() const
+{
+  loadTaxiways();
+  return loadAllById<FGPavement>(mPavements);
+}
+
+//------------------------------------------------------------------------------
+FGRunwayRef FGAirport::getActiveRunwayForUsage() const
 {
   FGEnvironmentMgr* envMgr = (FGEnvironmentMgr *) globals->get_subsystem("environment");
   
@@ -296,7 +388,7 @@ FGRunway* FGAirport::getActiveRunwayForUsage() const
   double hdg = 270;
   
   if (envMgr) {
-    FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
+    FGEnvironment stationWeather(envMgr->getEnvironment(geod()));
   
     double windSpeed = stationWeather.get_wind_speed_kt();
     if (windSpeed > 0.0) {
@@ -307,19 +399,19 @@ FGRunway* FGAirport::getActiveRunwayForUsage() const
   return findBestRunwayForHeading(hdg);
 }
 
-FGAirport* FGAirport::findClosest(const SGGeod& aPos, double aCuttofNm, Filter* filter)
+//------------------------------------------------------------------------------
+FGAirportRef FGAirport::findClosest( const SGGeod& aPos,
+                                     double aCuttofNm,
+                                     Filter* filter )
 {
   AirportFilter aptFilter;
-  if (filter == NULL) {
+  if( !filter )
     filter = &aptFilter;
-  }
   
-  FGPositionedRef r = FGPositioned::findClosest(aPos, aCuttofNm, filter);
-  if (!r) {
-    return NULL;
-  }
-  
-  return static_cast<FGAirport*>(r.ptr());
+  return static_pointer_cast<FGAirport>
+  (
+    FGPositioned::findClosest(aPos, aCuttofNm, filter)
+  );
 }
 
 FGAirport::HardSurfaceFilter::HardSurfaceFilter(double minLengthFt) :
@@ -329,20 +421,55 @@ FGAirport::HardSurfaceFilter::HardSurfaceFilter(double minLengthFt) :
     mMinLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0);
   }
 }
-      
+
 bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
 {
   return aApt->hasHardRunwayOfLengthFt(mMinLengthFt);
 }
 
-FGAirport* FGAirport::findByIdent(const std::string& aIdent)
+//------------------------------------------------------------------------------
+FGAirport::TypeRunwayFilter::TypeRunwayFilter():
+  _type(FGPositioned::AIRPORT),
+  _min_runway_length_ft( fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0) )
+{
+
+}
+
+//------------------------------------------------------------------------------
+bool FGAirport::TypeRunwayFilter::fromTypeString(const std::string& type)
+{
+  if(      type == "heliport" ) _type = FGPositioned::HELIPORT;
+  else if( type == "seaport"  ) _type = FGPositioned::SEAPORT;
+  else if( type == "airport"  ) _type = FGPositioned::AIRPORT;
+  else                          return false;
+
+  return true;
+}
+
+//------------------------------------------------------------------------------
+bool FGAirport::TypeRunwayFilter::pass(FGPositioned* pos) const
+{
+  FGAirport* apt = static_cast<FGAirport*>(pos);
+  if(  (apt->type() == FGPositioned::AIRPORT)
+    && !apt->hasHardRunwayOfLengthFt(_min_runway_length_ft)
+    )
+    return false;
+
+  return true;
+}
+
+//------------------------------------------------------------------------------
+FGAirportRef FGAirport::findByIdent(const std::string& aIdent)
 {
   AirportCache::iterator it = airportCache.find(aIdent);
   if (it != airportCache.end())
    return it->second;
 
   PortsFilter filter;
-  FGAirport* r = static_cast<FGAirport*> (FGPositioned::findFirstWithIdent(aIdent, &filter).get());
+  FGAirportRef r = static_pointer_cast<FGAirport>
+  (
+    FGPositioned::findFirstWithIdent(aIdent, &filter)
+  );
 
   // add airport to the cache (even when it's NULL, so we don't need to search in vain again)
   airportCache[aIdent] = r;
@@ -351,9 +478,10 @@ FGAirport* FGAirport::findByIdent(const std::string& aIdent)
   return r;
 }
 
-FGAirport* FGAirport::getByIdent(const std::string& aIdent)
+//------------------------------------------------------------------------------
+FGAirportRef FGAirport::getByIdent(const std::string& aIdent)
 {
-  FGAirport* r = findByIdent(aIdent);
+  FGAirportRef r = findByIdent(aIdent);
   if (!r)
     throw sg_range_exception("No such airport with ident: " + aIdent);
   return r;
@@ -365,7 +493,7 @@ char** FGAirport::searchNamesAndIdents(const std::string& aFilter)
 }
 
 // find basic airport location info from airport database
-const FGAirport *fgFindAirportID( const string& id)
+const FGAirport *fgFindAirportID( const std::string& id)
 {
     if ( id.empty() ) {
         return NULL;
@@ -374,6 +502,12 @@ const FGAirport *fgFindAirportID( const string& id)
     return FGAirport::findByIdent(id);
 }
 
+PositionedIDVec FGAirport::itemsOfType(FGPositioned::Type ty) const
+{
+  flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+  return cache->airportItemsOfType(guid(), ty);
+}
+
 void FGAirport::loadRunways() const
 {
   if (mRunwaysLoaded) {
@@ -383,7 +517,10 @@ void FGAirport::loadRunways() const
   loadSceneryDefinitions();
   
   mRunwaysLoaded = true;
-  mRunways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::RUNWAY);
+  PositionedIDVec rwys(itemsOfType(FGPositioned::RUNWAY));
+  BOOST_FOREACH(PositionedID id, rwys) {
+    mRunways.push_back(loadById<FGRunway>(id));
+  }
 }
 
 void FGAirport::loadHelipads() const
@@ -392,10 +529,8 @@ void FGAirport::loadHelipads() const
     return; // already loaded, great
   }
 
-  loadSceneryDefinitions();
-
   mHelipadsLoaded = true;
-  mHelipads = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::HELIPAD);
+  mHelipads = itemsOfType(FGPositioned::HELIPAD);
 }
 
 void FGAirport::loadTaxiways() const
@@ -405,7 +540,7 @@ void FGAirport::loadTaxiways() const
   }
   
   mTaxiwaysLoaded =  true;
-  mTaxiways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::TAXIWAY);
+  mTaxiways = itemsOfType(FGPositioned::TAXIWAY);
 }
 
 void FGAirport::loadProcedures() const
@@ -427,23 +562,24 @@ void FGAirport::loadProcedures() const
 
 void FGAirport::loadSceneryDefinitions() const
 {
-  NavDataCache* cache = NavDataCache::instance();
+  if (mThresholdDataLoaded) {
+    return;
+  }
+  
+  mThresholdDataLoaded = true;
+  
   SGPath path;
   if (!XMLLoader::findAirportData(ident(), "threshold", path)) {
     return; // no XML threshold data
   }
   
-  if (!cache->isCachedFileModified(path)) {
-    // cached values are correct, we're all done
-    return;
-  }
-  
-    flightgear::NavDataCache::Transaction txn(cache);
+  try {
     SGPropertyNode_ptr rootNode = new SGPropertyNode;
     readProperties(path.str(), rootNode);
     const_cast<FGAirport*>(this)->readThresholdData(rootNode);
-    cache->stampCacheFile(path);
-    txn.commit();
+  } catch (sg_exception& e) {
+    SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading threshold XML failed:" << e.getFormattedMessage());
+  }
 }
 
 void FGAirport::readThresholdData(SGPropertyNode* aRoot)
@@ -464,40 +600,47 @@ void FGAirport::readThresholdData(SGPropertyNode* aRoot)
 void FGAirport::processThreshold(SGPropertyNode* aThreshold)
 {
   // first, let's identify the current runway
-  string rwyIdent(aThreshold->getStringValue("rwy"));
+  std::string rwyIdent(aThreshold->getStringValue("rwy"));
   NavDataCache* cache = NavDataCache::instance(); 
   PositionedID id = cache->airportItemWithIdent(guid(), FGPositioned::RUNWAY, rwyIdent);
-  if (id == 0) {
-    SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: "
-           "found runway not defined in the global data:" << ident() << "/" << rwyIdent);
-    return;
-  }
   
   double lon = aThreshold->getDoubleValue("lon"),
   lat = aThreshold->getDoubleValue("lat");
-  SGGeod newThreshold(SGGeod::fromDegM(lon, lat, mPosition.getElevationM()));
+  SGGeod newThreshold(SGGeod::fromDegM(lon, lat, elevationM()));
   
   double newHeading = aThreshold->getDoubleValue("hdg-deg");
   double newDisplacedThreshold = aThreshold->getDoubleValue("displ-m");
   double newStopway = aThreshold->getDoubleValue("stopw-m");
   
-  cache->updateRunwayThreshold(id, newThreshold,
-                               newHeading, newDisplacedThreshold, newStopway);
+  if (id == 0) {
+    SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: "
+           "found runway not defined in the global data:" << ident() << "/" << rwyIdent);
+    // enable this code when threshold.xml contains sufficient data to
+    // fully specify a new runway, *and* we figure out how to assign runtime
+    // Positioned IDs and insert temporary items into the spatial map.
+#if 0
+    double newLength = 0.0, newWidth = 0.0;
+    int surfaceCode = 0;
+    FGRunway* rwy = new FGRunway(id, guid(), rwyIdent, newThreshold,
+                       newHeading,
+                       newLength, newWidth,
+                       newDisplacedThreshold, newStopway,
+                       surfaceCode);
+    // insert into the spatial map too
+    mRunways.push_back(rwy);
+#endif
+  } else {
+    FGRunway* rwy = loadById<FGRunway>(id);
+    rwy->updateThreshold(newThreshold, newHeading,
+                         newDisplacedThreshold, newStopway);
+
+  }
 }
 
 SGGeod FGAirport::getTowerLocation() const
 {
   validateTowerData();
-  
-  NavDataCache* cache = NavDataCache::instance();
-  PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
-  if (towers.empty()) {
-    SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <<ident());
-    return SGGeod();
-  }
-  
-  FGPositionedRef tower = cache->loadById(towers.front());
-  return tower->geod();
+  return mTowerPosition;
 }
 
 void FGAirport::validateTowerData() const
@@ -505,25 +648,34 @@ void FGAirport::validateTowerData() const
   if (mTowerDataLoaded) {
     return;
   }
-
+  
   mTowerDataLoaded = true;
+
+// first, load data from the cache (apt.dat)
   NavDataCache* cache = NavDataCache::instance();
+  PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
+  if (towers.empty()) {
+    SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <<ident());
+    mTowerPosition = geod(); // use airport position
+    // increase tower elevation by 20 metres above the field elevation
+    mTowerPosition.setElevationM(geod().getElevationM() + 20.0);
+  } else {
+    FGPositionedRef tower = cache->loadById(towers.front());
+    mTowerPosition = tower->geod();
+  }
+  
   SGPath path;
   if (!XMLLoader::findAirportData(ident(), "twr", path)) {
-    return; // no XML tower data
+    return; // no XML tower data, base position is fine
   }
   
-  if (!cache->isCachedFileModified(path)) {
-  // cached values are correct, we're all done
-    return;
+  try {
+    SGPropertyNode_ptr rootNode = new SGPropertyNode;
+    readProperties(path.str(), rootNode);
+    const_cast<FGAirport*>(this)->readTowerData(rootNode);
+  } catch (sg_exception& e){
+    SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading twr XML failed:" << e.getFormattedMessage());
   }
-   
-  flightgear::NavDataCache::Transaction txn(cache);
-  SGPropertyNode_ptr rootNode = new SGPropertyNode;
-  readProperties(path.str(), rootNode);
-  const_cast<FGAirport*>(this)->readTowerData(rootNode);
-  cache->stampCacheFile(path);
-  txn.commit();
 }
 
 void FGAirport::readTowerData(SGPropertyNode* aRoot)
@@ -536,52 +688,36 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot)
 // scenery for a precise terrain elevation, we use the field elevation
 // (this is also what the apt.dat code does)
   double fieldElevationM = geod().getElevationM();
-  SGGeod towerLocation(SGGeod::fromDegM(lon, lat, fieldElevationM + elevM));
-  
-  NavDataCache* cache = NavDataCache::instance();
-  PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER);
-  if (towers.empty()) {
-    cache->insertTower(guid(), towerLocation);
-  } else {
-    // update the position
-    cache->updatePosition(towers.front(), towerLocation);
-  }
+  mTowerPosition = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM);
 }
 
-bool FGAirport::validateILSData()
+void FGAirport::validateILSData()
 {
   if (mILSDataLoaded) {
-    return false;
+    return;
   }
   
+  // to avoid re-entrancy on this code-path, ensure we set loaded
+  // immediately.
   mILSDataLoaded = true;
-  NavDataCache* cache = NavDataCache::instance();
+    
   SGPath path;
   if (!XMLLoader::findAirportData(ident(), "ils", path)) {
-    return false; // no XML tower data
+    return; // no XML tower data
   }
   
-  if (!cache->isCachedFileModified(path)) {
-    // cached values are correct, we're all done
-    return false;
+  try {
+      SGPropertyNode_ptr rootNode = new SGPropertyNode;
+      readProperties(path.str(), rootNode);
+      readILSData(rootNode);
+  } catch (sg_exception& e){
+      SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading ils XML failed:" << e.getFormattedMessage());
   }
-  
-  SGPropertyNode_ptr rootNode = new SGPropertyNode;
-  readProperties(path.str(), rootNode);
-
-  flightgear::NavDataCache::Transaction txn(cache);
-  readILSData(rootNode);
-  cache->stampCacheFile(path);
-  txn.commit();
-    
-// we loaded data, tell the caller it might need to reload things
-  return true;
 }
 
 void FGAirport::readILSData(SGPropertyNode* aRoot)
-{
+{  
   NavDataCache* cache = NavDataCache::instance();
-  
   // find the entry matching the runway
   SGPropertyNode* runwayNode, *ilsNode;
   for (int i=0; (runwayNode = aRoot->getChild("runway", i)) != NULL; ++i) {
@@ -593,7 +729,7 @@ void FGAirport::readILSData(SGPropertyNode* aRoot)
                                         ilsNode->getStringValue("nav-id"));
       if (ils == 0) {
         SG_LOG(SG_GENERAL, SG_INFO, "reading ILS data for " << ident() <<
-               ", couldn;t find runway/navaid for:" <<
+               ", couldn't find runway/navaid for:" <<
                ilsNode->getStringValue("rwy") << "/" <<
                ilsNode->getStringValue("nav-id"));
         continue;
@@ -604,7 +740,9 @@ void FGAirport::readILSData(SGPropertyNode* aRoot)
         lat = ilsNode->getDoubleValue("lat"),
         elevM = ilsNode->getDoubleValue("elev-m");
  
-      cache->updateILS(ils, SGGeod::fromDegM(lon, lat, elevM), hdgDeg);
+      FGNavRecordRef nav(FGPositioned::loadById<FGNavRecord>(ils));
+      assert(nav.valid());
+      nav->updateFromXML(SGGeod::fromDegM(lon, lat, elevM), hdgDeg);
     } // of ILS iteration
   } // of runway iteration
 }
@@ -624,18 +762,21 @@ void FGAirport::addApproach(Approach* aApp)
   mApproaches.push_back(aApp);
 }
 
+//------------------------------------------------------------------------------
 unsigned int FGAirport::numSIDs() const
 {
   loadProcedures();
   return mSIDs.size();
 }
 
+//------------------------------------------------------------------------------
 flightgear::SID* FGAirport::getSIDByIndex(unsigned int aIndex) const
 {
   loadProcedures();
   return mSIDs[aIndex];
 }
 
+//------------------------------------------------------------------------------
 flightgear::SID* FGAirport::findSIDWithIdent(const std::string& aIdent) const
 {
   loadProcedures();
@@ -648,18 +789,28 @@ flightgear::SID* FGAirport::findSIDWithIdent(const std::string& aIdent) const
   return NULL;
 }
 
+//------------------------------------------------------------------------------
+flightgear::SIDList FGAirport::getSIDs() const
+{
+  loadProcedures();
+  return flightgear::SIDList(mSIDs.begin(), mSIDs.end());
+}
+
+//------------------------------------------------------------------------------
 unsigned int FGAirport::numSTARs() const
 {
   loadProcedures();
   return mSTARs.size();
 }
 
+//------------------------------------------------------------------------------
 STAR* FGAirport::getSTARByIndex(unsigned int aIndex) const
 {
   loadProcedures();
   return mSTARs[aIndex];
 }
 
+//------------------------------------------------------------------------------
 STAR* FGAirport::findSTARWithIdent(const std::string& aIdent) const
 {
   loadProcedures();
@@ -672,18 +823,27 @@ STAR* FGAirport::findSTARWithIdent(const std::string& aIdent) const
   return NULL;
 }
 
+//------------------------------------------------------------------------------
+STARList FGAirport::getSTARs() const
+{
+  loadProcedures();
+  return STARList(mSTARs.begin(), mSTARs.end());
+}
+
 unsigned int FGAirport::numApproaches() const
 {
   loadProcedures();
   return mApproaches.size();
 }
 
+//------------------------------------------------------------------------------
 Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const
 {
   loadProcedures();
   return mApproaches[aIndex];
 }
 
+//------------------------------------------------------------------------------
 Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const
 {
   loadProcedures();
@@ -696,6 +856,22 @@ Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const
   return NULL;
 }
 
+//------------------------------------------------------------------------------
+ApproachList FGAirport::getApproaches(ProcedureType type) const
+{
+  loadProcedures();
+  if( type == PROCEDURE_INVALID )
+    return ApproachList(mApproaches.begin(), mApproaches.end());
+
+  ApproachList ret;
+  for(size_t i = 0; i < mApproaches.size(); ++i)
+  {
+    if( mApproaches[i]->type() == type )
+      ret.push_back(mApproaches[i]);
+  }
+  return ret;
+}
+
 CommStationList
 FGAirport::commStations() const
 {
@@ -705,7 +881,7 @@ FGAirport::commStations() const
                                                             FGPositioned::FREQ_GROUND,
                                                             FGPositioned::FREQ_UNICOM))
   {
-    result.push_back((CommStation*) cache->loadById(pos));
+    result.push_back( loadById<CommStation>(pos) );
   }
   
   return result;
@@ -717,14 +893,54 @@ FGAirport::commStationsOfType(FGPositioned::Type aTy) const
   NavDataCache* cache = NavDataCache::instance();
   CommStationList result;
   BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(), aTy)) {
-    result.push_back((CommStation*) cache->loadById(pos));
+    result.push_back( loadById<CommStation>(pos) );
   }
   
   return result;
 }
 
+class AirportWithSize
+{
+public:
+    AirportWithSize(FGPositionedRef pos) :
+        _pos(pos),
+        _sizeMetric(0)
+    {
+        assert(pos->type() == FGPositioned::AIRPORT);
+        FGAirport* apt = static_cast<FGAirport*>(pos.get());
+        BOOST_FOREACH(FGRunway* rwy, apt->getRunwaysWithoutReciprocals()) {
+            _sizeMetric += static_cast<int>(rwy->lengthFt());
+        }
+    }
+    
+    bool operator<(const AirportWithSize& other) const
+    {
+        return _sizeMetric < other._sizeMetric;
+    }
+    
+    FGPositionedRef pos() const
+    { return _pos; }
+private:
+    FGPositionedRef _pos;
+    unsigned int _sizeMetric;
+    
+};
+
+void FGAirport::sortBySize(FGPositionedList& airportList)
+{
+    std::vector<AirportWithSize> annotated;
+    BOOST_FOREACH(FGPositionedRef p, airportList) {
+        annotated.push_back(AirportWithSize(p));
+    }
+    std::sort(annotated.begin(), annotated.end());
+    
+    for (unsigned int i=0; i<annotated.size(); ++i) {
+        airportList[i] = annotated[i].pos();
+    }
+}
+
 // get airport elevation
-double fgGetAirportElev( const string& id )
+double fgGetAirportElev( const std::string& id )
 {
     const FGAirport *a=fgFindAirportID( id);
     if (a) {
@@ -736,7 +952,7 @@ double fgGetAirportElev( const string& id )
 
 
 // get airport position
-SGGeod fgGetAirportPos( const string& id )
+SGGeod fgGetAirportPos( const std::string& id )
 {
     const FGAirport *a = fgFindAirportID( id);