]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/simple.cxx
Overhaul the ground-net / parking code.
[flightgear.git] / src / Airports / simple.cxx
index a418bd4144f1a81472f67e3d005953693949c4ae..84ed7ffabb39c561e49f0ab0782ca0ebe9a12d0f 100644 (file)
 #include "simple.hxx"
 
 #include <cassert>
+#include <boost/foreach.hpp>
 
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
 #include <simgear/debug/logstream.hxx>
 #include <simgear/sg_inlines.h>
+#include <simgear/structure/exception.hxx>
 
 #include <Environment/environment_mgr.hxx>
 #include <Environment/environment.hxx>
 #include <Airports/pavement.hxx>
 #include <Airports/dynamics.hxx>
 #include <Airports/xmlloader.hxx>
+#include <Navaids/procedure.hxx>
+#include <Navaids/waypoint.hxx>
+#include <ATC/CommStation.hxx>
+#include <Navaids/NavDataCache.hxx>
+
+using std::vector;
+using std::pair;
+
+using namespace flightgear;
 
-// magic import of a helper which uses FGPositioned internals
-extern char** searchAirportNamesAndIdents(const std::string& aFilter);
 
 /***************************************************************************
  * FGAirport
  ***************************************************************************/
 
-FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tower_location,
+FGAirport::FGAirport(PositionedID aGuid, const string &id, const SGGeod& location,
         const string &name, bool has_metar, Type aType) :
-    FGPositioned(aType, id, location),
-    _tower_location(tower_location),
+    FGPositioned(aGuid, aType, id, location),
     _name(name),
     _has_metar(has_metar),
     _dynamics(0),
+    mTowerDataLoaded(false),
     mRunwaysLoaded(false),
-    mTaxiwaysLoaded(true)
+    mTaxiwaysLoaded(false)
 {
-  init(true); // init FGPositioned
 }
 
 
@@ -87,22 +95,29 @@ bool FGAirport::isHeliport() const
   return type() == HELIPORT;
 }
 
+bool FGAirport::isAirportType(FGPositioned* pos)
+{
+    if (!pos) {
+        return false;
+    }
+    
+    return (pos->type() >= AIRPORT) && (pos->type() <= SEAPORT);
+}
+
 FGAirportDynamics * FGAirport::getDynamics()
 {
-    if (_dynamics != 0) {
+    if (_dynamics) {
         return _dynamics;
-    } else {
-        //cerr << "Trying to load dynamics for " << _id << endl;
-        _dynamics = new FGAirportDynamics(this);
-        XMLLoader::load(_dynamics);
-
-        FGRunwayPreference rwyPrefs(this);
-        XMLLoader::load(&rwyPrefs);
-        _dynamics->setRwyUse(rwyPrefs);
-
-        //FGSidStar SIDs(this);
-        XMLLoader::load(_dynamics->getSIDs());
-   }
+    }
+    
+    _dynamics = new FGAirportDynamics(this);
+    XMLLoader::load(_dynamics);
+    _dynamics->init();
+  
+    FGRunwayPreference rwyPrefs(this);
+    XMLLoader::load(&rwyPrefs);
+    _dynamics->setRwyUse(rwyPrefs);
+    
     return _dynamics;
 }
 
@@ -117,50 +132,30 @@ FGRunway* FGAirport::getRunwayByIndex(unsigned int aIndex) const
   loadRunways();
   
   assert(aIndex >= 0 && aIndex < mRunways.size());
-  return mRunways[aIndex];
+  return (FGRunway*) flightgear::NavDataCache::instance()->loadById(mRunways[aIndex]);
 }
 
 bool FGAirport::hasRunwayWithIdent(const string& aIdent) const
 {
-  return (getIteratorForRunwayIdent(aIdent) != mRunways.end());
+  return flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent) != 0;
 }
 
 FGRunway* FGAirport::getRunwayByIdent(const string& aIdent) const
 {
-  Runway_iterator it = getIteratorForRunwayIdent(aIdent);
-  if (it == mRunways.end()) {
+  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 *it;
+  return (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
 }
 
-FGAirport::Runway_iterator
-FGAirport::getIteratorForRunwayIdent(const string& aIdent) const
-{ 
-  loadRunways();
-  
-  string ident(aIdent);
-  if ((aIdent.size() == 1) || !isdigit(aIdent[1])) {
-    ident = "0" + aIdent;
-  }
-
-  Runway_iterator it = mRunways.begin();
-  for (; it != mRunways.end(); ++it) {
-    if ((*it)->ident() == ident) {
-      return it;
-    }
-  }
-
-  return it; // end()
-}
 
 FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
 {
   loadRunways();
   
-  Runway_iterator it = mRunways.begin();
   FGRunway* result = NULL;
   double currentBestQuality = 0.0;
   
@@ -170,30 +165,55 @@ FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
   double surfaceWeight = param->getDoubleValue("surface-weight", 10);
   double deviationWeight = param->getDoubleValue("deviation-weight", 1);
     
-  for (; it != mRunways.end(); ++it) {
-    double good = (*it)->score(lengthWeight, widthWeight, surfaceWeight);
-    
-    double dev = aHeading - (*it)->headingDeg();
+  BOOST_FOREACH(PositionedID id, mRunways) {
+    FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id);
+    double good = rwy->score(lengthWeight, widthWeight, surfaceWeight);
+    double dev = aHeading - rwy->headingDeg();
     SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
     double bad = fabs(deviationWeight * dev) + 1e-20;
     double quality = good / bad;
     
     if (quality > currentBestQuality) {
       currentBestQuality = quality;
-      result = *it;
+      result = rwy;
     }
   }
 
   return result;
 }
 
+FGRunway* 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);
+
+    double inboundCourse = SGGeodesy::courseDeg(aPos, rwy->end());
+    double dev = inboundCourse - rwy->headingDeg();
+    SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
+
+    dev = fabs(dev);
+    if (dev < currentLowestDev) { // new best match
+      currentLowestDev = dev;
+      result = rwy;
+    }
+  } // of runway iteration
+  
+  return result;
+
+}
+
 bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
 {
   loadRunways();
   
-  unsigned int numRunways(mRunways.size());
-  for (unsigned int r=0; r<numRunways; ++r) {
-    FGRunway* rwy = mRunways[r];
+  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
     }
@@ -215,8 +235,9 @@ unsigned int FGAirport::numTaxiways() const
 FGTaxiway* FGAirport::getTaxiwayByIndex(unsigned int aIndex) const
 {
   loadTaxiways();
+  
   assert(aIndex >= 0 && aIndex < mTaxiways.size());
-  return mTaxiways[aIndex];
+  return (FGTaxiway*) flightgear::NavDataCache::instance()->loadById(mTaxiways[aIndex]);
 }
 
 unsigned int FGAirport::numPavements() const
@@ -229,21 +250,7 @@ FGPavement* FGAirport::getPavementByIndex(unsigned int aIndex) const
 {
   loadTaxiways();
   assert(aIndex >= 0 && aIndex < mPavements.size());
-  return mPavements[aIndex];
-}
-
-void FGAirport::setRunwaysAndTaxiways(vector<FGRunwayPtr>& rwys,
-       vector<FGTaxiwayPtr>& txwys,
-       vector<FGPavementPtr>& pvts)
-{
-  mRunways.swap(rwys);
-  Runway_iterator it = mRunways.begin();
-  for (; it != mRunways.end(); ++it) {
-    (*it)->setAirport(this);
-  }
-
-  mTaxiways.swap(txwys);
-  mPavements.swap(pvts);
+  return (FGPavement*) flightgear::NavDataCache::instance()->loadById(mPavements[aIndex]);
 }
 
 FGRunway* FGAirport::getActiveRunwayForUsage() const
@@ -287,6 +294,9 @@ FGAirport* FGAirport::findClosest(const SGGeod& aPos, double aCuttofNm, Filter*
 FGAirport::HardSurfaceFilter::HardSurfaceFilter(double minLengthFt) :
   mMinLengthFt(minLengthFt)
 {
+  if (minLengthFt < 0.0) {
+    mMinLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0);
+  }
 }
       
 bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
@@ -296,9 +306,8 @@ bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
 
 FGAirport* FGAirport::findByIdent(const std::string& aIdent)
 {
-  FGPositionedRef r;
   PortsFilter filter;
-  r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
+  FGPositionedRef r = FGPositioned::findFirstWithIdent(aIdent, &filter);
   if (!r) {
     return NULL; // we don't warn here, let the caller do that
   }
@@ -309,7 +318,7 @@ FGAirport* FGAirport::getByIdent(const std::string& aIdent)
 {
   FGPositionedRef r;
   PortsFilter filter;
-  r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
+  r = FGPositioned::findFirstWithIdent(aIdent, &filter);
   if (!r) {
     throw sg_range_exception("No such airport with ident: " + aIdent);
   }
@@ -318,9 +327,7 @@ FGAirport* FGAirport::getByIdent(const std::string& aIdent)
 
 char** FGAirport::searchNamesAndIdents(const std::string& aFilter)
 {
-  // we delegate all the work to a horrible helper in FGPositioned, which can
-  // access the (private) index data.
-  return searchAirportNamesAndIdents(aFilter);
+  return NavDataCache::instance()->searchAirportNamesAndIdents(aFilter);
 }
 
 // find basic airport location info from airport database
@@ -339,8 +346,10 @@ void FGAirport::loadRunways() const
     return; // already loaded, great
   }
   
+  loadSceneryDefinitions();
+  
   mRunwaysLoaded = true;
-  loadSceneryDefintions();
+  mRunways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::RUNWAY);
 }
 
 void FGAirport::loadTaxiways() const
@@ -348,29 +357,46 @@ void FGAirport::loadTaxiways() const
   if (mTaxiwaysLoaded) {
     return; // already loaded, great
   }
+  
+  mTaxiwaysLoaded =  true;
+  mTaxiways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::TAXIWAY);
 }
 
-void FGAirport::loadSceneryDefintions() const
-{  
-  // allow users to disable the scenery data in the short-term
-  // longer term, this option can probably disappear
-  if (!fgGetBool("/sim/paths/use-custom-scenery-data")) {
-    return; 
+void FGAirport::loadProcedures() const
+{
+  if (mProceduresLoaded) {
+    return;
   }
   
+  mProceduresLoaded = true;
   SGPath path;
-  SGPropertyNode_ptr rootNode = new SGPropertyNode;
-  if (XMLLoader::findAirportData(ident(), "threshold", path)) {
-    readProperties(path.str(), rootNode);
-    const_cast<FGAirport*>(this)->readThresholdData(rootNode);
+  if (!XMLLoader::findAirportData(ident(), "procedures", path)) {
+    SG_LOG(SG_GENERAL, SG_INFO, "no procedures data available for " << ident());
+    return;
+  }
+  
+  SG_LOG(SG_GENERAL, SG_INFO, ident() << ": loading procedures from " << path.str());
+  RouteBase::loadAirportProcedures(path, const_cast<FGAirport*>(this));
+}
+
+void FGAirport::loadSceneryDefinitions() const
+{
+  NavDataCache* cache = NavDataCache::instance();
+  SGPath path;
+  if (!XMLLoader::findAirportData(ident(), "threshold", path)) {
+    return; // no XML threshold data
   }
   
-  // repeat for the tower data
-  rootNode = new SGPropertyNode;
-  if (XMLLoader::findAirportData(ident(), "twr", path)) {
-    readProperties(path.str(), rootNode);
-    const_cast<FGAirport*>(this)->readTowerData(rootNode);
+  if (!cache->isCachedFileModified(path)) {
+    // cached values are correct, we're all done
+    return;
   }
+  
+  SGPropertyNode_ptr rootNode = new SGPropertyNode;
+  readProperties(path.str(), rootNode);
+  const_cast<FGAirport*>(this)->readThresholdData(rootNode);
+  cache->stampCacheFile(path);
+
 }
 
 void FGAirport::readThresholdData(SGPropertyNode* aRoot)
@@ -391,15 +417,64 @@ void FGAirport::readThresholdData(SGPropertyNode* aRoot)
 void FGAirport::processThreshold(SGPropertyNode* aThreshold)
 {
   // first, let's identify the current runway
-  string id(aThreshold->getStringValue("rwy"));
-  if (!hasRunwayWithIdent(id)) {
-    SG_LOG(SG_GENERAL, SG_WARN, "FGAirport::processThreshold: "
-      "found runway not defined in the global data:" << ident() << "/" << id);
+  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()));
+  
+  double newHeading = aThreshold->getDoubleValue("hdg-deg");
+  double newDisplacedThreshold = aThreshold->getDoubleValue("displ-m") * SG_METER_TO_FEET;
+  double newStopway = aThreshold->getDoubleValue("stopw-m") * SG_METER_TO_FEET;
+  
+  cache->updateRunwayThreshold(id, 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();
+}
+
+void FGAirport::validateTowerData() const
+{
+  if (mTowerDataLoaded) {
     return;
   }
+
+  mTowerDataLoaded = true;
+  NavDataCache* cache = NavDataCache::instance();
+  SGPath path;
+  if (!XMLLoader::findAirportData(ident(), "twr", path)) {
+    return; // no XML tower data
+  }
   
-  FGRunway* rwy = getRunwayByIdent(id);
-  rwy->processThreshold(aThreshold);
+  if (!cache->isCachedFileModified(path)) {
+  // cached values are correct, we're all done
+    return;
+  }
+    
+  SGPropertyNode_ptr rootNode = new SGPropertyNode;
+  readProperties(path.str(), rootNode);
+  const_cast<FGAirport*>(this)->readTowerData(rootNode);
+  cache->stampCacheFile(path);
 }
 
 void FGAirport::readTowerData(SGPropertyNode* aRoot)
@@ -407,9 +482,193 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot)
   SGPropertyNode* twrNode = aRoot->getChild("tower")->getChild("twr");
   double lat = twrNode->getDoubleValue("lat"), 
     lon = twrNode->getDoubleValue("lon"), 
-    elevM = twrNode->getDoubleValue("elev-m");
-    
-  _tower_location = SGGeod::fromDegM(lon, lat, elevM);
+    elevM = twrNode->getDoubleValue("elev-m");  
+// tower elevation is AGL, not AMSL. Since we don't want to depend on the
+// 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);
+  }
+}
+
+bool FGAirport::validateILSData()
+{
+  if (mILSDataLoaded) {
+    return false;
+  }
+  
+  mILSDataLoaded = true;
+  NavDataCache* cache = NavDataCache::instance();
+  SGPath path;
+  if (!XMLLoader::findAirportData(ident(), "ils", path)) {
+    return false; // no XML tower data
+  }
+  
+  if (!cache->isCachedFileModified(path)) {
+    // cached values are correct, we're all done
+    return false;
+  }
+  
+  SGPropertyNode_ptr rootNode = new SGPropertyNode;
+  readProperties(path.str(), rootNode);
+  readILSData(rootNode);
+  cache->stampCacheFile(path);
+  
+// 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) {
+    for (int j=0; (ilsNode = runwayNode->getChild("ils", j)) != NULL; ++j) {
+      // must match on both nav-ident and runway ident, to support the following:
+      // - runways with multiple distinct ILS installations (KEWD, for example)
+      // - runways where both ends share the same nav ident (LFAT, for example)
+      PositionedID ils = cache->findILS(guid(), ilsNode->getStringValue("rwy"),
+                                        ilsNode->getStringValue("nav-id"));
+      if (ils == 0) {
+        SG_LOG(SG_GENERAL, SG_INFO, "reading ILS data for " << ident() <<
+               ", couldn;t find runway/navaid for:" <<
+               ilsNode->getStringValue("rwy") << "/" <<
+               ilsNode->getStringValue("nav-id"));
+        continue;
+      }
+      
+      double hdgDeg = ilsNode->getDoubleValue("hdg-deg"),
+        lon = ilsNode->getDoubleValue("lon"),
+        lat = ilsNode->getDoubleValue("lat"),
+        elevM = ilsNode->getDoubleValue("elev-m");
+      cache->updateILS(ils, SGGeod::fromDegM(lon, lat, elevM), hdgDeg);
+    } // of ILS iteration
+  } // of runway iteration
+}
+
+void FGAirport::addSID(flightgear::SID* aSid)
+{
+  mSIDs.push_back(aSid);
+}
+
+void FGAirport::addSTAR(STAR* aStar)
+{
+  mSTARs.push_back(aStar);
+}
+
+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();
+  for (unsigned int i=0; i<mSIDs.size(); ++i) {
+    if (mSIDs[i]->ident() == aIdent) {
+      return mSIDs[i];
+    }
+  }
+  
+  return NULL;
+}
+
+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();
+  for (unsigned int i=0; i<mSTARs.size(); ++i) {
+    if (mSTARs[i]->ident() == aIdent) {
+      return mSTARs[i];
+    }
+  }
+  
+  return NULL;
+}
+
+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();
+  for (unsigned int i=0; i<mApproaches.size(); ++i) {
+    if (mApproaches[i]->ident() == aIdent) {
+      return mApproaches[i];
+    }
+  }
+  
+  return NULL;
+}
+
+CommStationList
+FGAirport::commStations() const
+{
+  NavDataCache* cache = NavDataCache::instance();
+  CommStationList result;
+  BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(),
+                                                            FGPositioned::FREQ_GROUND,
+                                                            FGPositioned::FREQ_UNICOM))
+  {
+    result.push_back((CommStation*) cache->loadById(pos));
+  }
+  
+  return result;
+}
+
+CommStationList
+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));
+  }
+  
+  return result;
 }
 
 // get airport elevation