// route.cxx - classes supporting waypoints and route structures // Written by James Turner, started 2009. // // Copyright (C) 2009 Curtis L. Olson // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "config.h" #include "route.hxx" // std #include #include // Boost #include #include #include // SimGear #include #include #include #include #include #include #include // FlightGear #include
#include "Main/fg_props.hxx" #include #include #include #include #include using std::string; using std::vector; using std::endl; using std::fstream; namespace flightgear { const double NO_MAG_VAR = -1000.0; // an impossible mag-var value bool isMachRestrict(RouteRestriction rr) { return (rr == SPEED_RESTRICT_MACH) || (rr == SPEED_COMPUTED_MACH); } Waypt::Waypt(RouteBase* aOwner) : _owner(aOwner), _magVarDeg(NO_MAG_VAR) { } Waypt::~Waypt() { } std::string Waypt::ident() const { return {}; } bool Waypt::flag(WayptFlag aFlag) const { return ((_flags & aFlag) != 0); } void Waypt::setFlag(WayptFlag aFlag, bool aV) { if (aFlag == 0) { throw sg_range_exception("invalid waypoint flag set"); } _flags = (_flags & ~aFlag); if (aV) _flags |= aFlag; } bool Waypt::matches(Waypt* aOther) const { assert(aOther); if (ident() != aOther->ident()) { // cheap check first return false; } return matches(aOther->position()); } bool Waypt::matches(FGPositioned* aPos) const { return aPos && (aPos == source()); } bool Waypt::matches(const SGGeod& aPos) const { double d = SGGeodesy::distanceM(position(), aPos); return (d < 100.0); // 100 metres seems plenty } void Waypt::setAltitude(double aAlt, RouteRestriction aRestrict) { _altitudeFt = aAlt; _altRestrict = aRestrict; } void Waypt::setSpeed(double aSpeed, RouteRestriction aRestrict) { _speed = aSpeed; _speedRestrict = aRestrict; } double Waypt::speedKts() const { assert(_speedRestrict != SPEED_RESTRICT_MACH); return speed(); } double Waypt::speedMach() const { assert(_speedRestrict == SPEED_RESTRICT_MACH); return speed(); } double Waypt::magvarDeg() const { if (_magVarDeg == NO_MAG_VAR) { // derived classes with a default pos must override this method assert(!(position() == SGGeod())); double jd = globals->get_time_params()->getJD(); _magVarDeg = sgGetMagVar(position(), jd) * SG_RADIANS_TO_DEGREES; } return _magVarDeg; } double Waypt::headingRadialDeg() const { return 0.0; } std::string Waypt::icaoDescription() const { return ident(); } /////////////////////////////////////////////////////////////////////////// // persistence static RouteRestriction restrictionFromString(const char* aStr) { std::string l = boost::to_lower_copy(std::string(aStr)); if (l == "at") return RESTRICT_AT; if (l == "above") return RESTRICT_ABOVE; if (l == "below") return RESTRICT_BELOW; if (l == "none") return RESTRICT_NONE; if (l == "mach") return SPEED_RESTRICT_MACH; if (l.empty()) return RESTRICT_NONE; throw sg_io_exception("unknown restriction specification:" + l, "Route restrictFromString"); } static const char* restrictionToString(RouteRestriction aRestrict) { switch (aRestrict) { case RESTRICT_AT: return "at"; case RESTRICT_BELOW: return "below"; case RESTRICT_ABOVE: return "above"; case RESTRICT_NONE: return "none"; case SPEED_RESTRICT_MACH: return "mach"; default: throw sg_exception("invalid route restriction", "Route restrictToString"); } } WayptRef Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName) { WayptRef r; if (aTypeName == "basic") { r = new BasicWaypt(aOwner); } else if (aTypeName == "navaid") { r = new NavaidWaypoint(aOwner); } else if (aTypeName == "offset-navaid") { r = new OffsetNavaidWaypoint(aOwner); } else if (aTypeName == "hold") { r = new Hold(aOwner); } else if (aTypeName == "runway") { r = new RunwayWaypt(aOwner); } else if (aTypeName == "hdgToAlt") { r = new HeadingToAltitude(aOwner); } else if (aTypeName == "dmeIntercept") { r = new DMEIntercept(aOwner); } else if (aTypeName == "radialIntercept") { r = new RadialIntercept(aOwner); } else if (aTypeName == "vectors") { r = new ATCVectors(aOwner); } else if (aTypeName == "discontinuity") { r = new Discontinuity(aOwner); } else if (aTypeName == "via") { r = new Via(aOwner); } if (!r || (r->type() != aTypeName)) { throw sg_exception("broken factory method for type:" + aTypeName, "Waypt::createInstance"); } return r; } WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp) { if (!aProp->hasChild("type")) { throw sg_io_exception("bad props node, no type provided", "Waypt::createFromProperties"); } flightgear::AirwayRef via; if (aProp->hasChild("airway")) { const auto level = static_cast(aProp->getIntValue("network")); via = flightgear::Airway::findByIdent(aProp->getStringValue("airway"), level); if (via) { // override owner if we are from an airway aOwner = via.get(); } } try { WayptRef nd(createInstance(aOwner, aProp->getStringValue("type"))); nd->initFromProperties(aProp); return nd; } catch (sg_exception& e) { SG_LOG(SG_GENERAL, SG_WARN, "failed to create waypoint, trying basic:" << e.getMessage()); } // if we failed to make the waypoint, try again making a basic waypoint. // this handles the case where a navaid waypoint is missing, for example // we also reject navaids that don't look correct (too far form the specified // lat-lon, eg see https://sourceforge.net/p/flightgear/codetickets/1814/ ) // and again fallback to here. WayptRef nd(new BasicWaypt(aOwner)); nd->initFromProperties(aProp); return nd; } void Waypt::saveAsNode(SGPropertyNode* n) const { n->setStringValue("type", type()); writeToProperties(n); } void Waypt::initFromProperties(SGPropertyNode_ptr aProp) { if (aProp->hasChild("generated")) { setFlag(WPT_GENERATED, aProp->getBoolValue("generated")); } if (aProp->hasChild("overflight")) { setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight")); } if (aProp->hasChild("arrival")) { setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival")); } if (aProp->hasChild("approach")) { setFlag(WPT_APPROACH, aProp->getBoolValue("approach")); } if (aProp->hasChild("departure")) { setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure")); } if (aProp->hasChild("miss")) { setFlag(WPT_MISS, aProp->getBoolValue("miss")); } if (aProp->hasChild("airway")) { setFlag(WPT_VIA, true); } if (aProp->hasChild("alt-restrict")) { _altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict")); _altitudeFt = aProp->getDoubleValue("altitude-ft"); } if (aProp->hasChild("speed-restrict")) { _speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict")); _speed = aProp->getDoubleValue("speed"); } } void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const { if (flag(WPT_OVERFLIGHT)) { aProp->setBoolValue("overflight", true); } if (flag(WPT_DEPARTURE)) { aProp->setBoolValue("departure", true); } if (flag(WPT_ARRIVAL)) { aProp->setBoolValue("arrival", true); } if (flag(WPT_APPROACH)) { aProp->setBoolValue("approach", true); } if (flag(WPT_VIA)) { flightgear::AirwayRef awy = (flightgear::Airway*) _owner; aProp->setStringValue("airway", awy->ident()); aProp->setIntValue("network", awy->level()); } if (flag(WPT_MISS)) { aProp->setBoolValue("miss", true); } if (flag(WPT_GENERATED)) { aProp->setBoolValue("generated", true); } if (_altRestrict != RESTRICT_NONE) { aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict)); aProp->setDoubleValue("altitude-ft", _altitudeFt); } if (_speedRestrict != RESTRICT_NONE) { aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict)); aProp->setDoubleValue("speed", _speed); } } RouteBase::~RouteBase() { } void RouteBase::dumpRouteToKML(const WayptVec& aRoute, const std::string& aName) { SGPath p = "/Users/jmt/Desktop/" + aName + ".kml"; sg_ofstream f(p); if (!f.is_open()) { SG_LOG(SG_NAVAID, SG_WARN, "unable to open:" << p); return; } // pre-amble f << "\n" "\n" "\n"; dumpRouteToKMLLineString(aName, aRoute, f); // post-amble f << "\n" "" << endl; f.close(); } void RouteBase::dumpRouteToKMLLineString(const std::string& aIdent, const WayptVec& aRoute, std::ostream& aStream) { // preamble aStream << "\n"; aStream << "" << aIdent << "\n"; aStream << "\n"; aStream << "1\n"; aStream << "\n"; // waypoints for (unsigned int i=0; iposition(); aStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl; } // postable aStream << "\n" "\n" "\n" << endl; } void RouteBase::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt) { assert(aApt); try { NavdataVisitor visitor(aApt, aPath); readXML(aPath, visitor); } catch (sg_io_exception& ex) { SG_LOG(SG_NAVAID, SG_WARN, "failure parsing procedures: " << aPath << "\n\t" << ex.getMessage() << "\n\tat:" << ex.getLocation().asString()); } catch (sg_exception& ex) { SG_LOG(SG_NAVAID, SG_WARN, "failure parsing procedures: " << aPath << "\n\t" << ex.getMessage()); } } } // of namespace flightgear