From a39df4877264fc25365f609c41ed86eec66cc944 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 10 Nov 2015 22:54:57 +0000 Subject: [PATCH] Navaid diagram for launcher - work in progress, needs labels --- src/Airports/airport.cxx | 25 ++- src/Airports/airport.hxx | 5 + src/GUI/BaseDiagram.cxx | 290 +++++++++++++++++++++++++++++++-- src/GUI/BaseDiagram.hxx | 17 +- src/GUI/LocationWidget.cxx | 95 +++++++++-- src/GUI/LocationWidget.hxx | 1 + src/GUI/LocationWidget.ui | 13 +- src/GUI/NavaidDiagram.cxx | 2 - src/GUI/QtLauncher.cxx | 1 + src/GUI/airport-icon.png | Bin 0 -> 834 bytes src/GUI/airport-tower-icon.png | Bin 0 -> 826 bytes src/GUI/heliport-icon.png | Bin 0 -> 847 bytes src/GUI/large-search-icon.png | Bin 8899 -> 0 bytes src/GUI/ndb-icon.png | Bin 0 -> 4463 bytes src/GUI/resources.qrc | 14 +- src/GUI/seaport-icon.png | Bin 0 -> 1088 bytes src/GUI/seaport-tower-icon.png | Bin 0 -> 1075 bytes src/GUI/spinner.gif | Bin 0 -> 1849 bytes src/GUI/vor-dme-icon.png | Bin 0 -> 754 bytes src/GUI/vor-icon.png | Bin 0 -> 758 bytes src/GUI/vortac-icon.png | Bin 0 -> 828 bytes src/GUI/waypoint-icon.png | Bin 0 -> 565 bytes src/Main/positioninit.cxx | 78 +++++---- 23 files changed, 467 insertions(+), 74 deletions(-) create mode 100644 src/GUI/airport-icon.png create mode 100644 src/GUI/airport-tower-icon.png create mode 100644 src/GUI/heliport-icon.png delete mode 100644 src/GUI/large-search-icon.png create mode 100644 src/GUI/ndb-icon.png create mode 100644 src/GUI/seaport-icon.png create mode 100644 src/GUI/seaport-tower-icon.png create mode 100644 src/GUI/spinner.gif create mode 100644 src/GUI/vor-dme-icon.png create mode 100644 src/GUI/vor-icon.png create mode 100644 src/GUI/vortac-icon.png create mode 100644 src/GUI/waypoint-icon.png diff --git a/src/Airports/airport.cxx b/src/Airports/airport.cxx index 60a711bf5..ce8fd1a39 100644 --- a/src/Airports/airport.cxx +++ b/src/Airports/airport.cxx @@ -76,6 +76,7 @@ FGAirport::FGAirport( PositionedID aGuid, _has_metar(has_metar), _dynamics(0), mTowerDataLoaded(false), + mHasTower(false), mRunwaysLoaded(false), mHelipadsLoaded(false), mTaxiwaysLoaded(false), @@ -314,6 +315,20 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const return false; } +FGRunwayRef FGAirport::longestRunway() const +{ + FGRunwayRef r; + loadRunways(); + + BOOST_FOREACH(FGRunwayRef rwy, mRunways) { + if (!r || (r->lengthFt() < rwy->lengthFt())) { + r = rwy; + } + } // of runways iteration + + return r; +} + //------------------------------------------------------------------------------ FGRunwayList FGAirport::getRunways() const { @@ -663,13 +678,14 @@ void FGAirport::validateTowerData() const NavDataCache* cache = NavDataCache::instance(); PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER); if (towers.empty()) { - SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <loadById(towers.front()); mTowerPosition = tower->geod(); + mHasTower = true; } SGPath path; @@ -681,6 +697,7 @@ void FGAirport::validateTowerData() const SGPropertyNode_ptr rootNode = new SGPropertyNode; readProperties(path.str(), rootNode); const_cast(this)->readTowerData(rootNode); + mHasTower = true; } catch (sg_exception& e){ SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading twr XML failed:" << e.getFormattedMessage()); } @@ -723,6 +740,12 @@ void FGAirport::validateILSData() } } +bool FGAirport::hasTower() const +{ + validateTowerData(); + return mHasTower; +} + void FGAirport::readILSData(SGPropertyNode* aRoot) { NavDataCache* cache = NavDataCache::instance(); diff --git a/src/Airports/airport.hxx b/src/Airports/airport.hxx index b9155d68d..b585d19d8 100644 --- a/src/Airports/airport.hxx +++ b/src/Airports/airport.hxx @@ -73,6 +73,8 @@ class FGAirport : public FGPositioned */ void validateILSData(); + bool hasTower() const; + SGGeod getTowerLocation() const; void setMetar(bool value) { _has_metar = value; } @@ -142,6 +144,8 @@ class FGAirport : public FGPositioned */ bool hasHardRunwayOfLengthFt(double aLengthFt) const; + FGRunwayRef longestRunway() const; + unsigned int numTaxiways() const; FGTaxiwayRef getTaxiwayByIndex(unsigned int aIndex) const; FGTaxiwayList getTaxiways() const; @@ -319,6 +323,7 @@ private: void loadProcedures() const; mutable bool mTowerDataLoaded; + mutable bool mHasTower; mutable SGGeod mTowerPosition; mutable bool mRunwaysLoaded; diff --git a/src/GUI/BaseDiagram.cxx b/src/GUI/BaseDiagram.cxx index ddc59f0ad..1c938a0a0 100644 --- a/src/GUI/BaseDiagram.cxx +++ b/src/GUI/BaseDiagram.cxx @@ -27,6 +27,10 @@ #include #include +#include +#include +#include + /* equatorial and polar earth radius */ const float rec = 6378137; // earth radius, equator (?) const float rpol = 6356752.314f; // earth radius, polar (?) @@ -62,6 +66,21 @@ QTransform BaseDiagram::transform() const return t; } +void BaseDiagram::extendRect(QRectF &r, const QPointF &p) +{ + if (p.x() < r.left()) { + r.setLeft(p.x()); + } else if (p.x() > r.right()) { + r.setRight(p.x()); + } + + if (p.y() < r.top()) { + r.setTop(p.y()); + } else if (p.y() > r.bottom()) { + r.setBottom(p.y()); + } +} + void BaseDiagram::paintEvent(QPaintEvent* pe) { QPainter p(this); @@ -79,9 +98,92 @@ void BaseDiagram::paintEvent(QPaintEvent* pe) QTransform t(transform()); p.setTransform(t); + paintNavaids(&p); + paintContents(&p); } +class MapFilter : public FGPositioned::TypeFilter +{ +public: + MapFilter() + { + // addType(FGPositioned::FIX); + addType(FGPositioned::AIRPORT); + addType(FGPositioned::NDB); + addType(FGPositioned::VOR); + } + + virtual bool pass(FGPositioned* aPos) const + { + bool ok = TypeFilter::pass(aPos); + if (ok && (aPos->type() == FGPositioned::FIX)) { + // ignore fixes which end in digits + if (aPos->ident().length() > 4 && isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) { + return false; + } + } + + return ok; + } +}; + + +void BaseDiagram::paintNavaids(QPainter* painter) +{ + QTransform xf = painter->transform(); + painter->setTransform(QTransform()); // reset to identity + QTransform invT = xf.inverted(); + SGGeod topLeft = unproject(invT.map(QPointF(0,0)), m_projectionCenter); + + double minRunwayLengthFt = (16 / m_scale) * SG_METER_TO_FEET; + // add 10nm fudge factor + double drawRangeNm = SGGeodesy::distanceNm(m_projectionCenter, topLeft) + 10.0; + //qDebug() << "draw range computed as:" << drawRangeNm; + + MapFilter f; + FGPositionedList items = FGPositioned::findWithinRange(m_projectionCenter, drawRangeNm, &f); + + // pass 0 - icons + + FGPositionedList::const_iterator it; + for (it = items.begin(); it != items.end(); ++it) { + bool drawAsIcon = true; + + if ((*it)->type() == FGPositioned::AIRPORT) { + FGAirport* apt = static_cast(it->ptr()); + if (apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) { + + drawAsIcon = false; + painter->setTransform(xf); + QVector lines = projectAirportRuwaysWithCenter(apt, m_projectionCenter); + + QPen pen(QColor(0x03, 0x83, 0xbf), 8); + pen.setCosmetic(true); + painter->setPen(pen); + painter->drawLines(lines); + + QPen linePen(Qt::white, 2); + linePen.setCosmetic(true); + painter->setPen(linePen); + painter->drawLines(lines); + + painter->resetTransform(); + } + } + + if (drawAsIcon) { + QPixmap pm = iconForPositioned(*it); + QPointF loc = xf.map(project((*it)->geod())); + loc -= QPointF(pm.width() >> 1, pm.height() >> 1); + painter->drawPixmap(loc, pm); + } + } + + // restore transform + painter->setTransform(xf); +} + void BaseDiagram::mousePressEvent(QMouseEvent *me) { m_lastMousePos = me->pos(); @@ -132,9 +234,8 @@ void BaseDiagram::wheelEvent(QWheelEvent *we) update(); } -void BaseDiagram::paintContents(QPainter*) +void BaseDiagram::paintContents(QPainter* painter) { - } void BaseDiagram::recomputeBounds(bool resetZoom) @@ -158,24 +259,14 @@ void BaseDiagram::doComputeBounds() void BaseDiagram::extendBounds(const QPointF& p) { - if (p.x() < m_bounds.left()) { - m_bounds.setLeft(p.x()); - } else if (p.x() > m_bounds.right()) { - m_bounds.setRight(p.x()); - } - - if (p.y() < m_bounds.top()) { - m_bounds.setTop(p.y()); - } else if (p.y() > m_bounds.bottom()) { - m_bounds.setBottom(p.y()); - } + extendRect(m_bounds, p); } -QPointF BaseDiagram::project(const SGGeod& geod) const +QPointF BaseDiagram::project(const SGGeod& geod, const SGGeod& center) { double r = earth_radius_lat(geod.getLatitudeRad()); - double ref_lat = m_projectionCenter.getLatitudeRad(), - ref_lon = m_projectionCenter.getLongitudeRad(), + double ref_lat = center.getLatitudeRad(), + ref_lon = center.getLongitudeRad(), lat = geod.getLatitudeRad(), lon = geod.getLongitudeRad(), lonDiff = lon - ref_lon; @@ -206,3 +297,170 @@ QPointF BaseDiagram::project(const SGGeod& geod) const return QPointF(x, -y) * r; } + +SGGeod BaseDiagram::unproject(const QPointF& xy, const SGGeod& center) +{ + double r = earth_radius_lat(center.getLatitudeRad()); + double lat = 0, + lon = 0, + ref_lat = center.getLatitudeRad(), + ref_lon = center.getLongitudeRad(), + rho = QVector2D(xy).length(), + c = rho/r; + + if (rho == 0) { + return center; + } + + double x = xy.x(), y = xy.y(); + lat = asin( cos(c) * sin(ref_lat) + (y * sin(c) * cos(ref_lat)) / rho); + + if (ref_lat == (90 * SG_DEGREES_TO_RADIANS)) // north pole + { + lon = ref_lon + atan(-x/y); + } + else if (ref_lat == -(90 * SG_DEGREES_TO_RADIANS)) // south pole + { + lon = ref_lon + atan(x/y); + } + else + { + lon = ref_lon + atan(x* sin(c) / (rho * cos(ref_lat) * cos(c) - y * sin(ref_lat) * sin(c))); + } + + return SGGeod::fromRad(lon, lat); +} + +QPointF BaseDiagram::project(const SGGeod& geod) const +{ + return project(geod, m_projectionCenter); +} + +QPixmap BaseDiagram::iconForPositioned(const FGPositionedRef& pos) +{ + // if airport type, check towered or untowered + + bool isTowered = false; + if (FGAirport::isAirportType(pos)) { + FGAirport* apt = static_cast(pos.ptr()); + isTowered = apt->hasTower(); + } + + switch (pos->type()) { + case FGPositioned::VOR: + // check for VORTAC + + if (static_cast(pos.ptr())->hasDME()) + return QPixmap(":/vor-dme-icon"); + + return QPixmap(":/vor-icon"); + + case FGPositioned::AIRPORT: + return iconForAirport(static_cast(pos.ptr())); + + case FGPositioned::HELIPORT: + return QPixmap(":/heliport-icon"); + case FGPositioned::SEAPORT: + return QPixmap(isTowered ? ":/seaport-tower-icon" : ":/seaport-icon"); + case FGPositioned::NDB: + return QPixmap(":/ndb-icon"); + case FGPositioned::FIX: + return QPixmap(":/waypoint-icon"); + + default: + break; + } + + return QPixmap(); +} + +QPixmap BaseDiagram::iconForAirport(FGAirport* apt) +{ + if (!apt->hasHardRunwayOfLengthFt(1500)) { + return QPixmap(apt->hasTower() ? ":/airport-tower-icon" : ":/airport-icon"); + } + + if (apt->hasHardRunwayOfLengthFt(8500)) { + QPixmap result(32, 32); + result.fill(Qt::transparent); + { + QPainter p(&result); + p.setRenderHint(QPainter::Antialiasing, true); + QRectF b = result.rect().adjusted(4, 4, -4, -4); + QVector lines = projectAirportRuwaysIntoRect(apt, b); + + p.setPen(QPen(QColor(0x03, 0x83, 0xbf), 8)); + p.drawLines(lines); + + p.setPen(QPen(Qt::white, 2)); + p.drawLines(lines); + } + return result; + } + + QPixmap result(25, 25); + result.fill(Qt::transparent); + + { + QPainter p(&result); + p.setRenderHint(QPainter::Antialiasing, true); + p.setPen(Qt::NoPen); + + p.setBrush(apt->hasTower() ? QColor(0x03, 0x83, 0xbf) : + QColor(0x9b, 0x5d, 0xa2)); + p.drawEllipse(QPointF(13, 13), 10, 10); + + FGRunwayRef r = apt->longestRunway(); + + p.setPen(QPen(Qt::white, 2)); + p.translate(13, 13); + p.rotate(r->headingDeg()); + p.drawLine(0, -8, 0, 8); + } + + return result; +} + +QVector BaseDiagram::projectAirportRuwaysWithCenter(FGAirportRef apt, const SGGeod& c) +{ + QVector r; + + const FGRunwayList& runways(apt->getRunwaysWithoutReciprocals()); + FGRunwayList::const_iterator it; + + for (it = runways.begin(); it != runways.end(); ++it) { + FGRunwayRef rwy = *it; + QPointF p1 = project(rwy->geod(), c); + QPointF p2 = project(rwy->end(), c); + r.append(QLineF(p1, p2)); + } + + return r; +} + +QVector BaseDiagram::projectAirportRuwaysIntoRect(FGAirportRef apt, const QRectF &bounds) +{ + QVector r = projectAirportRuwaysWithCenter(apt, apt->geod()); + + QRectF extent; + Q_FOREACH(const QLineF& l, r) { + extendRect(extent, l.p1()); + extendRect(extent, l.p2()); + } + + // find constraining scale factor + double ratioInX = bounds.width() / extent.width(); + double ratioInY = bounds.height() / extent.height(); + + QTransform t; + t.translate(bounds.left(), bounds.top()); + t.scale(std::min(ratioInX, ratioInY), + std::min(ratioInX, ratioInY)); + t.translate(-extent.left(), -extent.top()); // move unscaled to 0,0 + + for (int i=0; i +#include +#include + class BaseDiagram : public QWidget { Q_OBJECT public: BaseDiagram(QWidget* pr); + static QPixmap iconForPositioned(const FGPositionedRef &pos); + static QPixmap iconForAirport(FGAirport *apt); - + static QVector projectAirportRuwaysIntoRect(FGAirportRef apt, const QRectF& bounds); + static QVector projectAirportRuwaysWithCenter(FGAirportRef apt, const SGGeod &c); protected: virtual void paintEvent(QPaintEvent* pe); @@ -60,6 +66,15 @@ protected: QPointF m_panOffset, m_lastMousePos; int m_wheelAngleDeltaAccumulator; bool m_didPan; + + static void extendRect(QRectF& r, const QPointF& p); + + static QPointF project(const SGGeod &geod, const SGGeod ¢er); + + static SGGeod unproject(const QPointF &xy, const SGGeod ¢er); +private: + void paintNavaids(QPainter *p); + }; #endif // of GUI_BASEDIAGRAM_HXX diff --git a/src/GUI/LocationWidget.cxx b/src/GUI/LocationWidget.cxx index 0136103ba..69723a1b5 100644 --- a/src/GUI/LocationWidget.cxx +++ b/src/GUI/LocationWidget.cxx @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "AirportDiagram.hxx" #include "NavaidDiagram.hxx" @@ -202,6 +204,10 @@ public: } } + if (role == Qt::DecorationRole) { + return AirportDiagram::iconForPositioned(pos); + } + if (role == Qt::EditRole) { return QString::fromStdString(pos->ident()); } @@ -228,7 +234,6 @@ Q_SIGNALS: private: - void onSearchResultsPoll() { PositionedIDVec newIds = m_search->results(); @@ -267,7 +272,8 @@ LocationWidget::LocationWidget(QWidget *parent) : QIcon historyIcon(":/history-icon"); m_ui->searchHistory->setIcon(historyIcon); - m_ui->searchIcon->setPixmap(QPixmap(":/search-icon")); + QByteArray format; + m_ui->searchIcon->setMovie(new QMovie(":/spinner", format, this)); m_searchModel = new NavSearchModel; m_ui->searchResultsList->setModel(m_searchModel); @@ -310,8 +316,9 @@ LocationWidget::LocationWidget(QWidget *parent) : this, SLOT(onOffsetDataChanged())); m_backButton = new QToolButton(this); - m_backButton->setGeometry(0, 0, 32, 32); - m_backButton->setIcon(QIcon(":/search-icon")); + m_backButton->setGeometry(0, 0, 64, 32); + m_backButton->setText("<< Back"); + //m_backButton->setIcon(QIcon(":/search-icon")); m_backButton->raise(); connect(m_backButton, &QAbstractButton::clicked, @@ -374,6 +381,9 @@ void LocationWidget::setLocationOptions() { flightgear::Options* opt = flightgear::Options::sharedInstance(); + std::string altStr = QString::number(m_ui->altitudeSpinbox->value()).toStdString(); + std::string vcStr = QString::number(m_ui->airspeedSpinbox->value()).toStdString(); + if (m_locationIsLatLon) { // bypass the options mechanism because converting to deg:min:sec notation // just to parse back again is nasty. @@ -381,6 +391,9 @@ void LocationWidget::setLocationOptions() fgSetDouble("/position/latitude-deg", m_geodLocation.getLatitudeDeg()); fgSetDouble("/sim/presets/longitude-deg", m_geodLocation.getLongitudeDeg()); fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg()); + + opt->addOption("altitude", altStr); + opt->addOption("vc", vcStr); return; } @@ -396,35 +409,79 @@ void LocationWidget::setLocationOptions() int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt(); if (index >= 0) { // explicit runway choice - opt->addOption("runway", apt->getRunwayByIndex(index)->ident()); + FGRunwayRef runway = apt->getRunwayByIndex(index); + opt->addOption("runway", runway->ident()); + + // set nav-radio 1 based on selected runway + if (runway->ILS()) { + double mhz = runway->ILS()->get_freq() / 100.0; + QString navOpt = QString("%1:%2").arg(runway->headingDeg()).arg(mhz); + opt->addOption("nav1", navOpt.toStdString()); + } } if (m_ui->onFinalCheckbox->isChecked()) { opt->addOption("glideslope", "3.0"); - opt->addOption("offset-distance", "10.0"); // in nautical miles + double offsetNm = m_ui->approachDistanceSpin->value(); + opt->addOption("offset-distance", QString::number(offsetNm).toStdString()); } + } else if (m_ui->parkingRadio->isChecked()) { // parking selection opt->addOption("parkpos", m_ui->parkingCombo->currentText().toStdString()); } // of location is an airport - } + } else { + // location is a navaid + // note setting the ident here is ambigious, we really only need and + // want the 'navaid-id' property. However setting the 'real' option + // gives a better UI experience (eg existing Position in Air dialog) + FGPositioned::Type ty = m_location->type(); + switch (ty) { + case FGPositioned::VOR: + opt->addOption("vor", m_location->ident()); + setNavRadioOption(); + break; + + case FGPositioned::NDB: + opt->addOption("ndb", m_location->ident()); + setNavRadioOption(); + break; + + case FGPositioned::FIX: + opt->addOption("fix", m_location->ident()); + break; + default: + break; + } + + opt->addOption("altitude", altStr); + opt->addOption("vc", vcStr); - FGPositioned::Type ty = m_location->type(); - switch (ty) { - case FGPositioned::VOR: - case FGPositioned::NDB: - case FGPositioned::FIX: // set disambiguation property globals->get_props()->setIntValue("/sim/presets/navaid-id", static_cast(m_location->guid())); + } - // we always set 'fix', but really this is just to force positionInit - // code to check for the navaid-id value above. - opt->addOption("fix", m_location->ident()); - break; - default: - break; +} + +void LocationWidget::setNavRadioOption() +{ + flightgear::Options* opt = flightgear::Options::sharedInstance(); + + if (m_location->type() == FGPositioned::VOR) { + FGNavRecordRef nav(static_cast(m_location.ptr())); + double mhz = nav->get_freq() / 100.0; + int heading = 0; // add heading support + QString navOpt = QString("%1:%2").arg(heading).arg(mhz); + opt->addOption("nav1", navOpt.toStdString()); + } else { + FGNavRecordRef nav(static_cast(m_location.ptr())); + int khz = nav->get_freq() / 100; + int heading = 0; + QString adfOpt = QString("%1:%2").arg(heading).arg(khz); + qDebug() << "ADF opt is:" << adfOpt; + opt->addOption("adf1", adfOpt.toStdString()); } } @@ -446,7 +503,9 @@ void LocationWidget::onSearch() if (m_searchModel->isSearchActive()) { m_ui->searchStatusText->setText(QString("Searching for '%1'").arg(search)); + qDebug() << "setting icon visible"; m_ui->searchIcon->setVisible(true); + m_ui->searchIcon->movie()->start(); } else if (m_searchModel->rowCount(QModelIndex()) == 1) { setBaseLocation(m_searchModel->itemAtRow(0)); } diff --git a/src/GUI/LocationWidget.hxx b/src/GUI/LocationWidget.hxx index dc9987337..d6910d036 100644 --- a/src/GUI/LocationWidget.hxx +++ b/src/GUI/LocationWidget.hxx @@ -88,6 +88,7 @@ private: void onOffsetEnabledToggled(bool on); void onBackToSearch(); + void setNavRadioOption(); }; #endif // LOCATIONWIDGET_H diff --git a/src/GUI/LocationWidget.ui b/src/GUI/LocationWidget.ui index 719fe1921..f68766eef 100644 --- a/src/GUI/LocationWidget.ui +++ b/src/GUI/LocationWidget.ui @@ -26,7 +26,7 @@ - 1 + 2 @@ -292,6 +292,9 @@ + + 2 + 0 @@ -342,7 +345,7 @@ - + @@ -355,6 +358,12 @@ + + + 16 + 16 + + TextLabel diff --git a/src/GUI/NavaidDiagram.cxx b/src/GUI/NavaidDiagram.cxx index b31e881cd..fa6b78c2c 100644 --- a/src/GUI/NavaidDiagram.cxx +++ b/src/GUI/NavaidDiagram.cxx @@ -82,8 +82,6 @@ void NavaidDiagram::paintContents(QPainter *painter) SGGeod offsetGeod = SGGeodesy::direct(m_geod, m_offsetBearingDeg, d); QPointF offset = project(offsetGeod); - qDebug() << base << offset; - QPen pen(Qt::green); pen.setCosmetic(true); painter->setPen(pen); diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index 1cd689d73..57f8be6f3 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -787,6 +787,7 @@ void QtLauncher::onRun() bool startPaused = m_ui->startPausedCheck->isChecked() || m_ui->location->shouldStartPaused(); if (startPaused) { + qDebug() << "will start paused"; opt->addOption("enable-freeze", ""); } diff --git a/src/GUI/airport-icon.png b/src/GUI/airport-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1d23061ae451009e7b7b0c22fc3f59ae18a6ff4f GIT binary patch literal 834 zcmV-I1HJr-P)P000>X1^@s6#OZ}&00004b3#c}2nYxW zd4Q_K~zYIy_QXEOi>iaf9H0jF->*hbHN&wC=C)y(v*co7dD-l zg?h{kD~n2M)rBFA4I5nu)$a*r+NA{>1`$L;qoIjieXK;9pqh}pdo22%z8N#^YbE}x zch5QZ{O&#X-47w8`VvE(D3|^igtO_~@ylWQny`?6QD}%N)eE&3k}TxRG#4Tp*MB$; z+K#Xv4Gc=KD327fbv8N@8;iM1hV;e|ws-v#2K0qbKex3h7Ub?6PVf2LN})x@S^jsduHZ18oEKt}#~x z4*4L1J&?>NUj^fe7CzHlh@dv>14QZhv~{Yr8~`9WmYl4!(ZecS^Gs2T$&R*-R#k*- z+VI{{*aCoZ?_>ScnM@{Q%K?1F#$s-r8$AsaJX8HQ`QcQAShU^$bgOhxKoF!xIsgSg zq&?`DzQj-`c>ld{K@=m%(^TGR3Iho0Mtta{KqG>y$~h5!0z~|yLijTd6#IOsfVmv{ z9HbGYN}$`%S9ZrMGQAuK$d_`~O2^8YfYrsWS>`aSD9av zqRrSJ8@{iiEr5dAp3Wb86E^)oe4x={UIK{9gVd;V(DxOW%h;?xBvHLmx~VGZ`l;(H zET(6ELONWOHrS|jYqG#w02OWdZf3782Ff%SB2{0jdjbEKpQyFbo57gB&aU3J!5zlP z6QIU3JrvxIR6g~(G}bFoPXI8ZMw(9Eb|w~t0QAO(TMe2EfB?oppCK}hvf5kZhJb%Y zkXS13+%3&l@jmHG4DUoW3v8GBv25c=;rg<_XV6! zTc>tb0>C2IZ%{YlLtO&h^APDu<(!LQ`i6c;_#4ZYqQAoS{Vz|V-~D?114X}^4gdfE M07*qoM6N<$g7+qN>i_@% literal 0 HcmV?d00001 diff --git a/src/GUI/airport-tower-icon.png b/src/GUI/airport-tower-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0be995ce1a1ec3a9e76e6b7c51256f37650c57 GIT binary patch literal 826 zcmV-A1I7G_P)P000>X1^@s6#OZ}&00004b3#c}2nYxW zdiaKi_v}(qO6jCul=LG)Vl4sxB-ntR@z1 zDi&52B(*WEj<#&r)IzXhLd4dBh4xJ?5J8g$!JlBn#*akXdymDuna&nU_7k>j7MH}mXY3%e`Is{$rk6`B+yp{MUg+S zkU#vU-mP)t3vtE*#VSCiYd*{zZ3+N5-}l~kmX9EABo(hW?}|Kl;95eQW6(rbqCQ>> zrLzYfQUEV7x-p!Li5k3_bC7*q z6D07ZnY}3_GTPSyF@;Ud>=_g1Dju{+T*PoD_N)nl_Erv_c|)V9vL_9P(T$g;zeThu)?IWG_v1hY-E4XqxsTJ%_7vjjzkBn2h9uCYK+omU8n|HiYQf(mLUG{d5Dw zca@6Miq zdugiMIZ7BO64Uogv^cQ5?YHW}M}hmmhN{hAR?>3?`)aa{$tKsszY~=6qdkw7ueZW| zGPA=g6a`yb`79-%xlFV;FrV+6Yc}8V`dzqia5i+7w}Vb1VnpN3Cp=;bv%%m?J6Zu& ztmKbu#^bzLY858ShvDAKsbbSYst2piFrPN`zx)#Y2I^o6JQC`%NB{r;07*qoM6N<$ Eg7K+-?f?J) literal 0 HcmV?d00001 diff --git a/src/GUI/heliport-icon.png b/src/GUI/heliport-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..16075818c0b2f32bacd6e4ec01f195215e3bc6fd GIT binary patch literal 847 zcmV-V1F-ywP)&Rl~EAKe>2||3nB}R+SDS5R>g&)T1OH=4nxxvJ{~VV;JQV#()s?WpbHUak(<}z_0OF1c96ZDrPEJ zFf(q8c}>~A{qJUkw(h#RStaZN7R&~8?1u>ZGCisOY0!@9t}TY$13V|;J7WEq4np`9 zbP*D+!(_*l!hs;$GP&CO5i~#Yb6|d9GO%+&`1@2tU&H9s*8B>8K~PLHNTbu(XaH*( za;f(TfV_qg&>&NnOZzj;o4%7C0giO+?tEiLTOM5EA!mS{(pu#wRprG*ik;2o(#HU< zRCaAO&vuR_xOS*J^|+Ae0t%YJ7`D5?&S2AF8Cd z>#grS@GyY2Ah!UU1!iL0P%linz-gVj*0!f>wFXq0SUIo^CXVTV_grAnkN}BvfXHPF3$Vha9Me9=?DLKrq{B}5CFdX)GIMRR_b-EB(0iWTK)&{&}lLUe+VBx zRX*OgQl{0O3cK^74bGOqv{cr$d^+7R+E&4ws1}XI43_1BC~&@h8VlO8zz4^??SoPV zVEuyq&!5XIRRAwVy3xocYrb7LzdQx)LHIEi05_>eq}7=y@=C-i2jEd?xfTHqSArLk z&ZJqdZw**nAJu4G!TC6oy5B8_d8m$`))tQPpn5uy*)MiM#_Km!z{@IyLPc!7%u zYurE#3FSBGUvX65iPL4KNuKE8ddf;`2vdN1Wy9l05~QwBti?1hzW&OW)et{@{vTa4 zuEE#qJRB6GAmk3{8$_Ch^~Z#87Em8BS1Ag^Rp=T<%Xv^{;ZP&zmz5i-B#0WisF$B$ z055}$x)O1v25>>Y-|FB#Ig*o!(_sdZKC_Ky`i}KeJ|m9k`;(?<#dxPR5F|9;B#nlu zk9IXwH)TxLD;XnnCraTrM4>4Jg#JZs1I zhxyYl#WM7Du*Ko&fo%hcpQSJ|+cAG2p~Kz8y$d4k64XQ#tN+IoLgLw@59g?gnU!u; zg0ZS4QYeKmQ@>~CNUueFP_kh%U=?R=dzLu(#!SJPMpw#Ey_`9h(TIgOK~f`$os~je z8740?rzWD@B;_gPDf{C+VX?!c&abrQY(4chIbUU89)(1K)RWZJ!Lr1Xc-!RNRBsmb zq?uIrR3q+OHlt_$%J+r0jaZtUQk`0eg33?%ZMO4?$vm0bJo2h zzLQ9oLDx=4OZSm3Rj^NpHp3xfFk?MqQ?S3*N{^rpthcVWS!c+boPIF!!GfhGQY62u ze7}h6x5W2_67e$A;u;;x0=1eka)Si!&#$E$^c%F?Wzfhl?zlY zYAxgEMCVL5tT%i(;-Ma)ZA2Hd|GBZZ1o=*p?rtqNjvYm<;;?zMRsB&aTFTZP;}|0x zD`y96+@u>PFbqqr(2m!xXh^t#AKDxiNxYE=mxz`iZ+B}i@#VQRxx>11yIwe% z!E9d7+^B2>9{HuwB93oxskPKn9)(BWC?cDlC~>S+`&@JRzt7 z<+Te9_e;T2);-V0j~;#=?qDvsyqs)Li(>4+#DGF%G?faK``bZed-YYxLt2TulX z{Ddro++8i(-R?tfJB1ExiOPbf%k}4*)Z7g^o0repcVq1t?G@)R!*7S9&7UkeL@Z8f z=4n>88(Y;JZuH4C$!IROagBW@vg@^@wA){>s426VPh~RveFh(1iC9r*`IoS}r0c7F zwJ>rXwBPpQ{Kt(u+6~wJ-@bnT{oU|3AQJP%n-IJq4`Qf8avHwoKZ- z*W}-FY4`Z_;H0`g&+N0v9431lQXZ2UnjllicaXOT6OCGa)&;X z^(HUb)(|LYx&#Y96FFMxzhpSffr`y}M)>~PX1mKhuSe-c=;crUn+~u4{>s{0)q80> z;7|6`v8P;;nGZgvQRnFyHA9vv1QZFmKHzTmelYwezU{ks_Z#OYv7zLMm++m{o$21{H#U6r z7dgKq%kHKwXDoiUo$lq$7?0P4S;mGm%JMuUUsb4gV~_tF=X_iHc1NL1VLa+8YQd}O zv==iRmHKBjxP#-q{v!EzNv@y!O`9crum@xNdpBKDd9k=%-Qaw30TGHJI@AlY()!t%b=_2UlhX0MO-(ydS=cuUb0ss(@ z|8D>R8Cf&{01dm6ob(3|;JyJ~Eli&B4y=ev!pj=KDn%yc78pVI4$RC>rtyKfk-V3Z z*g@f_F*4$&oiWm5L|QoF+skUah_FCL{0J!kLJl`biZN^te7eZXl|6VX`A1;t&hX=c z``Fzd|H77?wwoO_ckPcerF_f;y+{cA<29{y^oBMKSC!oaR&3#qUL)rKMz^w8%W zh?W5JA2XjT+emaKyJS6*!x0Yxm3O&UaL#wp+0(KlTJgEhDa?4^2Z{p;=<{WB`PG& zdPf<@0@}-$z>W*Mb3{r+@PJH#=`>qIfX#T9Ar?_Mvso?-qijw5m@CBG7++#}JxdOT zgVWEXO_xhwFS2>lxl7VK*5?$~cx&!0ete$glUc^ZBY->_ArEi{{dUv$ahw=aKxhWM z)Pl^4dVv^^3OhSJ2Ut?o6U_UNyQCF4R?4zH>pZLS1H0Vmb?bKB1_bf`1njQ?_C>w@ zUk7+{pQW;l01jivv6&aegfHow%UDXtC{8WH!Z}ry2Rxk|`JKy^Sn=?#b;H(Q;fc)i z@N@GvYRc@3%xk#GuX#x^Rn#~Yl~maNCVDmc2^$Z5Pu7!1Z;RJNav@V2%0*^1GZ&03 z%6@M+R&SN(Rd#M(N3#!~-_+5407H!R(YrYDivFXU-%Fj?o2ZxjO_IwE$t;ac2Wk z_%nV!6eozF^0_*eKCA9D2;>@>E=!b(mFR_G`C)J%#XFN+Niq)$xgmBm+D)DZA3pWm z?K~Tq#%9GL2XMa^#!j2=it)&Xu9;CY>BRU{=E_e8Bi{6i5xG8)qRBNlo{Yn&0h=Iy zAp1)580)I$tE(&^rc{VL?^3Itob=xlEh3b+osZQDEKe)eeDBc7=k_I97Fh$_wRCk4 zA!Y!2aMoajWiP4S`%^g$M$ueCcJ`B!be6nfN0^Zt3u9ko$S9%)Scja%+qN2NHUZ5YsPqVj8zIBC_L zKM-!*uY%wAIcEmsj}0G|ZRxmmo+`#wES{fTb`?kE1f<<>w(*Llysw+X-IHM~%&NV) zj{K0g%29qr`Z_^tSyZ&fxvGt1%Ur5O1Ag_8-NZb^q~@m9GeI)scdQ&pD5|hpxhBTg z=WP?iZkK2}u@jR{E_(nOVmfLctt*zmbF6-Q(6@M10jeUjts*ww|CdPp=Ke$@&;2On zPL%wr)XwDEz|@rJSfgsFtLdyX5ze!4+)|U9L!&$%gLNl z0??kZ2QDT}E+<}u{M|GZB%m2RQ?z<%HkC1oBKg9#6uL_vJmPI;p!a&I+@fFzC%ZoU zYev!Y9J(UpROnOI81e(pIkp-TG~mOoQ~fLZ^5*haLxwq; z_rqqHJ&Hk1D5Z|ClJEY)L*xhEGy8D<+Y8~z{dXwsZniVK2BC;?Vbw`Idx;&R&yY)Y zj!i{x@>7ZW+RIN#7fcq{@n^i0qG<=NNpZ2=)Vlgm7_oaB?)6GzF~RPB zCX6MpZKj|*q-cW9*qD5Bc{`}`BZx2a6Q51mUt-vXZ)5|p{11I0IVx(9%JKnejQ;s# z;gf=Oj^e>#F}phoB~$B)A54atp4-q>{;s*XOwy`Q@26_$nKb9y(b#Nt`oHA90|(l? zV~#TIA>UT0`Jba}dS^iN;5)oGNs(n=r9_}>h)de0xE$%MldRgGrCxPLJ3*&QW+A*9A_PKQy~)QO0TzTM`XUm>P@79P>^Ji;CN5$69eOR!eZ6 zQWb7}~=Mw>@*Vzc2JUPe!_d7AVPWG+JO?`LnARvGJg%OyR|C{8BP zk4~70ozShY0oulq*@vHfXE+r(@EoevNgM3r#c<+^;bS=R8bMe7!`@e(PH>feY@~sa zi93vOI1(5!*GxWgEk`A|N^w#stx{^3=Bl9R8tW>D3lUz8C7{yDaGfWn6YUk_vkIRg zNK7knNf{y~hz3s9rLlpzs=Q3dTwKTAi*)IYOxsIZ(Tj&&_2rTNtW2Y+^n<;( z+(4EEcTBJiCnC)IOxWBl=q***MPGF z>1FeNs1M2`(%T0vF{(F`VlU~D<6%)>+y9bNdtn2Lp=Nz)PdGdzJM)h3a}I6dwFNXg3p!Loue6+O z!f(N4G5zIkKTDPWIaCf#lqMK4c-<~PybDpaGLzShoNM04d6$aA$0kI;X5}`)Gom-5 z6#ZReWE!Y6vK{Hxh+Ed{bLbe;dnwWDlUlW^RCaDWVs1Aj**m-ZJ%<3pxG@?{{%dES zW_GB2Cs}C8THhbF!(p*|Ri(tWKqe@?>#*9v4Iwps41X5P%F4w&mia)G3T{SbssSESGwT62_LVqZ}_{I z(nr?BG)E!>Q*_#R@kN@9)=Jc5-4t{pJw<5L9ju1B8_4|`^irrn0#V&uW*+&Rx{QiTg2%Bp}H#7d2h5{I2NaWU-M@38huO;8 zlzM9Yn)?m2yP~TfiaBSUfZWd;5wE80BUguEn`2$Gd`KLgQYC+wgpMRrs(jZI{Nt}g ze<|iqs|b5{3p0#Fp523AM-9YF)1dKIULmi+o!2#)=ECE6N4J_HTpVCBrUv(Je`1*p zIGFxm?#a8&km6`lCwekR=Ysf>Y82Gb>wpO<`}x{r3k~Nu{ErRK`1vds)x_GS4HOpR zE{2;fvDcL0p5EFS{=_iBQN+iZL1vhlP&2fNrFApQpLpMj?{=V`(1{%pneC|v_C?eD zc*zYJ3u!U@J5K7PNxe8<@Ra3i2j2H&#jef9ax(FePVRO^;SrItaw$^|cWPlhl9lC; z^`!suO1}#4Ph)cI(RX8$tH~?qaIg+<)?~jRPq))XZFM$ZCaKJLSv?6RcJF3b>=;&t+6CG3tOm<}yS%PXi2Yk@ zZY=SOXS1*SeAABO=<|&xQQ{e{PsV;~wVl{aZFhZVdvf@kSspwu)6}21M6T9!oz!& z<r5l))BUT{`cZEy zHRHHob95QI12&p#DOggwt3jwcB+Jj*_LlZC?St^;;9HK8K4i-5 zT}42DR{hN>`>l^_9j4Yj9jJDq52+>JJ@S7wR5mn#V_SkdCfUFJ*ezG9Y+O`AAi z7>i_IzUb7JnBaJl12pV};X&DG4i8mO-JA7A>V8jkkH-T#GS4V<4Mq239NXOO&X3KR zq=``uY^yrsrsLUkfTG(r9F#^M3dM?E?w4ZAf&0b=E;7VDmQ{Ep-w`Zxz=Im0FQKcK#*LxOL@V8MVJ71eA{e*Xzm8!9`v%0& zE~SxvXRP>xd}*1Fq5zzptjp@dy@mB}j(idfEd;msuPQ0n`;&I-!O zdj~%OJX)Tj%Tup{O&NgiY2d#K?i5kJwfc*>*?5!jB)j|!R=s^WXfB}2lgJ?>s1NXx z9PPADyz5gpqb^+o;uSF+w=n1vm}LmCoc@SNbpC>1psqs-wN+pp;Oy|`cs5{LV8Pyd zOXPWOGUk7)(%KV3a!|&)w0UR4EzpSpD!^A=`camF!N|aA&W10LlsY8dD`G5t#&{aS zsNH}gk3(8OZwn!^X?+)1%?o8_+iLDr{aXa0#(HwhkWS;+BlSLzehN^PWiEtMygN~Y zAo(Ew4eB_1z>H4j=$Q#h8zdLCZ1kh>4mO7)$w3sS;{jXP3rT6|ttY@3>63}KtPnB2 zxTw5LR#&4B14Mb}&p>xPYNhjl(6Vphvaf#wr?-dUuNK9gY8=|Jxt->)fLfi!NQ9>* z!u@vY$pn1Yb+&Z_yqX3>M*bIEA1$b^=6dpD(0jwph`aAe3ovqC67^V7!SYrZxgItWobhDx#AU@~fen`)!8RtVc7fNW4X*N_bmDScF1 znB?QzN1wNKIt``^AarblK18}2W<~T8pEDO$A`xf2kvqhqll|mGY!ZVn_$xu@n+1(W|tgGwq+xtP8plsDZdO9y%Hu$NbE2I&B>o)-BBGYc|=S=U? z^2)@UdC-DtU`k#~UA%IxdiSCv;J-rpJr#Rw;afc3r^RrxTCl;7w$s`}*4bU$0Nam6 ze&Sd+D=uKW@F_NxF?Hf5!y80;E z%Qa!N1fvk&IXUqS^(;Z#YmNSseX>&&svZ65({s?RQrg!5fdH83NTc1KeB@iHg}2fV zSX-OJtih(jL|oD{3qyMAC)EjB@=ZuIND7~@SR6vcc}IH5h$QMNeD?@H)4@g7RP#BH zh=_ylQxtbZ+Px9}gF3(iyPD6~{&5fXe<5u7ZW19%P7l~<$7pTo2ZDAd_H7bl1!Q+% zw4*8IumK9jUACSuZ(6NA*xKzsE1;I|xK{^Y(-L$#47d42ci8DM<>*R~x|(bx=#Irl zgY8RJ=?@*7Mn6C4ckLa6>elzbVn)w2Cnu>2my=a+T{5YuhG-}Wjml4^5(-ZgC3FJ znc1Q~okB61(^>uba~y>N|DBHN04bn~D6;>4Q=Mi2K>y=03w&Hm(t}inJaVr0UNH=a zrx&PJEW%0gC{yss;8Kqqk=u;n2M6A>pj{<=7W|xMBZ%++@15~~k;n*jrNue}z+BQQ z1bBRm(kduCA%HG_FQ}x7l}jgy(Tn8(IdqK#aLR3?7OfT&&$DggrGZ@ zZsASZ&9z3*N3$)O6E}n{(I6M7tpT7ch_ro*tZN#^FPP z7KD9E3s^xeDRWiV!|~v(0tK5h;z;L;zF;?hp$fe;3+PxCq81)4@|JFW!=uRYLJlg| zCH2d4D>?Bk(;(s>1*nroQ4`e$WdBL_XD2~$FmIfsIGxcD?I7$MRs1M5-N zpP^C60L#bub7nP$aD*DvgQ7O*ABUEgI`kA7!1kyRXI5Vk7bQMF@K>4 znFpQZu)_Dum3}&T1eb!Ij!8XzH1ysy@{T~{L4zJM!vuvMIr(78_&=we)?Hja4FV7> zRDynMv#Nhx*ujm<@TUeJBjnlwZ47IIZhf4zG+E;Cohbx80x3>LII-XPB3Y35c?PrY z=(a25%RH*j1u5@87Sjx5BF;$eKOsE=hSA&*2Z-4tfH2@=sGa~C643wu0-c3;08+pc WcCOs$s~$fR07~*|a^*6{f&UME*7%zM diff --git a/src/GUI/ndb-icon.png b/src/GUI/ndb-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3d0fd3fdf858dc18e40648c330429c4d9893b730 GIT binary patch literal 4463 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 BjZ6Rl literal 0 HcmV?d00001 diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index 4cbca391d..c48a7d40f 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -1,9 +1,19 @@ history-icon.png - large-search-icon.png arrow-right-icon.png arrow-left-icon.png ../../icons/128x128/apps/flightgear.png + spinner.gif + heliport-icon.png + seaport-icon.png + vor-icon.png + vortac-icon.png + waypoint-icon.png + ndb-icon.png + airport-icon.png + airport-tower-icon.png + vor-dme-icon.png + seaport-tower-icon.png - \ No newline at end of file + diff --git a/src/GUI/seaport-icon.png b/src/GUI/seaport-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..61755fe25d5a9e1a91d13b3d6b8ecf248abb89e1 GIT binary patch literal 1088 zcmV-G1i$-P000>X1^@s6#OZ}&00004b3#c}2nYxW zd3iUy46l$$lv0k8SPTbuV>1G!jsG=Z3i>cOvVhf5@kqQ>2UP3pu zNt)!G@u6pvlg%bs@x^(Yng4(OGv9aS;|Nunvmv_`)t5@XKrAs7-&+mWR4ejdtSXUI zG;_5;M=+*HhZAv{s^QC0Nykp#hsZYI#zi1Azz3e;ncn^JZ~v7@Dw0}hN!_~$cURLX z;G*;=^0DoL=&~YC#A(8WJ_T+FgvWu8RrG{WGX|_u(OXeE2p+k}JIP!uQC@T2o%DAd z3W?Md0s;!W>6+TM-&b{KP{LfAIaCEx-B5Tef}Qk z9SBFP25kX6W63&zzPJ0P!(KxY^q3E~DHLiN3 zVE+veDv5-3#V2DjJ_B6FYvZn>b$+#|+#^D(dK_14I%}y_>w0Fl2;G4)FDHlM z^WLuouW&Y!ZJ9QD6le{E&!{jAd<{}G>NP874d`7!eE|1<*lX;z3%0wY2tewAea+VN z%rHntrFP000>X1^@s6#OZ}&00004b3#c}2nYxW zdt?tGM zUcnSeeG#n&N{fnCA<|?w>uzFOvBi3UO7V_}5gSm^pp9Q_YHOfk3yM(@UqtD}K-1KQ z&7LzpWV5#H{v``gTRwN=5#QRVI=ezDAVh z5^pn!q1sB|Qf||x!g~~Y1#Va*Bm;b6ReRF$)*t?z$gVFgx9Q+rggYzA6mTjee0QWD z5nWm&F;r`8^e{aoOr7o9X-R;zF`XfsH@gwk1w;QCtm}(KTDZ*xa)}B^6UIc5(8BNy*YKIzhn+>1CBVS^&s9U zZ2)X(oN?}KCoq=Ht;!<31-QIOU{^(ex~#+>nHAN~76z&?3ff|=j)M;@9LXoxkrtIF z3L3PLy+<#}ahqiL$ZR{=IM+PEzt8rTi$#4E{T<~`kaehzm0p=lJaQ~ZhT|$U0GHYe z>D`(d`4J!x;U!8;k)x$PkgY&{R%+7+JivFIL?{BoO!yeKMBV>9QRwS%!#<=Rfp__ z^aN-vaP_Q!{sM9k^>3Qg$Q7ur05JGE=?mXLxfkI;M*W2K_Jw?X@+pbP)Z8k6Cfi#s zs_4#K80^HG8Rx#bPgM2_x)0%20L46sN~msMHs_aU_B2vwfT#3M!DulZl$zTtYS<`}TH zz?L*R-f;oc!Se`ku2s!H%|{m!equ tN1~&{W#W-hg+oPtWvCK?%0&JL`v>N{X-Xrb{Wbsq002ovPDHLkV1lHu@qhpT literal 0 HcmV?d00001 diff --git a/src/GUI/spinner.gif b/src/GUI/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4 GIT binary patch literal 1849 zcma*odr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSbGzm0!x7^P##Mnh7t-jP!X0Q zk_SQ}Po-L1tlDK;6l?(>v)e5ZBQx4|Y-Q?nr@Px3?9h(3ZWr3^tj=`TP57gKr87N$ zp2wWee1GRRCwo_xahnw)5cxNPJbCg2L6DV|6`#+yw6v6!mDS$f9-JvFD^n;GQ&UrZ zzh5jCkByB101O60U0q#p_1BM>Cv-vP?&s4@g_((4_1L=L$(a91)0=J91Gas#R{McE znYG^9*0A5YZ>#;~+Wkn(W5B0^yELIYLP!K}mB~<)AM@1&nqekynuaEGqPrzoH|KodRXJy)%+w_fu3nE5>@Bd_b zqC$EQ;{c`T&?EsNO|igL9gC7Ygxv?aQUEXMq?~>wg{EyW;VcJ37CUF#HjrT=KQO_* zS>M9yydXk18D(+QDJ1>r);Lav_uYKp$T?4vr{Q$lTo&pKv^?(>L-)G2*lwH!Ah7k? z7oH<8h-(KTKt5V6$8gF)C7Io&P5=SjTh)=zV=E2EUhQZP##L8S{d%UK>>+y82>+FV+#^BzW7u3F)Bb>=lYQ%%j`F>ASe zo*cw@V#u6T`A2He;70mR(V&iV&-7{qP~=SRf&jm9-T{*ZeZ}$rd0#6c&fLG^xJcf5 z+p<`wJYgW+_s*V{uI$nMB;%8`S_3>PfGOj3Rq}@Cx^+j?rk92fANSFDBYnOqQ>Vdj z)(|$AhP4t&Lb=Gvo2#3Gl%9<=Gv`Mz?Po@P4iLF!x}GUWJICDlFk-hS^Whyh7x~VH z@0vD1>HYD4&e+~yzS*-sFR{9`{QEEZO1zg7>R&7cHts-6j!xHVdA8eI+ZlVzd%`es zJT@$#GX(gvCJ1oJN%yLBK}{V=V;seo;!w|Yte!W1%5qLNFWqvZW>h&IiH+oPT=b@E zPhGzv5=(Un*X>v`>%8h_nj^NdYcE6NHS_ifkCV$*D)Tqrbu`s;<=t<4 zAHNqNV?6(g<1PY-w@#I-WYFViz?9TrkMr)u0g`O`u|>T;k|2sV*YF^punvT;$SuTy{j3Gv)yqD!R_CF>yR)MzmmYS5v+~R zXAdD%ng9?df;wd8GxR#%3O+gz};Vo;)sK%Bj-q>Oq%R7JU-KD?vYu>#2UjaDo z&8$>5xW~?KPD_#XFToU1hIb*VOMidUr6iYiO0N|i-7s`T8!cFT`rN!^1Pt78J93i6 z5HI1wIM$94m{3SLDvISDe6$ZG1;eq_D9RTaaC>=cO{@Bs>$IlPCPJJ$h$)-3vzNUQ6OsN#_zWxey!_9%hxwH2_dEJi=yY|1c7nDm2_Lm!Cof8-R_+9UkS zcBE(o47yE)oMR(Q=dp1a2wTX5KvvGyLqlWTa7V&!A*|w|)ax~1_~aJ0=_Lilg*0iQk7#ZD EAHN$8j{pDw literal 0 HcmV?d00001 diff --git a/src/GUI/vor-dme-icon.png b/src/GUI/vor-dme-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e5e5a48f160ace1c65ba1c78ab9972296541d914 GIT binary patch literal 754 zcmV4R@JocNnOM{NLa$AYjU#RvH8)n;?TS@7>PnTVB*;Ml z6e?AH0ytObH7e~M^A_^|SSFX^jguv~2<$J`OpykVi8p=#nOXz(^%Xs1M@pF$|EWk* zW=eVKAFTsDxC$11?nMVtl|rvkNs7oqK3kCmgLaqVS{FP79nOOt<|$BX`@&1b*kVg|n{SI(rO}4p;T3u4Kz?6v_0Kz}8Hua^D5+ zopttO{(?9+K7{H|Gr#TTe?b8g)ZF&)+Z~u2%4@4%O?vTiN0d&IVRhNmzR_Kx+%>B0 zw6^9ucHI!lCL$ro8yk=)muuDD172n0(ebtV%FWBqW9LmRkQR}ch|X^WRd04G+9gI? k41RX`{PH}O&3Zxp0SmePMlHaji2wiq07*qoM6N<$f{lhtGynhq literal 0 HcmV?d00001 diff --git a/src/GUI/vor-icon.png b/src/GUI/vor-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..111b014cf12f591f5b6792c161e2ff1dd31363fb GIT binary patch literal 758 zcmV#z{5=Hc^b!F;C_Q^$2}dLqJLNwR8O|RTL6S(@RL!RlY<4Bp zSqy^nZ^_aJcQ7_U$Q0Tb_MJeF!fzhVwS5M4$($=x0EmGjzlNw4|gv>o!YlT^XucPbQ^5fHoTe-3I7#Bwc6^ zFvF4~&_c~YP)W3LIzV*t2bFM>)!Z^jMZc2LgpiI0jCT`@40{fsX(IgrP>x~G2l@$s znI;H$ygC^DNR=wPn5hV|*CpBcy8fI0ADo#v1JF|myRGQ!Dz6W}6aNjh{eXFZ$*G2I z5hte_2s8$wUi5X9fvPV`Qt_8iEtzs-&vuxmyyK37N-_|A*v2=W%(1fuV&9^8b$MCuML&T)g2`9(#qs(1tcTm!bH2JhSX zpEk08%jddLTzc5#y_t-F^;Lc4rgi`RCT5_J6_w*D+k~~OCJdg}g_bLfmU$jyKwC9i zvc6Gitqu??uwxn;hQ|2qdqvGwR3}CCLek%Pr>F%lBp2;e7zMmCz@YO@k_mbd*$I-A zwq=q`kS`DUX5%SQIS1N_uvLxR_H)suvYupuyozQ^uTvo1Nhatu0QOkNRfG0|esL{& zdFHAk%_{6m1t6*%tZHDMfxI-T8_J)YDT{F?IETnR|Dzn8v9z@Ub851cicC-4{Q=c8 z;#_x}?R+#7M@1Rj5f$A-nE$bq;X8%LwOqYD9o6Uf1j<;_-+8N=RZ;P=t_L7DJrc)7wb$a0$reyyh$XQDqqPFp+m6XE9fY2DN_S(}6)?f&sX zS6ym2x)2yr)xLVrh9?H_bJ1CcZUY&Tnx7E6RAi}J@%Cr7Z?|>*jlig07m)+NpGfU~ z4;gk1pwJ)+0OPq24VMC{`s!PHL+}?N`Id3EY6yn!1AhheybkZukpCtC0000QGhs3ZiD<-PZu7S&)gb7#)v{C4Mk z-`{f{u#7QA^2JKY^Nh0XSRXzwZA~N0CvI%rx9y0{k+<=w#|e_31qOkmP}GRGaO+#E zo|U;1CbNIQ05@F|J+|q!G|!hR0~mIJo=f8&juWPzJRCc0*0nH~S2bXO?l5v!TQ-|5 z^QFoJhRygpGr7EY=VM$y*4*=q!tkc4fKc##-)S43NNvvmBV&8V=?C$DH^}PS z-QM7Pu!qp!6#hFWZsO(+jMV&zpq*4hLEbSXD_Sv&m4{8@2(N<_bJ6I_iE?&TIn-~D}8&3)f@M0DYQ z!2n`-?Rg*EI{5x+W)2v=3LMBdsnl%sGk}%#^0i3IK9IU19N zfa{uI89-hX%OhH(3?w=P2M9IluF^`abr=lbD$#{sVT5|Y%xb^900000NkvXXu0mjf DZ$$S8 literal 0 HcmV?d00001 diff --git a/src/Main/positioninit.cxx b/src/Main/positioninit.cxx index 59000c10b..681296687 100644 --- a/src/Main/positioninit.cxx +++ b/src/Main/positioninit.cxx @@ -353,37 +353,51 @@ static void fgSetDistOrAltFromGlideSlope() { // Set current_options lon/lat given an airport id and heading (degrees) -static bool fgSetPosFromNAV( const string& id, const double& freq, FGPositioned::Type type ) +static bool fgSetPosFromNAV( const string& id, + const double& freq, + FGPositioned::Type type, + PositionedID guid) { - FGNavList::TypeFilter filter(type); - const nav_list_type navlist = FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter ); - - if (navlist.empty()) { - SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = " - << id << ":" << freq ); - return false; - } - - if( navlist.size() > 1 ) { - std::ostringstream buf; - buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl; - for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) { - // NDB stored in kHz, VOR stored in MHz * 100 :-P - double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0; - string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz"; - buf << (*it)->ident() << " " - << std::setprecision(5) << (double)((*it)->get_freq() * factor) << " " - << (*it)->get_lat() << "/" << (*it)->get_lon() - << endl; + FGNavRecord* nav = 0; + + + if (guid != 0) { + nav = FGPositioned::loadById(guid); + if (!nav) + return false; + } else { + FGNavList::TypeFilter filter(type); + const nav_list_type navlist = FGNavList::findByIdentAndFreq( id.c_str(), freq, &filter ); + + if (navlist.empty()) { + SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate NAV = " + << id << ":" << freq ); + return false; + } + + if( navlist.size() > 1 ) { + std::ostringstream buf; + buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl; + for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) { + // NDB stored in kHz, VOR stored in MHz * 100 :-P + double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0; + string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz"; + buf << (*it)->ident() << " " + << std::setprecision(5) << (double)((*it)->get_freq() * factor) << " " + << (*it)->get_lat() << "/" << (*it)->get_lon() + << endl; + } + + SG_LOG( SG_GENERAL, SG_ALERT, buf.str() ); + return false; + } + + // nav list must be of length 1 + nav = navlist[0]; } - - SG_LOG( SG_GENERAL, SG_ALERT, buf.str() ); - return false; - } - - FGNavRecord *nav = navlist[0]; - fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg")); - return true; + + fgApplyStartOffset(nav->geod(), fgGetDouble("/sim/presets/heading-deg")); + return true; } // Set current_options lon/lat given an aircraft carrier id @@ -429,7 +443,7 @@ static bool fgSetPosFromCarrier( const string& carrier, const string& posid ) { } } -// Set current_options lon/lat given an airport id and heading (degrees) +// Set current_options lon/lat given a fix ident and GUID static bool fgSetPosFromFix( const string& id, PositionedID guid ) { FGPositioned* fix = NULL; @@ -558,14 +572,14 @@ bool initPosition() if ( !set_pos && !vor.empty() ) { // a VOR is requested - if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR ) ) { + if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR, navaidId ) ) { set_pos = true; } } if ( !set_pos && !ndb.empty() ) { // an NDB is requested - if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB ) ) { + if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB, navaidId ) ) { set_pos = true; } } -- 2.39.5