31#include <simgear/structure/exception.hxx>
32#include <simgear/misc/sg_path.hxx>
33#include <simgear/magvar/magvar.hxx>
34#include <simgear/timing/sg_time.hxx>
35#include <simgear/io/iostreams/sgstream.hxx>
36#include <simgear/misc/strutils.hxx>
37#include <simgear/props/props_io.hxx>
66double magvarDegAt(
const SGGeod& pos)
68 double jd =
globals->get_time_params()->getJD();
69 return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
73 const SGGeod& basePosition,
77 assert(pieces.size() == 4);
81 SG_LOG(SG_NAVAID, SG_INFO,
"Unable to find FGPositioned with ident:" << pieces[2]);
85 double r1 =
atof(pieces[1].c_str()),
86 r2 =
atof(pieces[3].c_str());
91 bool ok = SGGeodesy::radialIntersection(p1->geod(), r1,
p2->geod(), r2, intersection);
93 SG_LOG(SG_NAVAID, SG_INFO,
"no valid intersection for:" << pieces[0] <<
"/" << pieces[2]);
97 std::string
name = p1->ident() +
"-" +
p2->ident();
101WayptRef viaFromString(
const SGGeod& basePosition,
const std::string& target)
103 assert(target.find(
"VIA-") == 0);
104 string_list pieces(simgear::strutils::split(target.substr(4),
"/"));
105 if (pieces.size() != 2) {
106 SG_LOG(SG_NAVAID, SG_WARN,
"Malformed VIA specification string:" << target);
113 SG_LOG(SG_NAVAID, SG_WARN,
"TO navaid:" << pieces[3] <<
" unknown");
120 SG_LOG(SG_NAVAID, SG_WARN,
"Unknown airway:" << pieces[0]);
124 return new Via(
nullptr, airway, nav);
127static double convertSpeedToKnots(
RouteUnits aUnits,
double aAltitudeFt,
double aValue)
131 case SPEED_KPH:
return aValue * SG_KMH_TO_MPS * SG_MPS_TO_KT;
135 throw sg_format_exception(
"Can't convert unit to Knots",
"convertSpeedToKnots");
139static double convertSpeedFromKnots(
RouteUnits aUnits,
double aAltitudeFt,
double aValue)
148 case SPEED_KPH:
return aValue * SG_KT_TO_MPS * SG_MPS_TO_KMH;
152 throw sg_format_exception(
"Can't convert to unit",
"convertSpeedFromKnots");
160 const double valueKnots = convertSpeedToKnots(aSrc, aAltitudeFt, aValue);
161 return convertSpeedFromKnots(aDest, aAltitudeFt, valueKnots);
177 throw sg_format_exception(
"Unsupported source altitude units",
"convertAltitudeUnits");
185 throw sg_format_exception(
"Unsupported destination altitude units",
"convertAltitudeUnits");
206 return ((_flags & aFlag) != 0);
212 throw sg_range_exception(
"invalid waypoint flag set");
215 _flags = (_flags & ~aFlag);
216 if (aV) _flags |= aFlag;
239 return (aPos ==
source());
244 double d = SGGeodesy::distanceM(
position(), aPos);
330 double jd =
globals->get_time_params()->getJD();
331 _magVarDeg = sgGetMagVar(
position(), jd) * SG_RADIANS_TO_DEGREES;
352 std::string l = simgear::strutils::lowercase(aStr);
362 throw sg_io_exception(
"unknown restriction specification:" + l,
363 "Route restrictFromString");
377 throw sg_exception(
"invalid route restriction",
378 "Route restrictToString");
382WayptRef Waypt::createInstance(RouteBase* aOwner,
const std::string& aTypeName)
385 if (aTypeName ==
"basic") {
386 r =
new BasicWaypt(aOwner);
387 }
else if (aTypeName ==
"navaid") {
388 r =
new NavaidWaypoint(aOwner);
389 }
else if (aTypeName ==
"offset-navaid") {
390 r =
new OffsetNavaidWaypoint(aOwner);
391 }
else if (aTypeName ==
"hold") {
392 r =
new Hold(aOwner);
393 }
else if (aTypeName ==
"runway") {
394 r =
new RunwayWaypt(aOwner);
395 }
else if (aTypeName ==
"hdgToAlt") {
396 r =
new HeadingToAltitude(aOwner);
397 }
else if (aTypeName ==
"dmeIntercept") {
398 r =
new DMEIntercept(aOwner);
399 }
else if (aTypeName ==
"radialIntercept") {
400 r =
new RadialIntercept(aOwner);
401 }
else if (aTypeName ==
"vectors") {
402 r =
new ATCVectors(aOwner);
403 }
else if (aTypeName ==
"discontinuity") {
404 r =
new Discontinuity(aOwner);
405 }
else if (aTypeName ==
"via") {
409 if (!r || (r->type() != aTypeName)) {
410 throw sg_exception(
"broken factory method for type:" + aTypeName,
411 "Waypt::createInstance");
419 if (!aProp->hasChild(
"type")) {
420 SG_LOG(SG_GENERAL, SG_WARN,
"Bad waypoint node: missing type");
425 if (aProp->hasChild(
"airway")) {
427 if (aProp->hasValue(
"network")) {
438 WayptRef nd(createInstance(aOwner, aProp->getStringValue(
"type")));
439 if (nd->initFromProperties(aProp)) {
442 SG_LOG(SG_GENERAL, SG_WARN,
"failed to create waypoint, trying basic");
451 if (bw->initFromProperties(aProp)) {
461 string_list pieces(simgear::strutils::split(target,
"/"));
462 if ((pieces.size() != 1) && (pieces.size() != 3)) {
467 const bool defaultToLonLat =
true;
468 if (!simgear::strutils::parseStringAsGeod(pieces[0], &g, defaultToLonLat)) {
472 if (pieces.size() == 3) {
474 const double bearing = std::stod(pieces[1]);
475 const double distanceNm = std::stod(pieces[2]);
476 g = SGGeodesy::direct(g, bearing, distanceNm * SG_NM_TO_METER);
480 const int lonDeg =
static_cast<int>(g.getLongitudeDeg());
481 const int latDeg =
static_cast<int>(g.getLatitudeDeg());
484 char ew = (lonDeg < 0) ?
'W' :
'E';
485 char ns = (latDeg < 0) ?
'S' :
'N';
486 snprintf(buf, 32,
"%c%03d%c%03d", ew, std::abs(lonDeg), ns, std::abs(latDeg));
493 auto vicinity = aVicinity;
494 if (!vicinity.isValid()) {
495 vicinity =
globals->get_aircraft_position();
498 auto target = simgear::strutils::uppercase(s);
504 size_t pos = target.find(
'@');
505 if (pos != string::npos) {
506 auto altStr = simgear::strutils::uppercase(target.substr(pos + 1));
507 if (simgear::strutils::starts_with(altStr,
"FL")) {
509 altStr = altStr.substr(2);
510 }
else if (
fgGetString(
"/sim/startup/units") ==
"meter") {
514 alt = std::stof(altStr);
515 target = target.substr(0, pos);
522 const double magvar = magvarDegAt(vicinity);
526 }
else if (target.find(
"VIA-") == 0) {
527 wpt = viaFromString(vicinity, target);
532 string_list pieces(simgear::strutils::split(target,
"/"));
535 SG_LOG(SG_NAVAID, SG_INFO,
"Unable to find FGPositioned with ident:" << pieces.front());
539 if (pieces.size() == 1) {
541 }
else if (pieces.size() == 3) {
543 double radial =
atof(pieces[1].c_str()),
544 distanceNm =
atof(pieces[2].c_str());
547 }
else if (pieces.size() == 2) {
550 SG_LOG(SG_NAVAID, SG_INFO,
"Waypoint is not an airport:" << pieces.front());
555 SG_LOG(SG_NAVAID, SG_INFO,
"No runway: " << pieces[1] <<
" at " << pieces[0]);
561 }
else if (pieces.size() == 4) {
562 wpt = intersectionFromString(
p, vicinity, magvar, pieces);
567 SG_LOG(SG_NAVAID, SG_INFO,
"Unable to parse waypoint:" << target);
572 wpt->setAltitude(alt, altSetting, altitudeUnits);
580 n->setStringValue(
"type",
type());
586 if (aProp->hasChild(
"generated")) {
590 if (aProp->hasChild(
"overflight")) {
594 if (aProp->hasChild(
"arrival")) {
598 if (aProp->hasChild(
"approach")) {
602 if (aProp->hasChild(
"departure")) {
606 if (aProp->hasChild(
"miss")) {
610 if (aProp->hasChild(
"airway")) {
614 if (aProp->hasChild(
"alt-restrict")) {
616 if (aProp->hasChild(
"altitude-ft")) {
617 _altitude = aProp->getDoubleValue(
"altitude-ft");
619 }
else if (aProp->hasChild(
"altitude-m")) {
620 _altitude = aProp->getDoubleValue(
"altitude-m");
622 }
else if (aProp->hasChild(
"flight-level")) {
623 _altitude = aProp->getIntValue(
"flight-level");
627 if (aProp->hasChild(
"constraint-altitude")) {
632 if (aProp->hasChild(
"speed-restrict")) {
639 if (aProp->hasChild(
"speed-mach")) {
641 _speed = aProp->getDoubleValue(
"speed-mach");
642 }
else if (aProp->hasChild(
"speed-kph")) {
644 _speed = aProp->getDoubleValue(
"speed-kph");
646 _speed = aProp->getDoubleValue(
"speed");
658 aProp->setBoolValue(
"overflight",
true);
662 aProp->setBoolValue(
"departure",
true);
666 aProp->setBoolValue(
"arrival",
true);
670 aProp->setBoolValue(
"approach",
true);
675 aProp->setStringValue(
"airway", awy->ident());
676 aProp->setIntValue(
"network", awy->level());
680 aProp->setBoolValue(
"miss",
true);
684 aProp->setBoolValue(
"generated",
true);
690 aProp->setDoubleValue(
"altitude-m",
_altitude);
692 aProp->setDoubleValue(
"flight-level",
_altitude);
695 aProp->setDoubleValue(
"altitude-ft",
_altitude);
705 aProp->setDoubleValue(
"speed",
_speed);
715 SGPath
p = SGPath::desktop() / (aName +
".kml");
718 SG_LOG(SG_NAVAID, SG_WARN,
"unable to open:" <<
p);
723 f <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
724 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
736 const WayptVec& aRoute, std::ostream& aStream)
739 aStream <<
"<Placemark>\n";
740 aStream <<
"<name>" << aIdent <<
"</name>\n";
741 aStream <<
"<LineString>\n";
742 aStream <<
"<tessellate>1</tessellate>\n";
743 aStream <<
"<coordinates>\n";
746 for (
unsigned int i=0;
i<aRoute.size(); ++
i) {
747 SGGeod pos = aRoute[
i]->position();
748 aStream << pos.getLongitudeDeg() <<
"," << pos.getLatitudeDeg() <<
" " << endl;
752 aStream <<
"</coordinates>\n"
754 "</Placemark>\n" << endl;
762 readXML(aPath, visitor);
763 }
catch (sg_io_exception& ex) {
764 SG_LOG(SG_NAVAID, SG_WARN,
"failure parsing procedures: " << aPath <<
765 "\n\t" << ex.getMessage() <<
"\n\tat:" << ex.getLocation().asString());
766 }
catch (sg_exception& ex) {
767 SG_LOG(SG_NAVAID, SG_WARN,
"failure parsing procedures: " << aPath <<
768 "\n\t" << ex.getMessage());
FGRunwayRef getRunwayByIdent(const std::string &aIdent) const
bool hasRunwayWithIdent(const std::string &aIdent) const
static double knotsFromMachAtAltitudeFt(const double mach, const double altFt, const double Tsl=atmodel::ISA::T0)
static double machFromKnotsAtAltitudeFt(const double knots, const double altFt, const double Tsl=atmodel::ISA::T0)
static FGPositionedRef findClosestWithIdent(const std::string &aIdent, const SGGeod &aPos, Filter *aFilter=NULL)
virtual const SGGeod & geod() const
const std::string & ident() const
static AirwayRef findByIdentAndNavaid(const std::string &aIdent, const FGPositionedRef nav)
Find an airway by ident, and containing a particula rnavaid/fix.
static AirwayRef findByIdent(const std::string &aIdent, Level level)
Waypoint based upon a navaid.
static void dumpRouteToKML(const WayptVec &aRoute, const std::string &aName)
static void loadAirportProcedures(const SGPath &aPath, FGAirport *aApt)
static void dumpRouteToKMLLineString(const std::string &aIdent, const WayptVec &aRoute, std::ostream &aStream)
virtual std::string icaoDescription() const
icaoDescription - description of the waypoint in ICAO route plan format
RouteUnits _altitudeUnits
double speed(RouteUnits aUnits=DEFAULT_UNITS) const
double altitude(RouteUnits aUnits=DEFAULT_UNITS) const
double altitudeFt() const
RouteRestriction _altRestrict
std::optional< double > _constraintAltitude
some restriction types specify two altitudes, in which case this is the second value,...
virtual FGPositioned * source() const
The Positioned associated with this element, if one exists.
virtual std::string type() const =0
static WayptRef createFromString(RouteBase *aOwner, const std::string &s, const SGGeod &vicinity)
Create a waypoint from the route manager's standard string format:
void setAltitude(double aAlt, RouteRestriction aRestrict, RouteUnits aUnits=DEFAULT_UNITS)
void setConstraintAltitude(double aAlt)
RouteRestriction _speedRestrict
void setFlag(WayptFlag aFlag, bool aV=true)
bool matches(Waypt *aOther) const
Test if this element and another are 'the same', i.e matching ident and lat/lon are approximately equ...
static WayptRef createFromProperties(RouteBase *aOwner, SGPropertyNode_ptr aProp)
Factory method.
virtual bool initFromProperties(SGPropertyNode_ptr aProp)
Persistence helper - read node properties from a file.
virtual bool flag(WayptFlag aFlag) const
Test if the specified flag is set for this element.
virtual double headingRadialDeg() const
return the assoicated heading or radial for this waypoint.
virtual void writeToProperties(SGPropertyNode_ptr aProp) const
Persistence helper - save this element to a node.
void saveAsNode(SGPropertyNode *node) const
virtual std::string ident() const
Identifier assoicated with the waypoint.
virtual SGGeod position() const =0
static WayptRef fromLatLonString(RouteBase *aOwner, const std::string &target)
virtual double magvarDeg() const
Magentic variation at/in the vicinity of the waypoint.
void setSpeed(double aSpeed, RouteRestriction aRestrict, RouteUnits aUnits=DEFAULT_UNITS)
double constraintAltitude(RouteUnits aUnits=DEFAULT_UNITS) const
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
std::vector< std::string > string_list
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
double convertAltitudeUnits(RouteUnits aSrc, RouteUnits aDest, double aValue)
RouteRestriction restrictionFromString(const std::string &aStr)
SGSharedPtr< FGPositioned > FGPositionedRef
SGSharedPtr< Waypt > WayptRef
SGSharedPtr< Airway > AirwayRef
double convertSpeedUnits(RouteUnits aSrc, RouteUnits aDest, double aAltitudeFt, double aValue)
const char * restrictionToString(RouteRestriction aRestrict)
bool isMachRestrict(RouteRestriction rr)
@ WPT_MISS
segment is part of missed approach
@ WPT_GENERATED
waypoint was created automatically (not manually entered/loaded) for example waypoints from airway ro...
@ WPT_OVERFLIGHT
must overfly the point directly
@ WPT_VIA
waypoint prodcued by expanding a VIA segment
std::vector< WayptRef > WayptVec
@ SPEED_COMPUTED_MACH
variant on above to encode a Mach value
@ SPEED_RESTRICT_MACH
encode an 'AT' restriction in Mach, not IAS
static double atof(const string &str)