From 01216e4c19de572b9a74b242e408ea6a85b8791e Mon Sep 17 00:00:00 2001 From: legoboyvdlp R Date: Mon, 2 Aug 2021 16:31:54 +0100 Subject: [PATCH] Add ability to override airport runway definitions by creating a rwy_override.xml file which allows procedures to be loaded for airports which have new runway numbering --- src/Airports/airport.cxx | 59 +++++++++++++++++++++++++++++++++++++-- src/Airports/airport.hxx | 21 ++++++++++++-- src/Navaids/LevelDXML.cxx | 37 ++++++++++++++++-------- 3 files changed, 99 insertions(+), 18 deletions(-) diff --git a/src/Airports/airport.cxx b/src/Airports/airport.cxx index 742039f39..916312b0f 100644 --- a/src/Airports/airport.cxx +++ b/src/Airports/airport.cxx @@ -21,9 +21,7 @@ // // $Id$ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "airport.hxx" @@ -592,6 +590,29 @@ void FGAirport::loadProcedures() const RouteBase::loadAirportProcedures(path, const_cast(this)); } +void FGAirport::loadRunwayRenames() const +{ + if (mRunwayRenamesLoaded) { + return; + } + + SGPath path; + if (!XMLLoader::findAirportData(ident(), "runway_rename", path)) { + // No rename for airport; ignore + mRunwayRenamesLoaded = true; + return; + } + + try { + SGPropertyNode_ptr rootNode = new SGPropertyNode; + readProperties(path, rootNode); + const_cast(this)->parseRunwayRenameData(rootNode); + mRunwayRenamesLoaded = true; + } catch (sg_exception& e) { + SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading runrway rename XML failed:" << e.getFormattedMessage()); + } +} + void FGAirport::loadSceneryDefinitions() const { if (mThresholdDataLoaded) { @@ -736,6 +757,38 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot) mTowerPosition = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM); } +void FGAirport::parseRunwayRenameData(SGPropertyNode* aRoot) +{ + SGPropertyNode* overrideNode = aRoot->getChild("runway-rename"); + for (auto rnm : overrideNode->getChildren("runway")) { + const std::string oldIdent = rnm->getStringValue("old-ident"); + const std::string newIdent = rnm->getStringValue("new-ident"); + if (oldIdent.empty() || newIdent.empty()) { + SG_LOG(SG_NAVAID, SG_WARN, ident() << ": runway rename: Skipping bad runway rename entry"); + continue; + } + + if (!hasRunwayWithIdent(oldIdent)) { + SG_LOG(SG_NAVAID, SG_WARN, ident() << ": no old runway with ident:" << oldIdent); + continue; + } + + _renamedRunways[newIdent] = oldIdent; + } +} + +std::string FGAirport::findAPTRunwayForNewName(const std::string& newIdent) const +{ + loadRunwayRenames(); + + auto it = _renamedRunways.find(newIdent); + if (it == _renamedRunways.end()) + return {}; + + return it->second; +} + + void FGAirport::validateILSData() { if (mILSDataLoaded) { diff --git a/src/Airports/airport.hxx b/src/Airports/airport.hxx index a8c45fd93..1c0555c5f 100644 --- a/src/Airports/airport.hxx +++ b/src/Airports/airport.hxx @@ -152,7 +152,9 @@ class FGAirport : public FGPositioned */ FGRunwayList getRunways() const; - /** + std::string findAPTRunwayForNewName(const std::string& newIdent) const; + + /** * Useful predicate for FMS/GPS/NAV displays and similar - check if this * aiport has a hard-surfaced runway of at least the specified length. */ @@ -320,6 +322,7 @@ class FGAirport : public FGPositioned // helper to allow testing without needing a full Airports hierarchy // only for use by the test-suite, not available outside of it. void testSuiteInjectGroundnetXML(const SGPath& path); + private: static flightgear::AirportCache airportCache; @@ -343,19 +346,26 @@ private: void validateTowerData() const; /** - * Helper to parse property data loaded from an ICAO.twr.xml file + * Helpers to parse property data loaded from an ICAO.twr.xml file */ void readTowerData(SGPropertyNode* aRoot); + /** + * Helpers to parse property data loaded from an ICAO.rwy_override.xml file + * or a ICAO.rwy_override.xml file + */ + void parseRunwayRenameData(SGPropertyNode* aRoot); + PositionedIDVec itemsOfType(FGPositioned::Type ty) const; std::string _name; - bool _has_metar; + bool _has_metar = false; void loadRunways() const; void loadHelipads() const; void loadTaxiways() const; void loadProcedures() const; + void loadRunwayRenames() const; mutable bool mTowerDataLoaded; mutable bool mHasTower; @@ -365,6 +375,7 @@ private: mutable bool mHelipadsLoaded; mutable bool mTaxiwaysLoaded; mutable bool mProceduresLoaded; + mutable bool mRunwayRenamesLoaded = false; bool mIsClosed; mutable bool mThresholdDataLoaded; bool mILSDataLoaded; @@ -386,6 +397,10 @@ private: std::vector mApproaches; mutable std::unique_ptr _groundNetwork; + + using RunwayRenameMap = std::map; + // map from new name (eg in Navigraph) to old name (in apt.dat) + RunwayRenameMap _renamedRunways; }; // find basic airport location info from airport database diff --git a/src/Navaids/LevelDXML.cxx b/src/Navaids/LevelDXML.cxx index 113931f67..d2a7b533d 100644 --- a/src/Navaids/LevelDXML.cxx +++ b/src/Navaids/LevelDXML.cxx @@ -115,10 +115,15 @@ void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes auto rwys = simgear::strutils::split_on_any_of(v, " ,"); for (auto rwy : rwys) { if (!_airport->hasRunwayWithIdent(rwy)) { - SG_LOG(SG_NAVAID, SG_DEV_WARN, "Procedure file " << _path << " references unknown airport runway:" << rwy); - continue; + const auto renamed = _airport->findAPTRunwayForNewName(rwy); + if (!renamed.empty()) { + rwy = renamed; + } else { + SG_LOG(SG_NAVAID, SG_DEV_WARN, "Procedure file " << _path << " references unknown airport runway:" << rwy); + continue; + } } - + aProc->addRunway(_airport->getRunwayByIdent(rwy)); } } @@ -241,15 +246,23 @@ Waypt* NavdataVisitor::buildWaypoint(RouteBase* owner) wp = new BasicWaypt(pos, _wayptName, owner); } else if (_wayptType == "Runway") { string ident = _wayptName.substr(2); - if (_airport->hasRunwayWithIdent(ident)) { - FGRunwayRef rwy = _airport->getRunwayByIdent(ident); - wp = new RunwayWaypt(rwy, owner); - } else { - SG_LOG(SG_NAVAID, SG_DEV_WARN, "Missing runway " << ident << " reading " << _path); - SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); - // fall back to a basic WP - wp = new BasicWaypt(pos, _wayptName, owner); - } + if (!_airport->hasRunwayWithIdent(ident)) { + const auto renamed = _airport->findAPTRunwayForNewName(ident); + if (renamed.empty()) { + SG_LOG(SG_NAVAID, SG_DEV_WARN, "Missing runway " << ident << " reading " << _path); + SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); + // fall back to a basic WP + wp = new BasicWaypt(pos, _wayptName, owner); + ident.clear(); + } else { + ident = renamed; + } + } + + if (!ident.empty()) { + FGRunwayRef rwy = _airport->getRunwayByIdent(ident); + wp = new RunwayWaypt(rwy, owner); + } } else if (_wayptType == "Hold") { SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); Hold* h = new Hold(pos, _wayptName, owner);