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
This commit is contained in:
parent
20155df3f6
commit
01216e4c19
3 changed files with 99 additions and 18 deletions
|
@ -21,9 +21,7 @@
|
||||||
//
|
//
|
||||||
// $Id$
|
// $Id$
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#include <config.h>
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "airport.hxx"
|
#include "airport.hxx"
|
||||||
|
|
||||||
|
@ -592,6 +590,29 @@ void FGAirport::loadProcedures() const
|
||||||
RouteBase::loadAirportProcedures(path, const_cast<FGAirport*>(this));
|
RouteBase::loadAirportProcedures(path, const_cast<FGAirport*>(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<FGAirport*>(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
|
void FGAirport::loadSceneryDefinitions() const
|
||||||
{
|
{
|
||||||
if (mThresholdDataLoaded) {
|
if (mThresholdDataLoaded) {
|
||||||
|
@ -736,6 +757,38 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot)
|
||||||
mTowerPosition = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM);
|
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()
|
void FGAirport::validateILSData()
|
||||||
{
|
{
|
||||||
if (mILSDataLoaded) {
|
if (mILSDataLoaded) {
|
||||||
|
|
|
@ -152,7 +152,9 @@ class FGAirport : public FGPositioned
|
||||||
*/
|
*/
|
||||||
FGRunwayList getRunways() const;
|
FGRunwayList getRunways() const;
|
||||||
|
|
||||||
/**
|
std::string findAPTRunwayForNewName(const std::string& newIdent) const;
|
||||||
|
|
||||||
|
/**
|
||||||
* Useful predicate for FMS/GPS/NAV displays and similar - check if this
|
* Useful predicate for FMS/GPS/NAV displays and similar - check if this
|
||||||
* aiport has a hard-surfaced runway of at least the specified length.
|
* 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
|
// helper to allow testing without needing a full Airports hierarchy
|
||||||
// only for use by the test-suite, not available outside of it.
|
// only for use by the test-suite, not available outside of it.
|
||||||
void testSuiteInjectGroundnetXML(const SGPath& path);
|
void testSuiteInjectGroundnetXML(const SGPath& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static flightgear::AirportCache airportCache;
|
static flightgear::AirportCache airportCache;
|
||||||
|
|
||||||
|
@ -343,19 +346,26 @@ private:
|
||||||
void validateTowerData() const;
|
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);
|
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;
|
PositionedIDVec itemsOfType(FGPositioned::Type ty) const;
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
bool _has_metar;
|
bool _has_metar = false;
|
||||||
|
|
||||||
void loadRunways() const;
|
void loadRunways() const;
|
||||||
void loadHelipads() const;
|
void loadHelipads() const;
|
||||||
void loadTaxiways() const;
|
void loadTaxiways() const;
|
||||||
void loadProcedures() const;
|
void loadProcedures() const;
|
||||||
|
void loadRunwayRenames() const;
|
||||||
|
|
||||||
mutable bool mTowerDataLoaded;
|
mutable bool mTowerDataLoaded;
|
||||||
mutable bool mHasTower;
|
mutable bool mHasTower;
|
||||||
|
@ -365,6 +375,7 @@ private:
|
||||||
mutable bool mHelipadsLoaded;
|
mutable bool mHelipadsLoaded;
|
||||||
mutable bool mTaxiwaysLoaded;
|
mutable bool mTaxiwaysLoaded;
|
||||||
mutable bool mProceduresLoaded;
|
mutable bool mProceduresLoaded;
|
||||||
|
mutable bool mRunwayRenamesLoaded = false;
|
||||||
bool mIsClosed;
|
bool mIsClosed;
|
||||||
mutable bool mThresholdDataLoaded;
|
mutable bool mThresholdDataLoaded;
|
||||||
bool mILSDataLoaded;
|
bool mILSDataLoaded;
|
||||||
|
@ -386,6 +397,10 @@ private:
|
||||||
std::vector<ApproachRef> mApproaches;
|
std::vector<ApproachRef> mApproaches;
|
||||||
|
|
||||||
mutable std::unique_ptr<FGGroundNetwork> _groundNetwork;
|
mutable std::unique_ptr<FGGroundNetwork> _groundNetwork;
|
||||||
|
|
||||||
|
using RunwayRenameMap = std::map<std::string, std::string>;
|
||||||
|
// map from new name (eg in Navigraph) to old name (in apt.dat)
|
||||||
|
RunwayRenameMap _renamedRunways;
|
||||||
};
|
};
|
||||||
|
|
||||||
// find basic airport location info from airport database
|
// find basic airport location info from airport database
|
||||||
|
|
|
@ -115,10 +115,15 @@ void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes
|
||||||
auto rwys = simgear::strutils::split_on_any_of(v, " ,");
|
auto rwys = simgear::strutils::split_on_any_of(v, " ,");
|
||||||
for (auto rwy : rwys) {
|
for (auto rwy : rwys) {
|
||||||
if (!_airport->hasRunwayWithIdent(rwy)) {
|
if (!_airport->hasRunwayWithIdent(rwy)) {
|
||||||
SG_LOG(SG_NAVAID, SG_DEV_WARN, "Procedure file " << _path << " references unknown airport runway:" << rwy);
|
const auto renamed = _airport->findAPTRunwayForNewName(rwy);
|
||||||
continue;
|
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));
|
aProc->addRunway(_airport->getRunwayByIdent(rwy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,15 +246,23 @@ Waypt* NavdataVisitor::buildWaypoint(RouteBase* owner)
|
||||||
wp = new BasicWaypt(pos, _wayptName, owner);
|
wp = new BasicWaypt(pos, _wayptName, owner);
|
||||||
} else if (_wayptType == "Runway") {
|
} else if (_wayptType == "Runway") {
|
||||||
string ident = _wayptName.substr(2);
|
string ident = _wayptName.substr(2);
|
||||||
if (_airport->hasRunwayWithIdent(ident)) {
|
if (!_airport->hasRunwayWithIdent(ident)) {
|
||||||
FGRunwayRef rwy = _airport->getRunwayByIdent(ident);
|
const auto renamed = _airport->findAPTRunwayForNewName(ident);
|
||||||
wp = new RunwayWaypt(rwy, owner);
|
if (renamed.empty()) {
|
||||||
} else {
|
SG_LOG(SG_NAVAID, SG_DEV_WARN, "Missing runway " << ident << " reading " << _path);
|
||||||
SG_LOG(SG_NAVAID, SG_DEV_WARN, "Missing runway " << ident << " reading " << _path);
|
SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
|
||||||
SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
|
// fall back to a basic WP
|
||||||
// fall back to a basic WP
|
wp = new BasicWaypt(pos, _wayptName, owner);
|
||||||
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") {
|
} else if (_wayptType == "Hold") {
|
||||||
SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
|
SGGeod pos(SGGeod::fromDeg(_longitude, _latitude));
|
||||||
Hold* h = new Hold(pos, _wayptName, owner);
|
Hold* h = new Hold(pos, _wayptName, owner);
|
||||||
|
|
Loading…
Add table
Reference in a new issue