#ifdef HAVE_CONFIG_H # include "config.h" #endif #include "LevelDXML.hxx" #include #include #include #include #include using std::string; using std::vector; namespace flightgear { NavdataVisitor::NavdataVisitor(FGAirport* aApt, const SGPath& aPath): _airport(aApt), _path(aPath), _sid(NULL), _star(NULL), _approach(NULL), _transition(NULL), _procedure(NULL) { } void NavdataVisitor::startXML() { } void NavdataVisitor::endXML() { } void NavdataVisitor::startElement(const char* name, const XMLAttributes &atts) { _text.clear(); string tag(name); if (tag == "Airport") { string icao(atts.getValue("ICAOcode")); if (_airport->ident() != icao) { throw sg_format_exception("Airport and ICAO mismatch", icao, _path.utf8Str()); } } else if (tag == "Sid") { string ident(atts.getValue("Name")); _sid = new SID(ident, _airport); _procedure = _sid; _waypoints.clear(); processRunways(_sid, atts); } else if (tag == "Star") { string ident(atts.getValue("Name")); _star = new STAR(ident, _airport); _procedure = _star; _waypoints.clear(); processRunways(_star, atts); } else if ((tag == "Sid_Waypoint") || (tag == "App_Waypoint") || (tag == "Star_Waypoint") || (tag == "AppTr_Waypoint") || (tag == "SidTr_Waypoint") || (tag == "RwyTr_Waypoint")) { // reset waypoint data _speed = 0.0; _altRestrict = RESTRICT_NONE; _altitude = 0.0; _overflightWaypt = false; // default to Fly-by _courseFlag = false; // default to heading } else if (tag == "Approach") { _ident = atts.getValue("Name"); _waypoints.clear(); ProcedureType ty = PROCEDURE_APPROACH_RNAV; _approach = new Approach(_ident, ty); _procedure = _approach; } else if ((tag == "Sid_Transition") || (tag == "App_Transition") || (tag == "Star_Transition")) { _transIdent = atts.getValue("Name"); _transition = new Transition(_transIdent, PROCEDURE_TRANSITION, _procedure); _transWaypts.clear(); } else if (tag == "RunwayTransition") { _transIdent = atts.getValue("Runway"); _transition = new Transition(_transIdent, PROCEDURE_RUNWAY_TRANSITION, _procedure); _transWaypts.clear(); } else { // nothing here, we warn on unrecognized in endElement } } void NavdataVisitor::processRunways(ArrivalDeparture* aProc, const XMLAttributes &atts) { string v("All"); if (atts.hasAttribute("Runways")) { v = atts.getValue("Runways"); } if (v == "All") { for (unsigned int r=0; r<_airport->numRunways(); ++r) { aProc->addRunway(_airport->getRunwayByIndex(r)); } return; } vector rwys; boost::split(rwys, v, boost::is_any_of(" ,")); for (unsigned int r=0; rgetRunwayByIdent(rwys[r]); aProc->addRunway(rwy); } } void NavdataVisitor::endElement(const char* name) { string tag(name); if ((tag == "Sid_Waypoint") || (tag == "App_Waypoint") || (tag == "Star_Waypoint")) { _waypoints.push_back(buildWaypoint(_procedure)); } else if ((tag == "AppTr_Waypoint") || (tag == "SidTr_Waypoint") || (tag == "RwyTr_Waypoint") || (tag == "StarTr_Waypoint")) { _transWaypts.push_back(buildWaypoint(_transition)); } else if (tag == "Sid_Transition") { assert(_sid); // SID waypoints are stored backwards, to share code with STARs std::reverse(_transWaypts.begin(), _transWaypts.end()); _transition->setPrimary(_transWaypts); _sid->addTransition(_transition); } else if (tag == "Star_Transition") { assert(_star); _transition->setPrimary(_transWaypts); _star->addTransition(_transition); } else if (tag == "App_Transition") { assert(_approach); _transition->setPrimary(_transWaypts); _approach->addTransition(_transition); } else if (tag == "RunwayTransition") { ArrivalDeparture* ad; if (_sid) { // SID waypoints are stored backwards, to share code with STARs std::reverse(_transWaypts.begin(), _transWaypts.end()); ad = _sid; } else { ad = _star; } _transition->setPrimary(_transWaypts); FGRunwayRef rwy = _airport->getRunwayByIdent(_transIdent); ad->addRunwayTransition(rwy, _transition); } else if (tag == "Approach") { finishApproach(); } else if (tag == "Sid") { finishSid(); } else if (tag == "Star") { finishStar(); } else if (tag == "Longitude") { _longitude = atof(_text.c_str()); } else if (tag == "Latitude") { _latitude = atof(_text.c_str()); } else if (tag == "Name") { _wayptName = _text; } else if (tag == "Type") { _wayptType = _text; } else if (tag == "Speed") { _speed = atoi(_text.c_str()); } else if (tag == "Altitude") { _altitude = atof(_text.c_str()); } else if (tag == "AltitudeRestriction") { if (_text == "at") { _altRestrict = RESTRICT_AT; } else if (_text == "above") { _altRestrict = RESTRICT_ABOVE; } else if (_text == "below") { _altRestrict = RESTRICT_BELOW; } else { throw sg_format_exception("Unrecognized altitude restriction", _text); } } else if (tag == "Hld_Rad_or_Inbd") { if (_text == "Inbd") { _holdRadial = -1.0; } } else if (tag == "Hld_Time_or_Dist") { _holdDistance = (_text == "Dist"); } else if (tag == "Hld_Rad_value") { _holdRadial = atof(_text.c_str()); } else if (tag == "Hld_Turn") { _holdRighthanded = (_text == "Right"); } else if (tag == "Hld_td_value") { _holdTD = atof(_text.c_str()); } else if (tag == "Hdg_Crs") { _courseFlag = atoi(_text.c_str()); } else if (tag == "Hdg_Crs_value") { _courseOrHeading = atof(_text.c_str()); } else if (tag == "DMEtoIntercept") { _dmeDistance = atof(_text.c_str()); } else if (tag == "RadialtoIntercept") { _radial = atof(_text.c_str()); } else if (tag == "Flytype") { // values are 'Fly-by' and 'Fly-over' _overflightWaypt = (_text == "Fly-over"); } else if ((tag == "AltitudeCons") || (tag == "BankLimit") || (tag == "Sp_Turn")) { // ignored but don't warn } else { SG_LOG(SG_IO, SG_INFO, "unrecognized Level-D XML element:" << tag); } } Waypt* NavdataVisitor::buildWaypoint(RouteBase* owner) { Waypt* wp = NULL; if (_wayptType == "Normal") { // new LatLonWaypoint SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); wp = new BasicWaypt(pos, _wayptName, owner); } else if (_wayptType == "Runway") { string ident = _wayptName.substr(2); 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); wp = h; if (_holdRighthanded) { h->setRightHanded(); } else { h->setLeftHanded(); } if (_holdDistance) { h->setHoldDistance(_holdTD); } else { h->setHoldTime(_holdTD * 60.0); } if (_holdRadial >= 0.0) { h->setHoldRadial(_holdRadial); } } else if (_wayptType == "Vectors") { wp = new ATCVectors(owner, _airport); } else if ((_wayptType == "Intc") || (_wayptType == "VorRadialIntc")) { SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); wp = new RadialIntercept(owner, _wayptName, pos, _courseOrHeading, _radial); } else if (_wayptType == "DmeIntc") { SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)); wp = new DMEIntercept(owner, _wayptName, pos, _courseOrHeading, _dmeDistance); } else if (_wayptType == "ConstHdgtoAlt") { wp = new HeadingToAltitude(owner, _wayptName, _courseOrHeading); } else if (_wayptType == "PBD") { SGGeod pos(SGGeod::fromDeg(_longitude, _latitude)), pos2; double az2; SGGeodesy::direct(pos, _courseOrHeading, _dmeDistance, pos2, az2); wp = new BasicWaypt(pos2, _wayptName, owner); } else { SG_LOG(SG_NAVAID, SG_ALERT, "implement waypoint type:" << _wayptType); throw sg_format_exception("Unrecognized waypt type", _wayptType); } assert(wp); if ((_altitude > 0.0) && (_altRestrict != RESTRICT_NONE)) { wp->setAltitude(_altitude,_altRestrict); } if (_speed > 0.0) { wp->setSpeed(_speed, RESTRICT_AT); // or _BELOW? } if (_overflightWaypt) { wp->setFlag(WPT_OVERFLIGHT); } return wp; } void NavdataVisitor::finishApproach() { WayptVec::iterator it; FGRunwayRef rwy; // find the runway node for (it = _waypoints.begin(); it != _waypoints.end(); ++it) { FGPositionedRef navid = (*it)->source(); if (!navid) { continue; } if (navid->type() == FGPositioned::RUNWAY) { rwy = (FGRunway*) navid.get(); break; } } if (!rwy) { throw sg_format_exception("Malformed approach, no runway waypt", _ident); } WayptVec primary(_waypoints.begin(), it); // erase all points up to and including the runway, to leave only the // missed segments _waypoints.erase(_waypoints.begin(), ++it); _approach->setRunway(rwy); _approach->setPrimaryAndMissed(primary, _waypoints); _airport->addApproach(_approach); _approach = NULL; } void NavdataVisitor::finishSid() { // reverse order, because that's how we deal with commonality between // STARs and SIDs. SID::route undoes this std::reverse(_waypoints.begin(), _waypoints.end()); _sid->setCommon(_waypoints); _airport->addSID(_sid); _sid = NULL; } void NavdataVisitor::finishStar() { _star->setCommon(_waypoints); _airport->addSTAR(_star); _star = NULL; } void NavdataVisitor::data (const char * s, int len) { _text += string(s, len); } void NavdataVisitor::pi (const char * target, const char * data) { //cout << "Processing instruction " << target << ' ' << data << endl; } void NavdataVisitor::warning (const char * message, int line, int column) { SG_LOG(SG_NAVAID, SG_WARN, "Warning: " << message << " (" << line << ',' << column << ')'); } void NavdataVisitor::error (const char * message, int line, int column) { SG_LOG(SG_NAVAID, SG_ALERT, "Error: " << message << " (" << line << ',' << column << ')'); } }