From 35a368b3a27d39832537fdfc86000c6da1777480 Mon Sep 17 00:00:00 2001 From: Alexander Barthel Date: Tue, 26 Oct 2021 21:28:41 +0200 Subject: [PATCH] Added airport MSA (minimum sector altitude) display to map (symbols with all sectors), tooltip, map context menu for show information and information display. #498 --- littlenavmap.pro | 2 + littlenavmap.qrc | 1 + resources/icons/msa.svg | 212 ++++++++++++++++++++ src/common/htmlinfobuilder.cpp | 78 ++++++++ src/common/htmlinfobuilder.h | 11 + src/common/mapcolors.cpp | 8 + src/common/mapcolors.h | 4 + src/common/mapflags.cpp | 2 + src/common/mapflags.h | 2 +- src/common/mapresult.cpp | 89 ++++++++- src/common/mapresult.h | 17 +- src/common/maptypes.cpp | 143 ++++++------- src/common/maptypes.h | 80 +++++++- src/common/maptypesfactory.cpp | 64 ++++-- src/common/maptypesfactory.h | 20 +- src/common/symbolpainter.cpp | 133 ++++++++++++- src/common/symbolpainter.h | 17 +- src/gui/mainwindow.cpp | 7 +- src/gui/mainwindow.ui | 26 ++- src/info/infocontroller.cpp | 135 +++++-------- src/info/infocontroller.h | 5 + src/mapgui/mapcontextmenu.cpp | 9 +- src/mapgui/maplayer.cpp | 28 +++ src/mapgui/maplayer.h | 26 +++ src/mapgui/mapscreenindex.cpp | 6 +- src/mapgui/maptooltip.cpp | 331 +++++-------------------------- src/mapgui/maptooltip.h | 8 +- src/mapgui/mapwidget.cpp | 6 +- src/mappainter/mappainterils.h | 2 +- src/mappainter/mappaintermsa.cpp | 111 +++++++++++ src/mappainter/mappaintermsa.h | 46 +++++ src/mappainter/mappaintlayer.cpp | 28 ++- src/mappainter/mappaintlayer.h | 2 + src/navapp.cpp | 10 + src/navapp.h | 2 + src/query/mapquery.cpp | 235 +++++++++++++++------- src/query/mapquery.h | 31 ++- src/route/routecontroller.cpp | 1 - 38 files changed, 1338 insertions(+), 600 deletions(-) create mode 100644 resources/icons/msa.svg create mode 100644 src/mappainter/mappaintermsa.cpp create mode 100644 src/mappainter/mappaintermsa.h diff --git a/littlenavmap.pro b/littlenavmap.pro index 8ac45e987..859501af4 100644 --- a/littlenavmap.pro +++ b/littlenavmap.pro @@ -298,6 +298,7 @@ SOURCES += \ src/mappainter/mappainteraltitude.cpp \ src/mappainter/mappainterils.cpp \ src/mappainter/mappaintermark.cpp \ + src/mappainter/mappaintermsa.cpp \ src/mappainter/mappainternav.cpp \ src/mappainter/mappainterroute.cpp \ src/mappainter/mappaintership.cpp \ @@ -471,6 +472,7 @@ HEADERS += \ src/mappainter/mappainteraltitude.h \ src/mappainter/mappainterils.h \ src/mappainter/mappaintermark.h \ + src/mappainter/mappaintermsa.h \ src/mappainter/mappainternav.h \ src/mappainter/mappainterroute.h \ src/mappainter/mappaintership.h \ diff --git a/littlenavmap.qrc b/littlenavmap.qrc index 17798be2f..e2ea1c4f0 100644 --- a/littlenavmap.qrc +++ b/littlenavmap.qrc @@ -291,5 +291,6 @@ resources/icons/userpoint_Other.svg resources/icons/gls.svg resources/icons/enroutehold.svg + resources/icons/msa.svg diff --git a/resources/icons/msa.svg b/resources/icons/msa.svg new file mode 100644 index 000000000..679698c9c --- /dev/null +++ b/resources/icons/msa.svg @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + 12 + 30 + 24 + diff --git a/src/common/htmlinfobuilder.cpp b/src/common/htmlinfobuilder.cpp index 3f9f42a65..28796cefd 100644 --- a/src/common/htmlinfobuilder.cpp +++ b/src/common/htmlinfobuilder.cpp @@ -60,6 +60,7 @@ #include #include #include +#include // Use % to concatenate strings faster than + #include @@ -1727,6 +1728,73 @@ void HtmlInfoBuilder::weatherText(const map::WeatherContext& context, const MapA } // if(info) } +void HtmlInfoBuilder::airportMsaText(const map::MapAirportMsa& msa, atools::util::HtmlBuilder& html) const +{ + html.img(QIcon(":/littlenavmap/resources/icons/msa.svg"), QString(), QString(), symbolSizeTitle); + html.nbsp().nbsp(); + + QString title; + if(msa.navType == map::AIRPORT) + title.append(tr("%1 %2").arg(tr("Airport")).arg(msa.navIdent)); + else + { + if(!msa.navIdent.isEmpty()) + { + if(msa.navType == map::WAYPOINT) + title.append(tr("%1 %2").arg(tr("Waypoint")).arg(msa.navIdent)); + else if(msa.navType == map::VOR) + title.append(tr("%1 %2").arg(map::vorType(msa.vorDmeOnly, msa.vorHasDme, msa.vorTacan, msa.vorVortac)).arg(msa.navIdent)); + else if(msa.navType == map::NDB) + title.append(tr("%1 %2").arg(tr("NDB")).arg(msa.navIdent)); + else if(msa.navType == map::RUNWAYEND) + title.append(tr("%1 %2").arg(tr("Runway")).arg(msa.navIdent)); + } + + if(!msa.airportIdent.isEmpty()) + title.append(tr(" (%1 %2)").arg(tr("Airport")).arg(msa.airportIdent)); + } + + navaidTitle(html, tr("MSA %1at %2").arg(msa.multipleCode % tr(" ")).arg(title)); + + if(info) + { + // Add map link if not tooltip + html.nbsp().nbsp(); + html.a(tr("Map"), QString("lnm://show?lonx=%1&laty=%2"). + arg(msa.position.getLonX()).arg(msa.position.getLatY()), ahtml::BOLD | ahtml::LINK_NO_UL); + } + html.table(); + html.row2(tr("Radius:"), Unit::distNm(msa.radius)); + html.row2(tr("Bearing and alt. units:"), msa.trueBearing ? tr("°T, ") : tr("°M, ") % Unit::getUnitAltStr()); + if(info) + html.row2(tr("Magnetic declination:"), map::magvarText(msa.magvar)); + if(msa.altitudes.size() == 0) + html.row2(tr("No altitude")); + else if(msa.altitudes.size() == 1) + html.row2(tr("Minimum altitude:"), Unit::altFeet(msa.altitudes.at(0))); + html.tableEnd(); + + if(verbose) + { + if(msa.altitudes.size() > 1) + { + QFont font; + if(info) + font = NavApp::getTextBrowserInfoFont(); + else + font = QToolTip::font(); + + int actualSize = 0; + float sizeFactor = info ? (msa.altitudes.size() > 1 ? 5. : 2.) : (msa.altitudes.size() > 1 ? 4. : 2.); + QIcon icon = SymbolPainter().createAirportMsaIcon(msa, QToolTip::font(), sizeFactor, &actualSize); + html.p().img(icon, QString(), QString(), QSize(actualSize, actualSize)).pEnd(); + } + } + + if(info) + html.br(); +} + void HtmlInfoBuilder::decodedMetars(HtmlBuilder& html, const atools::fs::weather::MetarResult& metar, const map::MapAirport& airport, const QString& name, bool mapDisplay) const { @@ -2256,6 +2324,11 @@ void HtmlInfoBuilder::trafficPatternText(const TrafficPattern& pattern, atools:: html.br(); } +void HtmlInfoBuilder::userpointTextInfo(const MapUserpoint& userpoint, HtmlBuilder& html) const +{ + userpointText(userpoint, html); +} + bool HtmlInfoBuilder::userpointText(MapUserpoint userpoint, HtmlBuilder& html) const { if(!userpoint.isValid()) @@ -2337,6 +2410,11 @@ bool HtmlInfoBuilder::userpointText(MapUserpoint userpoint, HtmlBuilder& html) c } } +void HtmlInfoBuilder::logEntryTextInfo(const MapLogbookEntry& logEntry, HtmlBuilder& html) const +{ + logEntryText(logEntry, html); +} + bool HtmlInfoBuilder::logEntryText(MapLogbookEntry logEntry, HtmlBuilder& html) const { if(!logEntry.isValid()) diff --git a/src/common/htmlinfobuilder.h b/src/common/htmlinfobuilder.h index 6fbdb4904..922054042 100644 --- a/src/common/htmlinfobuilder.h +++ b/src/common/htmlinfobuilder.h @@ -38,6 +38,7 @@ class QFileInfo; namespace map { struct MapAirport; +struct MapAirportMsa; struct MapVor; struct MapNdb; struct MapWaypoint; @@ -168,6 +169,14 @@ class HtmlInfoBuilder void weatherText(const map::WeatherContext& context, const map::MapAirport& airport, atools::util::HtmlBuilder& html) const; + /* + * Creates a HTML description for a VOR station. + * @param vor + * @param html Result containing HTML snippet + * @param background Background color for icons + */ + void airportMsaText(const map::MapAirportMsa& msa, atools::util::HtmlBuilder& html) const; + /* * Creates a HTML description for a VOR station. * @param vor @@ -199,9 +208,11 @@ class HtmlInfoBuilder /* Description for user defined points */ bool userpointText(map::MapUserpoint userpoint, atools::util::HtmlBuilder& html) const; + void userpointTextInfo(const map::MapUserpoint& userpoint, atools::util::HtmlBuilder& html) const; /* Description for logbook entries */ bool logEntryText(map::MapLogbookEntry logEntry, atools::util::HtmlBuilder& html) const; + void logEntryTextInfo(const map::MapLogbookEntry& logEntry, atools::util::HtmlBuilder& html) const; /* * Creates a HTML description of an airway. For info this includes all waypoints. diff --git a/src/common/mapcolors.cpp b/src/common/mapcolors.cpp index b2fb1557e..b8e9c80bd 100644 --- a/src/common/mapcolors.cpp +++ b/src/common/mapcolors.cpp @@ -59,6 +59,10 @@ QColor glsTextColor(20, 20, 20); QPen glsCenterPen(Qt::darkGray, 1.5, Qt::DashLine); QColor glsSymbolColor(Qt::darkGray); +QColor msaFillColor("#d0ffffff"); +QColor msaTextColor(Qt::darkGray); +QColor msaSymbolColor(Qt::darkGray); + QColor waypointSymbolColor(200, 0, 200); QColor airwayVictorColor("#969696"); // 1. @@ -639,6 +643,10 @@ void syncColors() syncColor(colorSettings, "GlsTextColor", glsTextColor); syncPen(colorSettings, "GlsCenterPen", glsCenterPen); + syncColor(colorSettings, "MsaTextColor", msaTextColor); + syncColorArgb(colorSettings, "MsaFillColor", msaFillColor); + syncColor(colorSettings, "MsaSymbolColor", msaSymbolColor); + syncColor(colorSettings, "WaypointColor", waypointSymbolColor); syncColor(colorSettings, "HoldingColor", holdingColor); colorSettings.endGroup(); diff --git a/src/common/mapcolors.h b/src/common/mapcolors.h index 6b7a6bf72..0a47a7474 100644 --- a/src/common/mapcolors.h +++ b/src/common/mapcolors.h @@ -79,6 +79,10 @@ extern QColor compassRoseColor; extern QColor compassRoseTextColor; extern QColor distanceColor; +extern QColor msaFillColor; +extern QColor msaTextColor; +extern QColor msaSymbolColor; + /* Weather icon colors */ extern QColor weatherBackgoundColor; extern QColor weatherWindColor; diff --git a/src/common/mapflags.cpp b/src/common/mapflags.cpp index 6fad3b115..d3d3f50af 100644 --- a/src/common/mapflags.cpp +++ b/src/common/mapflags.cpp @@ -39,6 +39,8 @@ QDebug operator<<(QDebug out, const map::MapTypes& type) { if(type.testFlag(AIRPORT)) flags.append("AIRPORT"); + if(type.testFlag(AIRPORT_MSA)) + flags.append("AIRPORT_MSA"); if(type.testFlag(AIRPORT_HARD)) flags.append("AIRPORT_HARD"); if(type.testFlag(AIRPORT_SOFT)) diff --git a/src/common/mapflags.h b/src/common/mapflags.h index 0e4e7d4c4..033755c22 100644 --- a/src/common/mapflags.h +++ b/src/common/mapflags.h @@ -76,7 +76,7 @@ enum MapType AIRCRAFT = 1 << 14, /* Simulator user aircraft */ AIRCRAFT_AI = 1 << 15, /* AI or multiplayer simulator aircraft */ AIRCRAFT_AI_SHIP = 1 << 16, /* AI or multiplayer simulator ship */ - // 1 << 17 + AIRPORT_MSA = 1 << 17, /* Minimum safe altitude for airports and navaids */ USERPOINTROUTE = 1 << 18, /* Flight plan user waypoint */ PARKING = 1 << 19, RUNWAYEND = 1 << 20, diff --git a/src/common/mapresult.cpp b/src/common/mapresult.cpp index 21877077f..d4009f35c 100644 --- a/src/common/mapresult.cpp +++ b/src/common/mapresult.cpp @@ -31,6 +31,12 @@ MapResult& MapResult::clear(const MapTypes& types) airportIds.clear(); } + if(types.testFlag(map::AIRPORT_MSA)) + { + airportMsa.clear(); + airportMsaIds.clear(); + } + if(types.testFlag(map::WAYPOINT)) { waypoints.clear(); @@ -96,13 +102,30 @@ MapResult& MapResult::clear(const MapTypes& types) return *this; } -template -void MapResult::clearAllButFirst(QList& list) +template +void MapResult::clearAllButFirst(QList& list) { while(list.size() > 1) list.removeLast(); } +template +void MapResult::removeInvalid(QList& list, QSet *ids) +{ + if(ids != nullptr) + { + for(const TYPE& type : list) + { + if(!type.isValid()) + ids->remove(type.id); + } + } + + list.erase(std::remove_if(list.begin(), list.end(), [](const TYPE& type) -> bool { + return !type.isValid(); + }), list.end()); +} + MapResult& MapResult::clearAllButFirst(const MapTypes& types) { if(types.testFlag(map::AIRPORT)) @@ -111,6 +134,12 @@ MapResult& MapResult::clearAllButFirst(const MapTypes& types) airportIds.clear(); } + if(types.testFlag(map::AIRPORT_MSA)) + { + clearAllButFirst(airportMsa); + airportMsaIds.clear(); + } + if(types.testFlag(map::WAYPOINT)) { clearAllButFirst(waypoints); @@ -287,6 +316,8 @@ QString MapResult::objectText(MapTypes navType, int elideName) const QString navaidStr; if(navType == map::AIRPORT && hasAirports()) navaidStr = map::airportTextShort(airports.first(), elideName); + if(navType == map::AIRPORT_MSA && hasAirportMsa()) + navaidStr = map::airportMsaTextShort(airportMsa.first()); else if(navType == map::VOR && hasVor()) navaidStr = map::vorText(vors.first(), elideName); else if(navType == map::NDB && hasNdb()) @@ -300,6 +331,32 @@ QString MapResult::objectText(MapTypes navType, int elideName) const return navaidStr; } +void MapResult::removeInvalid() +{ + removeInvalid(airports, &airportIds); + removeInvalid(runwayEnds); + removeInvalid(towers); + removeInvalid(parkings); + removeInvalid(helipads); + removeInvalid(waypoints, &waypointIds); + removeInvalid(vors, &waypointIds); + removeInvalid(ndbs, &ndbIds); + removeInvalid(markers); + removeInvalid(ils); + removeInvalid(airways); + removeInvalid(airspaces); + removeInvalid(userpointsRoute); + removeInvalid(userpoints, &userpointIds); + removeInvalid(logbookEntries); + removeInvalid(aiAircraft); + removeInvalid(onlineAircraft, &onlineAircraftIds); + removeInvalid(trafficPatterns); + removeInvalid(rangeMarkers); + removeInvalid(holdings, &holdingIds); + removeInvalid(airportMsa, &airportMsaIds); + removeInvalid(procPoints); +} + void MapResult::clearNavdataAirspaces() { QList::iterator it = std::remove_if(airspaces.begin(), airspaces.end(), @@ -330,6 +387,8 @@ const atools::geo::Pos& MapResult::getPosition(const std::initializer_list& types) const { if(type == map::AIRPORT) return airports.first().ident; + + if(type == map::AIRPORT_MSA) + return airportMsa.first().navIdent; else if(type == map::WAYPOINT) return waypoints.first().ident; else if(type == map::VOR) @@ -420,6 +482,12 @@ bool MapResult::getIdAndType(int& id, MapTypes& type, type = t; break; } + else if(t == map::AIRPORT_MSA) + { + id = airportMsa.first().getId(); + type = t; + break; + } else if(t == map::WAYPOINT) { id = waypoints.first().getId(); @@ -515,6 +583,8 @@ MapResult& MapResult::fromMapBase(const MapBase *base) { if(base->getType().testFlag(map::AIRPORT)) airports.append(base->asObj()); + else if(base->getType().testFlag(map::AIRPORT_MSA)) + airportMsa.append(base->asObj()); else if(base->getType().testFlag(map::WAYPOINT)) waypoints.append(base->asObj()); else if(base->getType().testFlag(map::VOR)) @@ -551,6 +621,7 @@ int MapResult::size(const MapTypes& types) const { int totalSize = 0; totalSize += types.testFlag(map::AIRPORT) ? airports.size() : 0; + totalSize += types.testFlag(map::AIRPORT_MSA) ? airportMsa.size() : 0; totalSize += types.testFlag(map::WAYPOINT) ? waypoints.size() : 0; totalSize += types.testFlag(map::VOR) ? vors.size() : 0; totalSize += types.testFlag(map::NDB) ? ndbs.size() : 0; @@ -579,6 +650,13 @@ QDebug operator<<(QDebug out, const map::MapResult& record) out << obj.id << obj.ident << obj.routeIndex << ","; out << "]"; } + if(!record.airportMsa.isEmpty()) + { + out << "AirportMSA["; + for(const map::MapAirportMsa& obj : record.airportMsa) + out << obj.id << obj.airportIdent << obj.navIdent << ","; + out << "]"; + } if(!record.runwayEnds.isEmpty()) { out << "RunwayEnd["; @@ -688,6 +766,11 @@ MapResultIndex& MapResultIndex::add(const MapResult& resultParam, const MapTypes result.airports.append(resultParam.airports); addAll(result.airports); } + if(types.testFlag(AIRPORT_MSA)) + { + result.airportMsa.append(resultParam.airportMsa); + addAll(result.airportMsa); + } if(types.testFlag(RUNWAYEND)) { result.runwayEnds.append(resultParam.runwayEnds); @@ -783,6 +866,8 @@ MapResultIndex& MapResultIndex::addRef(const MapResult& resultParam, const MapTy { if(types.testFlag(AIRPORT)) addAll(resultParam.airports); + if(types.testFlag(AIRPORT_MSA)) + addAll(resultParam.airportMsa); if(types.testFlag(RUNWAYEND)) addAll(resultParam.runwayEnds); if(types.testFlag(PARKING)) diff --git a/src/common/mapresult.h b/src/common/mapresult.h index 5a427be8a..b175eabf0 100644 --- a/src/common/mapresult.h +++ b/src/common/mapresult.h @@ -74,6 +74,9 @@ struct MapResult QList holdings; /* Either used defined hold or enroute hold */ QSet holdingIds; /* Ids used to deduplicate */ + QList airportMsa; + QSet airportMsaIds; /* Ids used to deduplicate */ + QList procPoints; /* true if none of the types exists in this result */ @@ -116,6 +119,11 @@ struct MapResult return !airports.isEmpty(); } + bool hasAirportMsa() const + { + return !airportMsa.isEmpty(); + } + bool hasAirways() const { return !airways.isEmpty(); @@ -187,9 +195,14 @@ struct MapResult QString objectText(MapTypes navType, int elideName = 1000) const; + void removeInvalid(); + private: - template - void clearAllButFirst(QList& list); + template + void clearAllButFirst(QList& list); + + template + void removeInvalid(QList& list, QSet *ids = nullptr); }; diff --git a/src/common/maptypes.cpp b/src/common/maptypes.cpp index 4a1180df4..d9e71b5fa 100644 --- a/src/common/maptypes.cpp +++ b/src/common/maptypes.cpp @@ -1320,56 +1320,24 @@ QDataStream& operator<<(QDataStream& dataStream, const map::DistanceMarker& obj) QDataStream& operator>>(QDataStream& dataStream, TrafficPattern& obj) { - dataStream >> obj.airportIcao - >> obj.runwayName - >> obj.color - >> obj.turnRight - >> obj.base45Degree - >> obj.showEntryExit - >> obj.runwayLength - >> obj.downwindDistance - >> obj.baseDistance - >> obj.courseTrue - >> obj.magvar - >> obj.position; + dataStream >> obj.airportIcao >> obj.runwayName >> obj.color >> obj.turnRight >> obj.base45Degree >> obj.showEntryExit + >> obj.runwayLength >> obj.downwindDistance >> obj.baseDistance >> obj.courseTrue >> obj.magvar >> obj.position; return dataStream; } QDataStream& operator<<(QDataStream& dataStream, const TrafficPattern& obj) { - dataStream << obj.airportIcao - << obj.runwayName - << obj.color - << obj.turnRight - << obj.base45Degree - << obj.showEntryExit - << obj.runwayLength - << obj.downwindDistance - << obj.baseDistance - << obj.courseTrue - << obj.magvar - << obj.position; + dataStream << obj.airportIcao << obj.runwayName << obj.color << obj.turnRight << obj.base45Degree << obj.showEntryExit + << obj.runwayLength << obj.downwindDistance << obj.baseDistance << obj.courseTrue << obj.magvar << obj.position; return dataStream; } QDataStream& operator>>(QDataStream& dataStream, MapHolding& obj) { - dataStream - >> obj.navIdent - >> obj.navType - >> obj.vorDmeOnly - >> obj.vorHasDme - >> obj.vorTacan - >> obj.vorVortac - >> obj.color - >> obj.turnLeft - >> obj.time - >> obj.speedKts - >> obj.courseTrue - >> obj.magvar - >> obj.position; + dataStream >> obj.navIdent >> obj.navType >> obj.vorDmeOnly >> obj.vorHasDme >> obj.vorTacan >> obj.vorVortac + >> obj.color >> obj.turnLeft >> obj.time >> obj.speedKts >> obj.courseTrue >> obj.magvar >> obj.position; obj.user = true; obj.length = obj.speedLimit = obj.minAltititude = obj.maxAltititude = 0.f; @@ -1379,20 +1347,8 @@ QDataStream& operator>>(QDataStream& dataStream, MapHolding& obj) QDataStream& operator<<(QDataStream& dataStream, const MapHolding& obj) { - dataStream - << obj.navIdent - << obj.navType - << obj.vorDmeOnly - << obj.vorHasDme - << obj.vorTacan - << obj.vorVortac - << obj.color - << obj.turnLeft - << obj.time - << obj.speedKts - << obj.courseTrue - << obj.magvar - << obj.position; + dataStream << obj.navIdent << obj.navType << obj.vorDmeOnly << obj.vorHasDme << obj.vorTacan << obj.vorVortac + << obj.color << obj.turnLeft << obj.time << obj.speedKts << obj.courseTrue << obj.magvar << obj.position; return dataStream; } @@ -1447,6 +1403,41 @@ QString vorType(bool dmeOnly, bool hasDme, bool tacan, bool vortac) } } +QString vorFullShortText(const MapVor& vor) +{ + return vorFullShortText(vor.type, vor.dmeOnly, vor.hasDme, vor.tacan, vor.vortac); +} + +QString vorFullShortText(const QString& vorType, bool dmeOnly, bool hasDme, bool tacan, bool vortac) +{ + if(tacan) + return QObject::tr("TACAN"); + else if(vorType.isEmpty()) + { + if(vortac) + return QObject::tr("VORTAC"); + else if(dmeOnly) + return QObject::tr("DME"); + else if(hasDme) + return QObject::tr("VORDME"); + else + return QObject::tr("VOR"); + } + else + { + QString type = vorType.startsWith("VT") ? vorType.at(vorType.size() - 1) : vorType.at(0); + + if(vortac) + return QObject::tr("VORTAC (%1)").arg(type); + else if(dmeOnly) + return QObject::tr("DME (%1)").arg(type); + else if(hasDme) + return QObject::tr("VORDME (%1)").arg(type); + else + return QObject::tr("VOR (%1)").arg(type); + } +} + QString vorText(const MapVor& vor, int elideName) { return QObject::tr("%1 %2 (%3)"). @@ -1545,6 +1536,22 @@ QString airportTextShort(const MapAirport& airport, int elideName) return QObject::tr("%1 (%2)").arg(atools::elideTextShort(airport.name, elideName)).arg(airport.displayIdent()); } +QString airportMsaTextShort(const MapAirportMsa& airportMsa) +{ + if(!airportMsa.isValid()) + return QObject::tr("MSA"); + else + return QObject::tr("%1/%2").arg(airportMsa.airportIdent).arg(airportMsa.navIdent); +} + +QString airportMsaText(const MapAirportMsa& airportMsa) +{ + if(!airportMsa.isValid()) + return QObject::tr("MSA"); + else + return QObject::tr("MSA Sectors at %1/%2").arg(airportMsa.airportIdent).arg(airportMsa.navIdent); +} + QString comTypeName(const QString& type) { Q_ASSERT(!comTypeNames.isEmpty()); @@ -1642,36 +1649,6 @@ QString patternDirection(const QString& type) return QString(); } -QString vorFullShortText(const MapVor& vor) -{ - if(vor.tacan) - return QObject::tr("TACAN"); - else if(vor.type.isEmpty()) - { - if(vor.vortac) - return QObject::tr("VORTAC"); - else if(vor.dmeOnly) - return QObject::tr("DME"); - else if(vor.hasDme) - return QObject::tr("VORDME"); - else - return QObject::tr("VOR"); - } - else - { - QString type = vor.type.startsWith("VT") ? vor.type.at(vor.type.size() - 1) : vor.type.at(0); - - if(vor.vortac) - return QObject::tr("VORTAC (%1)").arg(type); - else if(vor.dmeOnly) - return QObject::tr("DME (%1)").arg(type); - else if(vor.hasDme) - return QObject::tr("VORDME (%1)").arg(type); - else - return QObject::tr("VOR (%1)").arg(type); - } -} - QString ndbFullShortText(const MapNdb& ndb) { // Compass point vs. compass locator @@ -1701,6 +1678,8 @@ QString mapObjectTypeToString(MapTypes type) if(type.testFlag(AIRPORT)) str += "Airport"; + if(type.testFlag(AIRPORT_MSA)) + str += "Airport MSA"; if(type.testFlag(AIRPORT_HARD)) str += "AirportHard"; if(type.testFlag(AIRPORT_SOFT)) diff --git a/src/common/maptypes.h b/src/common/maptypes.h index 53f76b815..c2580dfe9 100644 --- a/src/common/maptypes.h +++ b/src/common/maptypes.h @@ -597,6 +597,41 @@ struct MapHelipad bool closed, transparent; }; +// ===================================================================== +/* All information for a airport MSA circle */ +struct MapAirportMsa : + public MapBase +{ + MapAirportMsa() : MapBase(map::AIRPORT_MSA) + { + } + + QString airportIdent, navIdent, region, multipleCode, vorType; + + map::MapType navType; /* AIRPORT, VOR, NDB or WAYPOINT*/ + bool vorDmeOnly, vorHasDme, vorTacan, vorVortac; /* VOR specific flags */ + + float radius, /* Radius in NM */ + magvar; /* Taken from environment or navaid */ + + bool user; /* true if created by user. otherwise from database */ + + QVector bearings, /* Bearings in true or mag degree - same size as altitudes */ + altitudes; /* Altitudes in feet - same size as bearings */ + + bool trueBearing; /* true if all bearing values are true - otherwise magnetic */ + atools::geo::LineString geometry, /* Outer circle/arcs geometry. 180 points for full circle. */ + labelPositions, /* Pre-calculated altitude label positions. */ + bearingEndPositions; /* Endpoints for bearing lines from center to end point */ + atools::geo::Rect bounding; + + const QString& getIdent() const + { + return navIdent; + } + +}; + // ===================================================================== /* VOR station */ /* database id vor.vor_id */ @@ -628,6 +663,11 @@ struct MapVor return channel; } + const QString& getIdent() const + { + return ident; + } + }; // ===================================================================== @@ -644,6 +684,12 @@ struct MapNdb float magvar; int frequency /* kHz * 100 */, range /* nm */; int routeIndex = -1; /* Filled by the get nearest methods for building the context menu */ + + const QString& getIdent() const + { + return ident; + } + }; // ===================================================================== @@ -663,6 +709,12 @@ struct MapWaypoint bool hasVictorAirways = false, hasJetAirways = false, hasTracks = false; int artificial = 0; + + const QString& getIdent() const + { + return ident; + } + }; /* Waypoint or intersection */ @@ -901,6 +953,11 @@ struct MapAirway atools::geo::Rect bounding; /* pre calculated using from and to */ bool eastCourse, westCourse; + const QString& getIdent() const + { + return name; + } + }; // ===================================================================== @@ -1027,6 +1084,11 @@ struct MapIls return slope > 0.1f; } + const QString& getIdent() const + { + return ident; + } + atools::geo::LineString boundary() const; atools::geo::Line centerLine() const; @@ -1135,7 +1197,7 @@ struct MapHolding } QString navIdent, name, vorType; /* Only for display purposes */ - map::MapTypes navType; /* AIRPORT, VOR, NDB or WAYPOINT*/ + map::MapType navType; /* AIRPORT, VOR, NDB or WAYPOINT*/ bool vorDmeOnly, vorHasDme, vorTacan, vorVortac; /* VOR specific flags */ QString airportIdent; @@ -1158,6 +1220,11 @@ struct MapHolding /* Distance of straight segment in NM. Either from database or calculated */ float distance(bool *estimated = nullptr) const; + const QString& getIdent() const + { + return navIdent; + } + }; /* Save only information for user defined holds */ @@ -1299,15 +1366,23 @@ QString comTypeName(const QString& type); QString airportText(const map::MapAirport& airport, int elideName = 100); QString airportTextShort(const map::MapAirport& airport, int elideName = 100); + +QString airportMsaText(const map::MapAirportMsa& airportMsa); +QString airportMsaTextShort(const map::MapAirportMsa& airportMsa); + QString vorFullShortText(const map::MapVor& vor); +QString vorFullShortText(const QString& vorType, bool dmeOnly, bool hasDme, bool tacan, bool vortac); QString vorText(const map::MapVor& vor, int elideName = 100); QString vorTextShort(const MapVor& vor); QString vorType(const map::MapVor& vor); QString vorType(bool dmeOnly, bool hasDme, bool tacan, bool vortac); + QString ndbFullShortText(const map::MapNdb& ndb); QString ndbText(const map::MapNdb& ndb, int elideName = 100); QString ndbTextShort(const MapNdb& ndb); + QString waypointText(const map::MapWaypoint& waypoint); + QString userpointRouteText(const map::MapUserpointRoute& userpoint); QString userpointText(const MapUserpoint& userpoint, int elideName = 100); QString logEntryText(const MapLogbookEntry& logEntry); @@ -1329,7 +1404,9 @@ void updateUnits(); } // namespace map /* Type info */ +Q_DECLARE_TYPEINFO(map::MapBase, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(map::MapAirport, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(map::MapAirportMsa, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(map::MapRunway, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(map::MapRunwayEnd, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(map::MapApron, Q_MOVABLE_TYPE); @@ -1348,6 +1425,7 @@ Q_DECLARE_TYPEINFO(map::MapUserpointRoute, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(map::PosCourse, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(map::MapAirspace, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(map::MapLogbookEntry, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(map::MapObjectRefExt, Q_MOVABLE_TYPE); /* Type info and serializable objects */ Q_DECLARE_TYPEINFO(map::MapObjectRef, Q_PRIMITIVE_TYPE); diff --git a/src/common/maptypesfactory.cpp b/src/common/maptypesfactory.cpp index 696902931..6ceeeb9e6 100644 --- a/src/common/maptypesfactory.cpp +++ b/src/common/maptypesfactory.cpp @@ -22,6 +22,7 @@ #include "geo/calculations.h" #include "common/maptypes.h" #include "io/binaryutil.h" +#include "fs/common/binarymsageometry.h" using namespace atools::geo; using atools::sql::SqlRecord; @@ -586,6 +587,22 @@ void MapTypesFactory::fillIls(const SqlRecord& record, map::MapIls& ils) ils.bounding.extend(ils.pos2); } +map::MapType MapTypesFactory::strToType(const QString& navType) +{ + if(navType == "W") + return map::WAYPOINT; + else if(navType == "N") + return map::NDB; + else if(navType == "V") + return map::VOR; // VOR/TACAN/DME + else if(navType == "A") + return map::AIRPORT; + else if(navType == "R") + return map::RUNWAYEND; + + return map::NONE; +} + void MapTypesFactory::fillHolding(const atools::sql::SqlRecord& record, map::MapHolding& holding) { holding.id = record.valueInt("holding_id"); @@ -594,17 +611,7 @@ void MapTypesFactory::fillHolding(const atools::sql::SqlRecord& record, map::Map holding.navIdent = record.valueStr("nav_ident"); // region varchar(2), -- ICAO two letter region identifier - QString navType = record.valueStr("nav_type"); - if(navType == "W") - holding.navType = map::WAYPOINT; - else if(navType == "N") - holding.navType = map::NDB; - else if(navType == "V") - holding.navType = map::VOR; // VOR/TACAN/DME - else if(navType == "A") - holding.navType = map::AIRPORT; - else if(navType == "R") - holding.navType = map::RUNWAYEND; + holding.navType = strToType(record.valueStr("nav_type")); holding.name = record.valueStr("name"); holding.vorType = record.valueStr("vor_type"); @@ -621,12 +628,43 @@ void MapTypesFactory::fillHolding(const atools::sql::SqlRecord& record, map::Map holding.minAltititude = record.valueFloat("minimum_altitude"); holding.maxAltititude = record.valueFloat("maximum_altitude"); holding.speedLimit = record.valueFloat("speed_limit"); - holding.position = Pos(record.valueFloat("lonx"), - record.valueFloat("laty")); + holding.position = Pos(record.valueFloat("lonx"), record.valueFloat("laty")); holding.speedKts = 0.f; holding.user = false; +} +void MapTypesFactory::fillAirportMsa(const atools::sql::SqlRecord& record, map::MapAirportMsa& airportMsa) +{ + // left_lonx, top_laty, right_lonx, bottom_laty, radius, lonx, laty, geometry + airportMsa.id = record.valueInt("airport_msa_id"); + airportMsa.airportIdent = record.valueStr("airport_ident"); + airportMsa.navIdent = record.valueStr("nav_ident"); + airportMsa.region = record.valueStr("region"); + airportMsa.multipleCode = record.valueStr("multiple_code"); + + airportMsa.vorType = record.valueStr("vor_type"); + airportMsa.vorTacan = airportMsa.vorType == "TC"; + airportMsa.vorVortac = airportMsa.vorType.startsWith("VT"); + airportMsa.vorDmeOnly = record.valueInt("vor_dme_only") > 0; + airportMsa.vorHasDme = record.valueInt("vor_has_dme") > 0; + + airportMsa.radius = record.valueFloat("radius"); + airportMsa.trueBearing = record.valueInt("true_bearing") > 0; + airportMsa.magvar = record.valueFloat("mag_var"); + airportMsa.navType = strToType(record.valueStr("nav_type")); + airportMsa.position = Pos(record.valueFloat("lonx"), record.valueFloat("laty")); + + atools::fs::common::BinaryMsaGeometry geo(record.valueBytes("geometry")); + airportMsa.bearings = geo.getBearings(); + airportMsa.altitudes = geo.getAltitudes(); + airportMsa.geometry = geo.getGeometry(); + airportMsa.labelPositions = geo.getLabelPositions(); + airportMsa.bearingEndPositions = geo.getBearingEndPositions(); + + airportMsa.bounding = Rect(record.valueFloat("left_lonx"), record.valueFloat("top_laty"), + record.valueFloat("right_lonx"), record.valueFloat("bottom_laty")); + airportMsa.user = false; } void MapTypesFactory::fillParking(const SqlRecord& record, map::MapParking& parking) diff --git a/src/common/maptypesfactory.h b/src/common/maptypesfactory.h index d4f46b8c5..7a909aae6 100644 --- a/src/common/maptypesfactory.h +++ b/src/common/maptypesfactory.h @@ -29,37 +29,22 @@ class SqlRecord; namespace map { struct MapAirport; - struct MapRunway; - struct MapRunwayEnd; - struct MapVor; - struct MapNdb; - struct MapHolding; - struct MapWaypoint; - struct MapAirway; - struct MapIls; - struct MapParking; - struct MapAirspace; - struct MapStart; - struct MapHelipad; - struct MapMarker; - struct MapUserpoint; - struct MapLogbookEntry; - +struct MapAirportMsa; } /* @@ -102,6 +87,7 @@ class MapTypesFactory void fillMarker(const atools::sql::SqlRecord& record, map::MapMarker& marker); void fillIls(const atools::sql::SqlRecord& record, map::MapIls& ils); void fillHolding(const atools::sql::SqlRecord& record, map::MapHolding& holding); + void fillAirportMsa(const atools::sql::SqlRecord& record, map::MapAirportMsa& airportMsa); void fillParking(const atools::sql::SqlRecord& record, map::MapParking& parking); void fillStart(const atools::sql::SqlRecord& record, map::MapStart& start); @@ -123,6 +109,8 @@ class MapTypesFactory map::MapAirportFlags airportFlag); map::MapAirportFlags fillAirportFlags(const atools::sql::SqlRecord& record, bool overview); + map::MapType strToType(const QString& navType); + }; #endif // LITTLENAVMAP_MAPTYPESFACTORY_H diff --git a/src/common/symbolpainter.cpp b/src/common/symbolpainter.cpp index 8a53e1887..311ccd585 100644 --- a/src/common/symbolpainter.cpp +++ b/src/common/symbolpainter.cpp @@ -43,15 +43,6 @@ const QVector AIRCRAFTLINES({QLine(0, -20, 0, 16), // Body QLine(-10, 18, 0, 14), QLine(0, 14, 10, 18) // Horizontal stabilizer }); -SymbolPainter::SymbolPainter() -{ -} - -SymbolPainter::~SymbolPainter() -{ - -} - QIcon SymbolPainter::createAirportIcon(const map::MapAirport& airport, int size) { QPixmap pixmap(size, size); @@ -75,6 +66,31 @@ QIcon SymbolPainter::createAirportWeatherIcon(const atools::fs::weather::Metar& return QIcon(pixmap); } +QIcon SymbolPainter::createAirportMsaIcon(const map::MapAirportMsa& airportMsa, const QFont& font, float symbolScale, int *actualSize) +{ + // Create a temporary pixmap to get the font size + int size = 100; + QPixmap pixmapTemp(size, size); + QPainter painterTemp(&pixmapTemp); + prepareForIcon(painterTemp); + painterTemp.setFont(font); + + // Get pixel size based on scale + size = airportMsaSize(&painterTemp, airportMsa, symbolScale); + + // Prepare final drawing canvas + QPixmap pixmap(size, size); + pixmap.fill(QColor(Qt::transparent)); + QPainter painter(&pixmap); + prepareForIcon(painter); + + if(actualSize != nullptr) + *actualSize = size; + + SymbolPainter().drawAirportMsa(&painter, airportMsa, size / 2, size / 2, symbolScale, false); + return QIcon(pixmap); +} + QIcon SymbolPainter::createVorIcon(const map::MapVor& vor, int size) { QPixmap pixmap(size, size); @@ -626,6 +642,105 @@ void SymbolPainter::drawWindPointer(QPainter *painter, float x, float y, int siz painter->resetTransform(); } +int SymbolPainter::airportMsaSize(QPainter *painter, const map::MapAirportMsa& airportMsa, float sizeFactor) +{ + return painter->fontMetrics().horizontalAdvance("0000") * + atools::roundToInt(airportMsa.altitudes.size() > 1 ? sizeFactor : sizeFactor / 2.f); +} + +void SymbolPainter::drawAirportMsa(QPainter *painter, const map::MapAirportMsa& airportMsa, float x, float y, float symbolScale, + bool header) +{ + atools::util::PainterContextSaver saver(painter); + + painter->setPen(QPen(mapcolors::msaSymbolColor, 2, Qt::SolidLine, Qt::FlatCap)); + painter->setBackground(mapcolors::msaFillColor); + painter->setBrush(mapcolors::msaFillColor); + painter->setBackgroundMode(Qt::OpaqueMode); + + int size = airportMsaSize(painter, airportMsa, symbolScale); + + int radius = size / 2; + + // Outer circle with semi-transparent fill + painter->drawEllipse(QPointF(x, y), radius - 2, radius - 2); + + // Opaque fill + QColor fill = mapcolors::msaFillColor; + fill.setAlpha(255); + painter->setBackground(fill); + + if(header) + { + // Draw a header label ============================================= + QString heading = tr("MSA %1 %2 (%3, %4)"). + arg(airportMsa.navIdent). + arg(Unit::distNm(airportMsa.radius, true, true)). + arg(airportMsa.trueBearing ? tr("°T") : tr("°M")). + arg(Unit::getUnitAltStr()); + + QRectF bounding = painter->fontMetrics().boundingRect(heading); + QPointF pt(x - bounding.size().width() / 2., y - radius); + painter->drawText(pt, heading); + + bounding.translate(pt); // Move rect to origin + painter->setBrush(Qt::transparent); + painter->drawRect(bounding); + painter->setBrush(fill); + } + + // Draw bearing lines and bearing degree labels ====================================== + float magvar = airportMsa.trueBearing ? 0.f : airportMsa.magvar; + if(airportMsa.altitudes.size() > 1) + { + for(int i = 0; i < airportMsa.bearings.size(); i++) + { + float bearing = airportMsa.bearings.at(i); + QString text = tr("%1°").arg(atools::geo::normalizeCourse(bearing)); + QSizeF txtsize = painter->fontMetrics().boundingRect(text).size(); + + // Convert from bearing to angle + bearing = atools::geo::normalizeCourse(bearing + 180.f + magvar); + + // Line from center to top + QLineF line(x, y, x, y - radius + 2); + line.setAngle(atools::geo::angleToQt(bearing)); + + // Draw sector line + painter->drawLine(line); + + // Move origin to label position and draw text + painter->translate(line.center()); + painter->rotate(bearing < 180.f ? bearing - 90.f : bearing + 90.f); // Keep text upwards + painter->drawText(QPointF(-txtsize.width() / 2., txtsize.height() / 3.), text); + painter->resetTransform(); + } + } + + // Draw altitude lables ===================================================================== + for(int i = 0; i < airportMsa.bearings.size(); i++) + { + float bearing = atools::geo::normalizeCourse(airportMsa.bearings.at(i) + 180.f + magvar); + float bearingTo = atools::geo::normalizeCourse(atools::atRoll(airportMsa.bearings, i + 1) + 180.f + magvar); + + // Line from origin to top defining label position - circles with one sector use a label closer to the center + QLineF line(x, y, x, y - radius * (airportMsa.altitudes.size() > 1 ? 0.70 : 0.4)); + + if(airportMsa.altitudes.size() > 1) + // Rotate for sector bearing + line.setAngle(atools::geo::angleToQt(bearing + atools::geo::angleAbsDiff(bearing, bearingTo) / 2.f)); + + QString text = Unit::altFeet(airportMsa.altitudes.at(i), false /* addUnit */, true /* narrow */); + QSizeF txtsize = painter->fontMetrics().boundingRect(text).size(); + painter->drawText(QPointF(line.p2().x() - txtsize.width() / 2., line.p2().y() + txtsize.height() / 2.), text); + } + + // Draw small center circle + painter->setBackground(fill); + painter->setBrush(fill); + painter->drawEllipse(QPointF(x, y), 4., 4.); +} + void SymbolPainter::drawTrackLine(QPainter *painter, float x, float y, int size, float dir) { atools::util::PainterContextSaver saver(painter); diff --git a/src/common/symbolpainter.h b/src/common/symbolpainter.h index 03f8c751a..fdd05b7e0 100644 --- a/src/common/symbolpainter.h +++ b/src/common/symbolpainter.h @@ -52,6 +52,7 @@ struct MapMarker; struct MapAirspace; struct MapAirway; struct MapHelipad; +struct MapAirportMsa; } /* @@ -65,12 +66,6 @@ class SymbolPainter Q_DECLARE_TR_FUNCTIONS(SymbolPainter) public: - /* - * @param backgroundColor used for tooltips of table view icons - */ - SymbolPainter(); - ~SymbolPainter(); - /* Create icons for tooltips, table views and more. Size is pixel. */ QIcon createAirportIcon(const map::MapAirport& airport, int size); QIcon createAirportWeatherIcon(const atools::fs::weather::Metar& metar, int size); @@ -83,6 +78,9 @@ class SymbolPainter QIcon createAirspaceIcon(const map::MapAirspace& airspace, int size); QIcon createHelipadIcon(const map::MapHelipad& helipad, int size); + /* Scale is not pixel size but a factor related for font size */ + QIcon createAirportMsaIcon(const map::MapAirportMsa& airportMsa, const QFont& font, float symbolScale, int *actualSize = nullptr); + /* Airport symbol. For airport diagram use a transparent text background */ void drawAirportSymbol(QPainter *painter, const map::MapAirport& airport, float x, float y, int size, bool isAirportDiagram, bool fast, bool addonHighlight); @@ -100,6 +98,9 @@ class SymbolPainter /* Wind arrow */ void drawWindPointer(QPainter *painter, float x, float y, int size, float dir); + /* Draw large symbol with sectors and labels. MSA circle with bearings and altitude */ + void drawAirportMsa(QPainter *painter, const map::MapAirportMsa& airportMsa, float x, float y, float symbolScale, bool header); + /* Aircraft track */ void drawTrackLine(QPainter *painter, float x, float y, int size, float dir); @@ -128,7 +129,7 @@ class SymbolPainter void drawHelipadSymbol(QPainter *painter, const map::MapHelipad& helipad, float x, float y, float w, float h, bool fast); - /* User defined flight plan waypoint */ + /* User defined flight plan waypoint. Green rect. */ void drawUserpointSymbol(QPainter *painter, int x, int y, int size, bool routeFill); /* Circle for approach points which are not navaids */ @@ -177,6 +178,8 @@ class SymbolPainter void drawBarbFeathers(QPainter *painter, const QVector& barbs, float lineLength, float barbLength5, float barbLength10, float barbLength50, float barbStep) const; + int airportMsaSize(QPainter *painter, const map::MapAirportMsa& airportMsa, float sizeFactor); + }; #endif // LITTLENAVMAP_SYMBOLPAINTER_H diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 8dc4d7976..b45a8d33b 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1443,6 +1443,7 @@ void MainWindow::connectAllSlots() connect(ui->actionMapShowIls, &QAction::toggled, this, &MainWindow::updateMapObjectsShown); connect(ui->actionMapShowGls, &QAction::toggled, this, &MainWindow::updateMapObjectsShown); connect(ui->actionMapShowHolding, &QAction::toggled, this, &MainWindow::updateMapObjectsShown); + connect(ui->actionMapShowAirportMsa, &QAction::toggled, this, &MainWindow::updateMapObjectsShown); connect(ui->actionMapShowVictorAirways, &QAction::toggled, this, &MainWindow::updateMapObjectsShown); connect(ui->actionMapShowJetAirways, &QAction::toggled, this, &MainWindow::updateMapObjectsShown); connect(ui->actionMapShowTracks, &QAction::toggled, this, &MainWindow::updateMapObjectsShown); @@ -3655,6 +3656,7 @@ void MainWindow::updateActionStates() ui->actionMapShowMinimumAltitude->setEnabled(NavApp::isMoraAvailable()); ui->actionMapShowGls->setEnabled(NavApp::isGlsAvailable()); ui->actionMapShowHolding->setEnabled(NavApp::isHoldingsAvailable()); + ui->actionMapShowAirportMsa->setEnabled(NavApp::isAirportMsaAvailable()); bool hasFlightplan = !NavApp::getRouteConst().isFlightplanEmpty(); bool hasTrack = !NavApp::isAircraftTrackEmpty(); @@ -3920,7 +3922,8 @@ void MainWindow::restoreStateMain() // Restore map settings if desired by the user widgetState.restore({ui->actionMapShowAirports, ui->actionMapShowSoftAirports, ui->actionMapShowEmptyAirports, ui->actionMapShowAddonAirports, ui->actionMapShowVor, ui->actionMapShowNdb, - ui->actionMapShowWp, ui->actionMapShowIls, ui->actionMapShowGls, ui->actionMapShowHolding, + ui->actionMapShowWp, ui->actionMapShowIls, ui->actionMapShowGls, + ui->actionMapShowHolding, ui->actionMapShowAirportMsa, ui->actionMapShowVictorAirways, ui->actionMapShowJetAirways, ui->actionMapShowTracks, ui->actionShowAirspaces, ui->actionMapShowRoute, ui->actionMapShowTocTod, ui->actionMapShowAircraft, @@ -4121,7 +4124,7 @@ void MainWindow::saveActionStates() widgetState.save({mapProjectionComboBox, mapThemeComboBox, ui->actionMapShowAirports, ui->actionMapShowSoftAirports, ui->actionMapShowEmptyAirports, ui->actionMapShowAddonAirports, ui->actionMapShowVor, ui->actionMapShowNdb, ui->actionMapShowWp, ui->actionMapShowIls, - ui->actionMapShowGls, ui->actionMapShowHolding, ui->actionMapShowVictorAirways, + ui->actionMapShowGls, ui->actionMapShowHolding, ui->actionMapShowAirportMsa, ui->actionMapShowVictorAirways, ui->actionMapShowJetAirways, ui->actionMapShowTracks, ui->actionShowAirspaces, ui->actionMapShowRoute, ui->actionMapShowTocTod, ui->actionMapShowAircraft, ui->actionMapShowCompassRose, ui->actionMapShowCompassRoseAttach, ui->actionMapAircraftCenter, diff --git a/src/gui/mainwindow.ui b/src/gui/mainwindow.ui index 6131cd140..7c1ac865c 100644 --- a/src/gui/mainwindow.ui +++ b/src/gui/mainwindow.ui @@ -379,7 +379,9 @@ + + @@ -5608,6 +5610,7 @@ relation to shown airport + @@ -12098,6 +12101,27 @@ Disabled if a navdatabase does not allow it. Ctrl+PgUp + + + true + + + false + + + + :/littlenavmap/resources/icons/msa.svg:/littlenavmap/resources/icons/msa.svg + + + Show &MSA Sectors + + + Show airport MSA sectors on the map + + + Show holdings on map + + @@ -12283,8 +12307,8 @@ Disabled if a navdatabase does not allow it. + - diff --git a/src/info/infocontroller.cpp b/src/info/infocontroller.cpp index 3e5865c70..bee816d4c 100644 --- a/src/info/infocontroller.cpp +++ b/src/info/infocontroller.cpp @@ -380,6 +380,9 @@ void InfoController::saveState() for(const map::MapAirport& airport : currentSearchResult.airports) refs.append({airport.id, map::AIRPORT}); + for(const map::MapAirportMsa& msa : currentSearchResult.airportMsa) + refs.append({msa.id, map::AIRPORT_MSA}); + for(const map::MapVor& vor : currentSearchResult.vors) refs.append({vor.id, map::VOR}); @@ -461,8 +464,10 @@ void InfoController::restoreState() res.airspaces.append(NavApp::getAirspaceController()->getAirspaceById(id)); } - showInformationInternal(res, false /* show windows */, false /* scroll to top */, - true /* forceUpdate */); + // Remove wrong objects + res.removeInvalid(); + + showInformationInternal(res, false /* show windows */, false /* scroll to top */, true /* forceUpdate */); Ui::MainWindow *ui = NavApp::getMainUi(); atools::gui::WidgetState(lnm::INFOWINDOW_WIDGET).restore(ui->tabWidgetLegend); @@ -597,8 +602,7 @@ void InfoController::onlineClientAndAtcUpdated() /* Show information in all tabs but do not show dock * @return true if information was updated */ -void InfoController::showInformationInternal(map::MapResult result, bool showWindows, bool scrollToTop, - bool forceUpdate) +void InfoController::showInformationInternal(map::MapResult result, bool showWindows, bool scrollToTop, bool forceUpdate) { #ifdef DEBUG_INFORMATION qDebug() << Q_FUNC_INFO << "result" << result; @@ -729,23 +733,19 @@ void InfoController::showInformationInternal(map::MapResult result, bool showWin // Update parts that have now weather or bearing depenedency ===================== html.clear(); infoBuilder->runwayText(airport, html); - atools::gui::util::updateTextEdit(ui->textBrowserRunwayInfo, html.getHtml(), - scrollToTop, !scrollToTop /* keep selection */); + atools::gui::util::updateTextEdit(ui->textBrowserRunwayInfo, html.getHtml(), scrollToTop, !scrollToTop /* keep selection */); html.clear(); infoBuilder->comText(airport, html); - atools::gui::util::updateTextEdit(ui->textBrowserComInfo, html.getHtml(), - scrollToTop, !scrollToTop /* keep selection */); + atools::gui::util::updateTextEdit(ui->textBrowserComInfo, html.getHtml(), scrollToTop, !scrollToTop /* keep selection */); html.clear(); infoBuilder->procedureText(airport, html); - atools::gui::util::updateTextEdit(ui->textBrowserApproachInfo, html.getHtml(), - scrollToTop, !scrollToTop /* keep selection */); + atools::gui::util::updateTextEdit(ui->textBrowserApproachInfo, html.getHtml(), scrollToTop, !scrollToTop /* keep selection */); html.clear(); infoBuilder->nearestText(airport, html); - atools::gui::util::updateTextEdit(ui->textBrowserNearestInfo, html.getHtml(), - scrollToTop, !scrollToTop /* keep selection */); + atools::gui::util::updateTextEdit(ui->textBrowserNearestInfo, html.getHtml(), scrollToTop, !scrollToTop /* keep selection */); } foundAirport = true; @@ -858,10 +858,10 @@ void InfoController::showInformationInternal(map::MapResult result, bool showWin } // Navaids tab ================================================================ - if(result.hasVor() || result.hasNdb() || result.hasWaypoints() || result.hasIls() || result.hasHoldings() || + if(result.hasVor() || result.hasNdb() || result.hasWaypoints() || result.hasIls() || result.hasHoldings() || result.hasAirportMsa() || result.hasAirways()) // if any navaids are to be shown clear search result before - currentSearchResult.clear(map::NAV_ALL | map::ILS | map::AIRWAY | map::RUNWAYEND | map::HOLDING); + currentSearchResult.clear(map::NAV_ALL | map::ILS | map::AIRWAY | map::RUNWAYEND | map::HOLDING | map::AIRPORT_MSA); if(!result.userpoints.isEmpty()) // if any userpoints are to be shown clear search result before @@ -960,8 +960,26 @@ void InfoController::showInformationInternal(map::MapResult result, bool showWin } } -bool InfoController::updateNavaidInternal(const map::MapResult& result, bool bearingChanged, bool scrollToTop, - bool forceUpdate) +template +void InfoController::buildOneNavaid(atools::util::HtmlBuilder& html, bool& bearingChanged, bool& foundNavaid, const QList& list, + QList& currentList, const HtmlInfoBuilder *info, + void (HtmlInfoBuilder::*func)(const TYPE&, atools::util::HtmlBuilder&) const) const +{ + for(const TYPE& type : list) + { +#ifdef DEBUG_INFORMATION + qDebug() << "Found" << type.getIdent(); +#endif + + if(!bearingChanged) + currentList.append(type); + (info->*func)(type, html); + html.br(); + foundNavaid = true; + } +} + +bool InfoController::updateNavaidInternal(const map::MapResult& result, bool bearingChanged, bool scrollToTop, bool forceUpdate) { HtmlBuilder html(true); Ui::MainWindow *ui = NavApp::getMainUi(); @@ -977,83 +995,26 @@ bool InfoController::updateNavaidInternal(const map::MapResult& result, bool bea html.b().a(tr("Remove Airway and Track Highlights"), QString("lnm://do?hideairways"), ahtml::LINK_NO_UL).bEnd().tdEnd().trEnd().tableEnd(); - for(const map::MapVor& vor : result.vors) - { -#ifdef DEBUG_INFORMATION - qDebug() << "Found vor" << vor.ident; -#endif - - if(!bearingChanged) - currentSearchResult.vors.append(vor); - infoBuilder->vorText(vor, html); - html.br(); - foundNavaid = true; - } - - for(const map::MapNdb& ndb : result.ndbs) - { -#ifdef DEBUG_INFORMATION - qDebug() << "Found ndb" << ndb.ident; -#endif - - if(!bearingChanged) - currentSearchResult.ndbs.append(ndb); - infoBuilder->ndbText(ndb, html); - html.br(); - foundNavaid = true; - } - - for(const map::MapWaypoint& waypoint : result.waypoints) - { -#ifdef DEBUG_INFORMATION - qDebug() << "Found waypoint" << waypoint.ident; -#endif - - if(!bearingChanged) - currentSearchResult.waypoints.append(waypoint); - infoBuilder->waypointText(waypoint, html); - html.br(); - foundNavaid = true; - } + buildOneNavaid(html, bearingChanged, foundNavaid, result.vors, currentSearchResult.vors, + infoBuilder, &HtmlInfoBuilder::vorText); - for(const map::MapIls& ils: result.ils) - { -#ifdef DEBUG_INFORMATION - qDebug() << "Found ils" << ils.ident; -#endif + buildOneNavaid(html, bearingChanged, foundNavaid, result.ndbs, currentSearchResult.ndbs, + infoBuilder, &HtmlInfoBuilder::ndbText); - if(!bearingChanged) - currentSearchResult.ils.append(ils); - infoBuilder->ilsTextInfo(ils, html); - html.br(); - foundNavaid = true; - } + buildOneNavaid(html, bearingChanged, foundNavaid, result.waypoints, currentSearchResult.waypoints, + infoBuilder, &HtmlInfoBuilder::waypointText); - for(const map::MapHolding& holding: result.holdings) - { -#ifdef DEBUG_INFORMATION - qDebug() << "Found hold" << holding.name; -#endif + buildOneNavaid(html, bearingChanged, foundNavaid, result.ils, currentSearchResult.ils, + infoBuilder, &HtmlInfoBuilder::ilsTextInfo); - if(!bearingChanged) - currentSearchResult.holdings.append(holding); - infoBuilder->holdingText(holding, html); - html.br(); - foundNavaid = true; - } + buildOneNavaid(html, bearingChanged, foundNavaid, result.holdings, currentSearchResult.holdings, + infoBuilder, &HtmlInfoBuilder::holdingText); - for(const map::MapAirway& airway : result.airways) - { -#ifdef DEBUG_INFORMATION - qDebug() << "Found airway" << airway.name; -#endif + buildOneNavaid(html, bearingChanged, foundNavaid, result.airportMsa, currentSearchResult.airportMsa, + infoBuilder, &HtmlInfoBuilder::airportMsaText); - if(!bearingChanged) - currentSearchResult.airways.append(airway); - infoBuilder->airwayText(airway, html); - html.br(); - foundNavaid = true; - } + buildOneNavaid(html, bearingChanged, foundNavaid, result.airways, currentSearchResult.airways, + infoBuilder, &HtmlInfoBuilder::airwayText); if(!foundNavaid) html.clear(); diff --git a/src/info/infocontroller.h b/src/info/infocontroller.h index 839e38f78..7b4e94ce2 100644 --- a/src/info/infocontroller.h +++ b/src/info/infocontroller.h @@ -145,6 +145,11 @@ class InfoController : void visibilityChangedAircraft(bool visible); void visibilityChangedInfo(bool visible); + template + void buildOneNavaid(atools::util::HtmlBuilder& html, bool& bearingChanged, bool& foundNavaid, const QList& list, + QList& currentList, const HtmlInfoBuilder * info, + void (HtmlInfoBuilder::*func)(const TYPE&, atools::util::HtmlBuilder&) const) const; + QString waitingForUpdateText, notConnectedText; bool databaseLoadStatus = false; diff --git a/src/mapgui/mapcontextmenu.cpp b/src/mapgui/mapcontextmenu.cpp index aef6e3d35..7aa2bd2ba 100644 --- a/src/mapgui/mapcontextmenu.cpp +++ b/src/mapgui/mapcontextmenu.cpp @@ -74,6 +74,7 @@ const static QVector DEFAULT_TYPE_SORT( map::WAYPOINT, map::USERPOINTROUTE, map::HOLDING, + map::AIRPORT_MSA, map::AIRWAY, map::TRACK, map::AIRCRAFT_AI_SHIP, @@ -238,6 +239,9 @@ QString MapContextMenu::mapBaseText(const map::MapBase *base) case map::AIRPORT: return map::airportText(*base->asPtr(), TEXT_ELIDE_AIRPORT_NAME); + case map::AIRPORT_MSA: + return map::airportMsaText(*base->asPtr()); + case map::VOR: return map::vorText(*base->asPtr()); @@ -304,6 +308,9 @@ QIcon MapContextMenu::mapBaseIcon(const map::MapBase *base) case map::AIRPORT: return painter.createAirportIcon(*base->asPtr(), size); + case map::AIRPORT_MSA: + return QIcon(":/littlenavmap/resources/icons/msa.svg"); + case map::VOR: return painter.createVorIcon(*base->asPtr(), size); @@ -502,7 +509,7 @@ void MapContextMenu::insertInformationMenu(QMenu& menu) insertMenuOrAction(menu, mc::INFORMATION, MapResultIndex(). addRef(*result, - map::AIRPORT | map::VOR | map::NDB | map::ILS | map::HOLDING | map::WAYPOINT | map::AIRWAY | + map::AIRPORT | map::VOR | map::NDB | map::ILS | map::HOLDING | map::AIRPORT_MSA | map::WAYPOINT | map::AIRWAY | map::TRACK | map::USERPOINT | map::AIRSPACE | map::AIRCRAFT | map::AIRCRAFT_AI | map::AIRCRAFT_ONLINE | map::LOGBOOK).sort(DEFAULT_TYPE_SORT, alphaSort), tr("&Show Information for %1"), diff --git a/src/mapgui/maplayer.cpp b/src/mapgui/maplayer.cpp index 7c2e84c21..39e2f6cd3 100644 --- a/src/mapgui/maplayer.cpp +++ b/src/mapgui/maplayer.cpp @@ -84,6 +84,16 @@ bool MapLayer::hasSameQueryParametersIls(const MapLayer *other) const return layerIls == other->layerIls; } +bool MapLayer::hasSameQueryParametersHolding(const MapLayer *other) const +{ + return layerHolding == other->layerHolding; +} + +bool MapLayer::hasSameQueryParametersAirportMsa(const MapLayer *other) const +{ + return layerAirportMsa == other->layerAirportMsa; +} + MapLayer& MapLayer::airport(bool value) { layerAirport = value; @@ -509,6 +519,24 @@ MapLayer& MapLayer::airportWeatherDetails(bool value) return *this; } +MapLayer& MapLayer::airportMsa(bool value) +{ + layerAirportMsa = value; + return *this; +} + +MapLayer& MapLayer::airportMsaDetails(bool value) +{ + layerAirportMsaDetails = value; + return *this; +} + +MapLayer& MapLayer::airportMsaSymbolScale(float scale) +{ + layerAirportMsaSymbolScale = scale; + return *this; +} + MapLayer& MapLayer::waypointSymbolSize(int size) { layerWaypointSymbolSize = size; diff --git a/src/mapgui/maplayer.h b/src/mapgui/maplayer.h index 8bc52ca65..68da45895 100644 --- a/src/mapgui/maplayer.h +++ b/src/mapgui/maplayer.h @@ -69,6 +69,8 @@ class MapLayer bool hasSameQueryParametersWind(const MapLayer *other) const; bool hasSameQueryParametersMarker(const MapLayer *other) const; bool hasSameQueryParametersIls(const MapLayer *other) const; + bool hasSameQueryParametersHolding(const MapLayer *other) const; + bool hasSameQueryParametersAirportMsa(const MapLayer *other) const; /* Show airports */ MapLayer& airport(bool value = true); @@ -118,6 +120,11 @@ class MapLayer MapLayer& airportWeather(bool value = true); MapLayer& airportWeatherDetails(bool = true); + /* Indicator for MSA */ + MapLayer& airportMsa(bool value = true); + MapLayer& airportMsaDetails(bool = true); + MapLayer& airportMsaSymbolScale(float scale); + /* Waypoint options */ MapLayer& waypoint(bool value = true); MapLayer& waypointName(bool value = true); @@ -597,6 +604,21 @@ class MapLayer return layerAirportWeatherDetails; } + bool isAirportMsa() const + { + return layerAirportMsa; + } + + bool isAirportMsaDetails() const + { + return layerAirportMsaDetails; + } + + int getAirportMsaSymbolScale() const + { + return layerAirportMsaSymbolScale; + } + /* minimum off route altitude */ bool isMinimumAltitude() const { @@ -637,11 +659,15 @@ class MapLayer bool layerAirportWeather = false, layerAirportWeatherDetails = false; + bool layerAirportMsa = false, layerAirportMsaDetails = false; + int layerAirportSymbolSize = 3, layerMinRunwayLength = 0; bool layerWindBarbs = false; int layerWindBarbsSymbolSize = 6; + float layerAirportMsaSymbolScale = 6.f; + bool layerWaypoint = false, layerWaypointName = false, layerVor = false, layerVorIdent = false, layerVorInfo = false, layerVorLarge = false, layerNdb = false, layerNdbIdent = false, layerNdbInfo = false, layerMarker = false, layerMarkerInfo = false, layerUserpointInfo = false, layerIls = false, layerIlsIdent = false, diff --git a/src/mapgui/mapscreenindex.cpp b/src/mapgui/mapscreenindex.cpp index e9be23695..92ca9b4aa 100644 --- a/src/mapgui/mapscreenindex.cpp +++ b/src/mapgui/mapscreenindex.cpp @@ -645,9 +645,9 @@ void MapScreenIndex::getAllNearest(int xs, int ys, int maxDistance, map::MapResu // Get objects from cache - already present objects will be skipped // Airway included to fetch waypoints - map::MapTypes mapTypes = shown & (map::AIRPORT_ALL_ADDON | map::VOR | map::NDB | map::WAYPOINT | - map::MARKER | map::HOLDING | map::AIRWAYJ | map::TRACK | - map::AIRWAYV | map::USERPOINT | map::LOGBOOK); + map::MapTypes mapTypes = shown & + (map::AIRPORT_ALL_ADDON | map::AIRPORT_MSA | map::VOR | map::NDB | map::WAYPOINT | map::MARKER | map::HOLDING | + map::AIRWAYJ | map::TRACK | map::AIRWAYV | map::USERPOINT | map::LOGBOOK); mapWidget->getMapQuery()->getNearestScreenObjects(conv, mapLayer, mapLayer->isAirportDiagram() && OptionData::instance().getDisplayOptionsAirport(). diff --git a/src/mapgui/maptooltip.cpp b/src/mapgui/maptooltip.cpp index 438fe65f5..d3ae24b81 100644 --- a/src/mapgui/maptooltip.cpp +++ b/src/mapgui/maptooltip.cpp @@ -53,6 +53,31 @@ MapTooltip::~MapTooltip() qDebug() << Q_FUNC_INFO; } +template +void MapTooltip::buildOneTooltip(atools::util::HtmlBuilder& html, bool& overflow, int& numEntries, const QList& list, + const HtmlInfoBuilder& info, + void (HtmlInfoBuilder::*func)(const TYPE&, atools::util::HtmlBuilder&) const) const +{ + if(!overflow) + { + for(const TYPE& type : list) + { + if(checkText(html)) + { + overflow = true; + break; + } + + if(!html.isEmpty()) + html.textBar(TEXT_BAR_LENGTH); + + (info.*func)(type, html); + + numEntries++; + } + } +} + QString MapTooltip::buildTooltip(const map::MapResult& mapSearchResult, const atools::geo::Pos& pos, const Route& route, bool airportDiagram) { @@ -165,110 +190,19 @@ QString MapTooltip::buildTooltip(const map::MapResult& mapSearchResult, const at } } - // Holds =========================================================================== - if(!overflow) - { - for(const MapHolding& entry : mapSearchResult.holdings) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.holdingText(entry, html); - - numEntries++; - } - } - // Traffic pattern =========================================================================== - if(!overflow) - { - for(const TrafficPattern& entry : mapSearchResult.trafficPatterns) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.trafficPatternText(entry, html); - - numEntries++; - } - } + buildOneTooltip(html, overflow, numEntries, mapSearchResult.trafficPatterns, info, &HtmlInfoBuilder::trafficPatternText); // Range rings =========================================================================== - if(!overflow) - { - for(const RangeMarker& entry : mapSearchResult.rangeMarkers) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.rangeMarkerText(entry, html); - - numEntries++; - } - } + buildOneTooltip(html, overflow, numEntries, mapSearchResult.rangeMarkers, info, &HtmlInfoBuilder::rangeMarkerText); - // Logbook entries =========================================================================== - if(!overflow) + if(opts.testFlag(optsd::TOOLTIP_NAVAID)) { - if(opts.testFlag(optsd::TOOLTIP_NAVAID)) - { - for(const MapLogbookEntry& entry : mapSearchResult.logbookEntries) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); + // Logbook entries =========================================================================== + buildOneTooltip(html, overflow, numEntries, mapSearchResult.logbookEntries, info, &HtmlInfoBuilder::logEntryTextInfo); - info.logEntryText(entry, html); - - numEntries++; - } - } - } - - // Userpoints =========================================================================== - if(!overflow) - { - if(opts.testFlag(optsd::TOOLTIP_NAVAID)) - { - for(const MapUserpoint& up : mapSearchResult.userpoints) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.userpointText(up, html); - - numEntries++; - } - } + // Userpoints =========================================================================== + buildOneTooltip(html, overflow, numEntries, mapSearchResult.userpoints, info, &HtmlInfoBuilder::userpointTextInfo); } // Airports =========================================================================== @@ -300,202 +234,31 @@ QString MapTooltip::buildTooltip(const map::MapResult& mapSearchResult, const at // Navaids =========================================================================== if(opts.testFlag(optsd::TOOLTIP_NAVAID)) { - if(!overflow) - { - for(const MapVor& vor : mapSearchResult.vors) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.vorText(vor, html); - - numEntries++; - } - } - - if(!overflow) - { - for(const MapNdb& ndb : mapSearchResult.ndbs) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.ndbText(ndb, html); - - numEntries++; - } - } - - if(!overflow) - { - for(const MapWaypoint& wp : mapSearchResult.waypoints) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.waypointText(wp, html); - - numEntries++; - } - } - - if(!overflow) - { - for(const MapMarker& m : mapSearchResult.markers) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.markerText(m, html); - - numEntries++; - } - } + buildOneTooltip(html, overflow, numEntries, mapSearchResult.vors, info, &HtmlInfoBuilder::vorText); + buildOneTooltip(html, overflow, numEntries, mapSearchResult.ndbs, info, &HtmlInfoBuilder::ndbText); + buildOneTooltip(html, overflow, numEntries, mapSearchResult.waypoints, info, &HtmlInfoBuilder::waypointText); + buildOneTooltip(html, overflow, numEntries, mapSearchResult.markers, info, &HtmlInfoBuilder::markerText); + buildOneTooltip(html, overflow, numEntries, mapSearchResult.ils, info, &HtmlInfoBuilder::ilsTextInfo); - if(!overflow) - { - for(const MapIls& ils : mapSearchResult.ils) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.ilsTextInfo(ils, html); + // Holds =========================================================================== + buildOneTooltip(html, overflow, numEntries, mapSearchResult.holdings, info, &HtmlInfoBuilder::holdingText); - numEntries++; - } - } + // MSA =========================================================================== + buildOneTooltip(html, overflow, numEntries, mapSearchResult.airportMsa, info, &HtmlInfoBuilder::airportMsaText); } // Airport stuff =========================================================================== if(airportDiagram && opts.testFlag(optsd::TOOLTIP_AIRPORT)) { - if(!overflow) - { - for(const MapAirport& ap : mapSearchResult.towers) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.towerText(ap, html); - - numEntries++; - } - } - - if(!overflow) - { - for(const MapParking& p : mapSearchResult.parkings) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.parkingText(p, html); - - numEntries++; - } - } - - if(!overflow) - { - for(const MapHelipad& p : mapSearchResult.helipads) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.helipadText(p, html); - - numEntries++; - } - } + buildOneTooltip(html, overflow, numEntries, mapSearchResult.towers, info, &HtmlInfoBuilder::towerText); + buildOneTooltip(html, overflow, numEntries, mapSearchResult.parkings, info, &HtmlInfoBuilder::parkingText); + buildOneTooltip(html, overflow, numEntries, mapSearchResult.helipads, info, &HtmlInfoBuilder::helipadText); } if(opts.testFlag(optsd::TOOLTIP_NAVAID)) { - if(!overflow) - { - for(const MapUserpointRoute& up : mapSearchResult.userpointsRoute) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.userpointTextRoute(up, html); - - numEntries++; - } - } - - if(!overflow) - { - for(const MapAirway& airway : mapSearchResult.airways) - { - if(checkText(html)) - { - overflow = true; - break; - } - - if(!html.isEmpty()) - html.textBar(TEXT_BAR_LENGTH); - - info.airwayText(airway, html); - - numEntries++; - } - } + buildOneTooltip(html, overflow, numEntries, mapSearchResult.userpointsRoute, info, &HtmlInfoBuilder::userpointTextRoute); + buildOneTooltip(html, overflow, numEntries, mapSearchResult.airways, info, &HtmlInfoBuilder::airwayText); } // High altitude winds =========================================================================== @@ -587,7 +350,7 @@ QString MapTooltip::buildTooltip(const map::MapResult& mapSearchResult, const at } /* Check if the result HTML has more than the allowed number of lines and add a "more" text */ -bool MapTooltip::checkText(HtmlBuilder& html) +bool MapTooltip::checkText(HtmlBuilder& html) const { return html.checklengthTextBar(MAX_LINES, tr("More ..."), TEXT_BAR_LENGTH); } diff --git a/src/mapgui/maptooltip.h b/src/mapgui/maptooltip.h index 4fa5724a8..52214bbe7 100644 --- a/src/mapgui/maptooltip.h +++ b/src/mapgui/maptooltip.h @@ -29,6 +29,7 @@ struct MapResult; class WeatherReporter; class Route; class MainWindow; +class HtmlInfoBuilder; namespace atools { namespace geo { @@ -67,7 +68,12 @@ class MapTooltip QString buildTooltip(const map::MapResult& mapSearchResult, const atools::geo::Pos& pos, const Route& route, bool airportDiagram); private: - bool checkText(atools::util::HtmlBuilder& html); + bool checkText(atools::util::HtmlBuilder& html) const; + + template + void buildOneTooltip(atools::util::HtmlBuilder& html, bool& overflow, int& numEntries, const QList& list, + const HtmlInfoBuilder& info, + void (HtmlInfoBuilder::*func)(const TYPE&, atools::util::HtmlBuilder&) const) const; static Q_DECL_CONSTEXPR int MAX_LINES = 20; diff --git a/src/mapgui/mapwidget.cpp b/src/mapgui/mapwidget.cpp index 497b09214..27dbad337 100644 --- a/src/mapgui/mapwidget.cpp +++ b/src/mapgui/mapwidget.cpp @@ -2206,7 +2206,7 @@ void MapWidget::simDataChanged(const atools::fs::sc::SimConnectData& simulatorDa // Update tooltip if it has bearing/distance fields if((mapSearchResultTooltip.hasAirports() || mapSearchResultTooltip.hasVor() || mapSearchResultTooltip.hasNdb() || mapSearchResultTooltip.hasWaypoints() || mapSearchResultTooltip.hasIls() || - mapSearchResultTooltip.hasHoldings() || + mapSearchResultTooltip.hasHoldings() || mapSearchResultTooltip.hasAirportMsa() || mapSearchResultTooltip.hasUserpoints()) && NavApp::isConnectedAndAircraft()) updateTooltip(); } @@ -2866,7 +2866,7 @@ void MapWidget::resetSettingActionsToDefault() atools::gui::SignalBlocker blocker({ui->actionMapShowAirports, ui->actionMapShowSoftAirports, ui->actionMapShowEmptyAirports, ui->actionMapShowAddonAirports, ui->actionMapShowVor, ui->actionMapShowNdb, ui->actionMapShowWp, - ui->actionMapShowIls, ui->actionMapShowGls, ui->actionMapShowHolding, + ui->actionMapShowIls, ui->actionMapShowGls, ui->actionMapShowHolding, ui->actionMapShowAirportMsa, ui->actionMapShowVictorAirways, ui->actionMapShowJetAirways, ui->actionMapShowTracks, ui->actionShowAirspaces, ui->actionMapShowRoute, ui->actionMapShowTocTod, ui->actionMapShowAircraft, ui->actionMapShowCompassRose, @@ -2894,6 +2894,7 @@ void MapWidget::resetSettingActionsToDefault() ui->actionMapShowIls->setChecked(true); ui->actionMapShowGls->setChecked(true); ui->actionMapShowHolding->setChecked(false); + ui->actionMapShowAirportMsa->setChecked(false); ui->actionMapShowVictorAirways->setChecked(false); ui->actionMapShowJetAirways->setChecked(false); ui->actionMapShowTracks->setChecked(false); @@ -3089,6 +3090,7 @@ void MapWidget::updateMapObjectsShown() setShowMapFeatures(map::NDB, ui->actionMapShowNdb->isChecked()); setShowMapFeatures(map::WAYPOINT, ui->actionMapShowWp->isChecked()); setShowMapFeatures(map::HOLDING, ui->actionMapShowHolding->isChecked()); + setShowMapFeatures(map::AIRPORT_MSA, ui->actionMapShowAirportMsa->isChecked()); // ILS and marker are shown together setShowMapFeatures(map::ILS, ui->actionMapShowIls->isChecked()); diff --git a/src/mappainter/mappainterils.h b/src/mappainter/mappainterils.h index 4955d9f02..23c187730 100644 --- a/src/mappainter/mappainterils.h +++ b/src/mappainter/mappainterils.h @@ -50,4 +50,4 @@ class MapPainterIls : }; -#endif // LITTLENAVMAP_MAPPAINTERAIRPORT_H +#endif // LITTLENAVMAP_MAPPAINTERILS_H diff --git a/src/mappainter/mappaintermsa.cpp b/src/mappainter/mappaintermsa.cpp new file mode 100644 index 000000000..3dcbc965f --- /dev/null +++ b/src/mappainter/mappaintermsa.cpp @@ -0,0 +1,111 @@ +/***************************************************************************** +* Copyright 2015-2020 Alexander Barthel alex@littlenavmap.org +* +* 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 3 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, see . +*****************************************************************************/ + +#include "mappainter/mappaintermsa.h" + +#include "mapgui/mapscale.h" +#include "mapgui/maplayer.h" +#include "query/mapquery.h" +#include "common/mapcolors.h" +#include "util/paintercontextsaver.h" +#include "common/symbolpainter.h" + +#include + +#include + +using namespace Marble; +using namespace atools::geo; +using map::MapAirportMsa; + +MapPainterMsa::MapPainterMsa(MapPaintWidget *mapWidget, MapScale *mapScale, PaintContext *paintContext) + : MapPainter(mapWidget, mapScale, paintContext) +{ +} + +MapPainterMsa::~MapPainterMsa() +{ +} + +void MapPainterMsa::render() +{ + if(context->mapLayer->isAirportMsa()) // Enabled by layer / zoom factor + { + const GeoDataLatLonBox& curBox = context->viewport->viewLatLonAltBox(); + + float x, y; + if(context->objectTypes.testFlag(map::AIRPORT_MSA)) // Enabled by checkbox in view + { + // Get drawing objects and cache them + bool overflow = false; + const QList *msaList = mapQuery->getAirportMsa(curBox, context->mapLayer, context->lazyUpdate, overflow); + context->setQueryOverflow(overflow); + + if(msaList != nullptr) + { + atools::util::PainterContextSaver saver(context->painter); + + for(const MapAirportMsa& msa : *msaList) + { + bool visible = wToS(msa.position, x, y /*, scale->getScreeenSizeForRect(msa.bounding)*/); + + if(!visible) + // Check bounding rect for visibility + visible = msa.bounding.overlaps(context->viewportRect); + + if(visible) + { + if(context->objCount()) + return; + + drawMsaSymbol(msa, x, y, context->drawFast); + } + } + } + } + } +} + +void MapPainterMsa::drawMsaSymbol(const map::MapAirportMsa& airportMsa, float x, float y, bool fast) +{ + atools::util::PainterContextSaver saver(context->painter); + + Marble::GeoPainter *painter = context->painter; + + if(context->mapLayer->isAirportMsaDetails() && !fast) + // Draw the full symbol with all sectors + symbolPainter->drawAirportMsa(painter, airportMsa, x, y, context->mapLayer->getAirportMsaSymbolScale(), true /* header */); + else + { + // Draw a simple underlay circle + context->painter->setPen(QPen(mapcolors::msaSymbolColor, 2, Qt::SolidLine, Qt::FlatCap)); + context->painter->setBrush(mapcolors::msaFillColor); + + float size = 10.f; + if(airportMsa.navType == map::AIRPORT) + size = context->szF(context->symbolSizeAirport, context->mapLayer->getAirportSymbolSize()); + else if(airportMsa.navType == map::VOR) + size = context->szF(context->symbolSizeNavaid, context->mapLayer->getVorSymbolSize()); + else if(airportMsa.navType == map::NDB) + size = context->szF(context->symbolSizeNavaid, context->mapLayer->getNdbSymbolSize()); + else if(airportMsa.navType == map::WAYPOINT) + size = context->szF(context->symbolSizeNavaid, context->mapLayer->getWaypointSymbolSize()); + + size = std::max(size, 6.f); + painter->drawEllipse(QPointF(x, y), size * 0.7f, size * 0.7f); + } +} diff --git a/src/mappainter/mappaintermsa.h b/src/mappainter/mappaintermsa.h new file mode 100644 index 000000000..c72a09922 --- /dev/null +++ b/src/mappainter/mappaintermsa.h @@ -0,0 +1,46 @@ +/***************************************************************************** +* Copyright 2015-2020 Alexander Barthel alex@littlenavmap.org +* +* 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 3 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, see . +*****************************************************************************/ + +#ifndef LITTLENAVMAP_MAPPAINTERMSA_H +#define LITTLENAVMAP_MAPPAINTERMSA_H + +#include "mappainter/mappainter.h" + +namespace map { +struct MapAirportMsa; +} + +/* + * Draws the airport MSA backdrop circles and details sector diagram. + */ +class MapPainterMsa : + public MapPainter +{ + Q_DECLARE_TR_FUNCTIONS(MapPainter) + +public: + MapPainterMsa(MapPaintWidget *mapPaintWidget, MapScale *mapScale, PaintContext *paintContext); + virtual ~MapPainterMsa() override; + + virtual void render() override; + +private: + void drawMsaSymbol(const map::MapAirportMsa& airportMsa, float x, float y, bool fast); + +}; + +#endif // LITTLENAVMAP_MAPPAINTERMSA_H diff --git a/src/mappainter/mappaintlayer.cpp b/src/mappainter/mappaintlayer.cpp index 91eada927..9c5dabdec 100644 --- a/src/mappainter/mappaintlayer.cpp +++ b/src/mappainter/mappaintlayer.cpp @@ -25,6 +25,7 @@ #include "mapgui/mapwidget.h" #include "mapgui/maplayersettings.h" #include "mappainter/mappainteraircraft.h" +#include "mappainter/mappaintermsa.h" #include "mappainter/mappaintertrack.h" #include "mappainter/mappaintership.h" #include "mappainter/mappainterairport.h" @@ -61,6 +62,7 @@ MapPaintLayer::MapPaintLayer(MapPaintWidget *widget) mapPainterNav = new MapPainterNav(mapWidget, mapScale, &context); mapPainterIls = new MapPainterIls(mapWidget, mapScale, &context); mapPainterAirport = new MapPainterAirport(mapWidget, mapScale, &context); + mapPainterMsa = new MapPainterMsa(mapWidget, mapScale, &context); mapPainterAirspace = new MapPainterAirspace(mapWidget, mapScale, &context); mapPainterMark = new MapPainterMark(mapWidget, mapScale, &context); mapPainterRoute = new MapPainterRoute(mapWidget, mapScale, &context); @@ -83,6 +85,7 @@ MapPaintLayer::~MapPaintLayer() delete mapPainterNav; delete mapPainterIls; delete mapPainterAirport; + delete mapPainterMsa; delete mapPainterAirspace; delete mapPainterMark; delete mapPainterRoute; @@ -180,6 +183,7 @@ void MapPaintLayer::initQueries() { mapPainterNav->initQueries(); mapPainterIls->initQueries(); + mapPainterMsa->initQueries(); mapPainterAirport->initQueries(); mapPainterAirspace->initQueries(); mapPainterMark->initQueries(); @@ -239,6 +243,7 @@ void MapPaintLayer::initMapLayerSettings() append(defLayer.clone(0.2f).airportDiagramRunway().airportDiagram(). airportDiagramDetail().airportDiagramDetail2().airportDiagramDetail3(). airportSymbolSize(20).airportInfo(). + airportMsa().airportMsaDetails().airportMsaSymbolScale(8.f). windBarbsSymbolSize(22). waypointSymbolSize(14).waypointName(). vorSymbolSize(30).vorIdent().vorInfo().vorLarge(). @@ -253,6 +258,7 @@ void MapPaintLayer::initMapLayerSettings() append(defLayer.clone(0.3f).airportDiagramRunway().airportDiagram().airportDiagramDetail().airportDiagramDetail2(). airportSymbolSize(20).airportInfo(). + airportMsa().airportMsaDetails().airportMsaSymbolScale(7.f). windBarbsSymbolSize(20). waypointSymbolSize(14).waypointName(). vorSymbolSize(30).vorIdent().vorInfo().vorLarge(). @@ -268,6 +274,7 @@ void MapPaintLayer::initMapLayerSettings() // airport diagram, large VOR, NDB, ILS, waypoint, airway, marker append(defLayer.clone(1.f).airportDiagramRunway().airportDiagram().airportDiagramDetail(). airportSymbolSize(20).airportInfo(). + airportMsa().airportMsaDetails().airportMsaSymbolScale(6.f). windBarbsSymbolSize(20). aiAircraftGroundText(false). waypointSymbolSize(14).waypointName(). @@ -284,6 +291,7 @@ void MapPaintLayer::initMapLayerSettings() // airport diagram, large VOR, NDB, ILS, waypoint, airway, marker append(defLayer.clone(5.f).airportDiagramRunway().airportDiagram(). airportSymbolSize(20).airportInfo(). + airportMsa().airportMsaDetails().airportMsaSymbolScale(5.f). waypointSymbolSize(10).waypointName(). windBarbsSymbolSize(18). aiAircraftGroundText(false). @@ -300,6 +308,7 @@ void MapPaintLayer::initMapLayerSettings() // airport, large VOR, NDB, ILS, waypoint, airway, marker append(defLayer.clone(10.f).airportDiagramRunway(). airportSymbolSize(18).airportInfo(). + airportMsa().airportMsaDetails().airportMsaSymbolScale(4.f). waypointSymbolSize(8).waypointName(). windBarbsSymbolSize(16). aiAircraftGroundText(false). @@ -316,6 +325,7 @@ void MapPaintLayer::initMapLayerSettings() // airport, large VOR, NDB, ILS, waypoint, airway, marker append(defLayer.clone(25.f).airportDiagramRunway(). airportSymbolSize(18).airportInfo(). + airportMsa(). waypointSymbolSize(8).waypointName(). windBarbsSymbolSize(16). aiAircraftGroundText(false). @@ -332,6 +342,7 @@ void MapPaintLayer::initMapLayerSettings() // airport, large VOR, NDB, ILS, airway append(defLayer.clone(50.f). airportSymbolSize(16).airportInfo(). + airportMsa(). waypointSymbolSize(6). windBarbsSymbolSize(16). aiShipSmall(false).aiAircraftGroundText(false).aiAircraftText(false). @@ -347,6 +358,7 @@ void MapPaintLayer::initMapLayerSettings() // airport, VOR, NDB, ILS, airway append(defLayer.clone(100.f). airportSymbolSize(16). + airportMsa(). waypointSymbolSize(3). windBarbsSymbolSize(14). aiAircraftGround(false).aiShipSmall(false).aiAircraftGroundText(false).aiAircraftText(false). @@ -364,6 +376,7 @@ void MapPaintLayer::initMapLayerSettings() airportSymbolSize(12).minRunwayLength(2500). airportOverviewRunway(false).airportName(false). windBarbsSymbolSize(14). + airportMsa(). approachText(false). aiAircraftGround(false).aiShipSmall(false).aiAircraftGroundText(false).aiAircraftText(false). waypoint(false). @@ -379,6 +392,7 @@ void MapPaintLayer::initMapLayerSettings() append(defLayer.clone(200.f).airportSymbolSize(12).minRunwayLength(layer::MAX_MEDIUM_RUNWAY_FT). airportOverviewRunway(false).airportName(false).airportSource(layer::MEDIUM). windBarbsSymbolSize(14). + airportMsa(). approachText(false). aiAircraftGround(false).aiShipSmall(false).aiAircraftGroundText(false).aiAircraftText(false). onlineAircraftText(false). @@ -393,6 +407,7 @@ void MapPaintLayer::initMapLayerSettings() append(defLayer.clone(300.f).airportSymbolSize(10).minRunwayLength(layer::MAX_MEDIUM_RUNWAY_FT). airportOverviewRunway(false).airportName(false).airportSource(layer::MEDIUM). windBarbsSymbolSize(12). + airportMsa(). approachText(false). aiAircraftGround(false).aiShipSmall(false). aiAircraftGroundText(false).aiAircraftText(false). @@ -692,12 +707,11 @@ bool MapPaintLayer::render(GeoPainter *painter, ViewportParams *viewport, const if(!context.isObjectOverflow()) mapPainterAirspace->render(); + if(!context.isObjectOverflow()) + mapPainterIls->render(); + if(context.mapLayer->isAirportDiagram()) { - // Put ILS below and navaids on top of airport diagram - if(!context.isObjectOverflow()) - mapPainterIls->render(); - if(!context.isObjectOverflow()) mapPainterAirport->render(); @@ -706,9 +720,8 @@ bool MapPaintLayer::render(GeoPainter *painter, ViewportParams *viewport, const } else { - // Airports on top of all if(!context.isObjectOverflow()) - mapPainterIls->render(); + mapPainterMsa->render(); if(!context.isObjectOverflow()) mapPainterNav->render(); @@ -730,6 +743,9 @@ bool MapPaintLayer::render(GeoPainter *painter, ViewportParams *viewport, const if(!context.isObjectOverflow()) mapPainterWeather->render(); + if(context.mapLayer->isAirportDiagram() && !context.isObjectOverflow()) + mapPainterMsa->render(); + if(!context.isObjectOverflow()) mapPainterTrack->render(); diff --git a/src/mappainter/mappaintlayer.h b/src/mappainter/mappaintlayer.h index d7de04a53..c7b8868d6 100644 --- a/src/mappainter/mappaintlayer.h +++ b/src/mappainter/mappaintlayer.h @@ -36,6 +36,7 @@ class MapWidget; class MapLayerSettings; class MapScale; class MapPainterAirport; +class MapPainterMsa; class MapPainterAirspace; class MapPainterNav; class MapPainterIls; @@ -189,6 +190,7 @@ class MapPaintLayer : /* All painters */ MapPainterAirport *mapPainterAirport; + MapPainterMsa *mapPainterMsa; MapPainterAirspace *mapPainterAirspace; MapPainterNav *mapPainterNav; MapPainterIls *mapPainterIls; diff --git a/src/navapp.cpp b/src/navapp.cpp index aae199c66..81f7e4c75 100644 --- a/src/navapp.cpp +++ b/src/navapp.cpp @@ -446,6 +446,11 @@ bool NavApp::isHoldingsAvailable() return getMapQueryGui()->hasHoldings(); } +bool NavApp::isAirportMsaAvailable() +{ + return getMapQueryGui()->hasAirportMsa(); +} + bool NavApp::isGlsAvailable() { return getMapQueryGui()->hasGls(); @@ -947,6 +952,11 @@ const InfoController *NavApp::getInfoController() return mainWindow->getInfoController(); } +QFont NavApp::getTextBrowserInfoFont() +{ + return getMainUi()->textBrowserAirportInfo->font(); +} + QString NavApp::getMapCopyright() { return mainWindow->getMapWidget()->getMapCopyright(); diff --git a/src/navapp.h b/src/navapp.h index b7425cb83..af70b4338 100644 --- a/src/navapp.h +++ b/src/navapp.h @@ -171,6 +171,7 @@ class NavApp : /* Check for availability in database */ static bool isMoraAvailable(); static bool isHoldingsAvailable(); + static bool isAirportMsaAvailable(); static bool isGlsAvailable(); static float getTakeoffFlownDistanceNm(); @@ -291,6 +292,7 @@ class NavApp : static RouteController *getRouteController(); static atools::gui::TabWidgetHandler *getRouteTabHandler(); static const InfoController *getInfoController(); + static QFont getTextBrowserInfoFont(); static QString getMapCopyright(); static DatabaseManager *getDatabaseManager(); diff --git a/src/query/mapquery.cpp b/src/query/mapquery.cpp index 00955a3d9..df99117cd 100644 --- a/src/query/mapquery.cpp +++ b/src/query/mapquery.cpp @@ -48,6 +48,8 @@ using map::MapIls; using map::MapParking; using map::MapHelipad; using map::MapUserpoint; +using map::MapAirportMsa; +using map::MapHolding; static double queryRectInflationFactor = 0.2; static double queryRectInflationIncrement = 0.1; @@ -94,7 +96,7 @@ MapQuery::~MapQuery() bool MapQuery::hasProcedures(const map::MapAirport& airport) { - map::MapAirport airportNav = getAirportNav(airport); + MapAirport airportNav = getAirportNav(airport); if(airportNav.isValid()) return NavApp::getAirportQueryNav()->hasProcedures(airportNav); @@ -103,7 +105,7 @@ bool MapQuery::hasProcedures(const map::MapAirport& airport) bool MapQuery::hasArrivalProcedures(const map::MapAirport& airport) { - map::MapAirport airportNav = getAirportNav(airport); + MapAirport airportNav = getAirportNav(airport); if(airportNav.isValid()) return NavApp::getAirportQueryNav()->hasArrivalProcedures(airportNav); @@ -112,7 +114,7 @@ bool MapQuery::hasArrivalProcedures(const map::MapAirport& airport) bool MapQuery::hasDepartureProcedures(const map::MapAirport& airport) { - map::MapAirport airportNav = getAirportNav(airport); + MapAirport airportNav = getAirportNav(airport); if(airportNav.isValid()) return NavApp::getAirportQueryNav()->hasDepartureProcedures(airportNav); @@ -123,7 +125,7 @@ map::MapAirport MapQuery::getAirportSim(const map::MapAirport& airport) { if(airport.navdata) { - map::MapAirport retval; + MapAirport retval; NavApp::getAirportQuerySim()->getAirportFuzzy(retval, airport); return retval; } @@ -134,7 +136,7 @@ map::MapAirport MapQuery::getAirportNav(const map::MapAirport& airport) { if(!airport.navdata) { - map::MapAirport retval; + MapAirport retval; NavApp::getAirportQueryNav()->getAirportFuzzy(retval, airport); return retval; } @@ -217,7 +219,7 @@ map::MapResultIndex *MapQuery::nearestNavaidsInternal(const Pos& pos, float dist if(type & map::VOR) { query::fetchObjectsForRect(rect, vorsByRectQuery, [ =, &res](atools::sql::SqlQuery *query) -> void { - map::MapVor obj; + MapVor obj; mapTypesFactory->fillVor(query->record(), obj); res.vors.append(obj); }); @@ -226,7 +228,7 @@ map::MapResultIndex *MapQuery::nearestNavaidsInternal(const Pos& pos, float dist if(type & map::NDB) { query::fetchObjectsForRect(rect, ndbsByRectQuery, [ =, &res](atools::sql::SqlQuery *query) -> void { - map::MapNdb obj; + MapNdb obj; mapTypesFactory->fillNdb(query->record(), obj); res.ndbs.append(obj); }); @@ -236,14 +238,14 @@ map::MapResultIndex *MapQuery::nearestNavaidsInternal(const Pos& pos, float dist { query::fetchObjectsForRect(rect, NavApp::getWaypointTrackQueryGui()->getWaypointsByRectQueryTrack(), [ =, &res](atools::sql::SqlQuery *query) -> void { - map::MapWaypoint obj; + MapWaypoint obj; mapTypesFactory->fillWaypoint(query->record(), obj, true /* track database */); res.waypoints.append(obj); }); query::fetchObjectsForRect(rect, NavApp::getWaypointTrackQueryGui()->getWaypointsByRectQuery(), [ =, &res](atools::sql::SqlQuery *query) -> void { - map::MapWaypoint obj; + MapWaypoint obj; mapTypesFactory->fillWaypoint(query->record(), obj, false /* track database */); if(!res.waypoints.contains(obj)) @@ -254,10 +256,10 @@ map::MapResultIndex *MapQuery::nearestNavaidsInternal(const Pos& pos, float dist if(type & map::ILS) { - QList ilsRes; + QList ilsRes; query::fetchObjectsForRect(rect, ilsByRectQuery, [ =, &ilsRes](atools::sql::SqlQuery *query) -> void { - map::MapIls obj; + MapIls obj; mapTypesFactory->fillIls(query->record(), obj); ilsRes.append(obj); }); @@ -304,7 +306,7 @@ void MapQuery::mapObjectByIdentInternal(map::MapResult& result, map::MapTypes ty AirportQuery *airportQuery = airportFromNavDatabase ? NavApp::getAirportQueryNav() : NavApp::getAirportQuerySim(); // Try exact ident first ===================== - map::MapAirport ap = airportQuery->getAirportByIdent(ident); + MapAirport ap = airportQuery->getAirportByIdent(ident); if(ap.isValid()) result.airports.append(ap); @@ -319,12 +321,28 @@ void MapQuery::mapObjectByIdentInternal(map::MapResult& result, map::MapTypes ty { // Try fuzzy search for nearest by official ids ===================== // Look through all fields (ICAO, IATA, FAA and local) for the given ident - QList airports = + QList airports = airportQuery->getAirportsByOfficialIdent(ident, &sortByDistancePos, maxDistanceMeter); result.airports.append(airports); } } + if(type & map::AIRPORT_MSA && airportMsaByIdentQuery != nullptr) + { + airportMsaByIdentQuery->bindValue(":navident", ident); + airportMsaByIdentQuery->bindValue(":region", region.isEmpty() ? "%" : region); + airportMsaByIdentQuery->bindValue(":airportident", airport.isEmpty() ? "%" : airport); + airportMsaByIdentQuery->exec(); + while(airportMsaByIdentQuery->next()) + { + MapAirportMsa msa; + mapTypesFactory->fillAirportMsa(airportMsaByIdentQuery->record(), msa); + result.airportMsa.append(msa); + } + maptools::sortByDistance(result.airportMsa, sortByDistancePos); + maptools::removeByDistance(result.airportMsa, sortByDistancePos, maxDistanceMeter); + } + if(type & map::VOR) { vorByIdentQuery->bindValue(":ident", ident); @@ -332,7 +350,7 @@ void MapQuery::mapObjectByIdentInternal(map::MapResult& result, map::MapTypes ty vorByIdentQuery->exec(); while(vorByIdentQuery->next()) { - map::MapVor vor; + MapVor vor; mapTypesFactory->fillVor(vorByIdentQuery->record(), vor); result.vors.append(vor); } @@ -347,7 +365,7 @@ void MapQuery::mapObjectByIdentInternal(map::MapResult& result, map::MapTypes ty ndbByIdentQuery->exec(); while(ndbByIdentQuery->next()) { - map::MapNdb ndb; + MapNdb ndb; mapTypesFactory->fillNdb(ndbByIdentQuery->record(), ndb); result.ndbs.append(ndb); } @@ -369,7 +387,7 @@ void MapQuery::mapObjectByIdentInternal(map::MapResult& result, map::MapTypes ty ilsByIdentQuery->exec(); while(ilsByIdentQuery->next()) { - map::MapIls ils; + MapIls ils; mapTypesFactory->fillIls(ilsByIdentQuery->record(), ils); result.ils.append(ils); } @@ -394,39 +412,45 @@ void MapQuery::getMapObjectById(map::MapResult& result, map::MapTypes type, map: { if(type == map::AIRPORT) { - map::MapAirport airport = (airportFromNavDatabase ? - NavApp::getAirportQueryNav() : - NavApp::getAirportQuerySim())->getAirportById(id); + MapAirport airport = (airportFromNavDatabase ? + NavApp::getAirportQueryNav() : + NavApp::getAirportQuerySim())->getAirportById(id); if(airport.isValid()) result.airports.append(airport); } + else if(type == map::AIRPORT_MSA) + { + MapAirportMsa msa = getAirportMsaById(id); + if(msa.isValid()) + result.airportMsa.append(msa); + } else if(type == map::VOR) { - map::MapVor vor = getVorById(id); + MapVor vor = getVorById(id); if(vor.isValid()) result.vors.append(vor); } else if(type == map::NDB) { - map::MapNdb ndb = getNdbById(id); + MapNdb ndb = getNdbById(id); if(ndb.isValid()) result.ndbs.append(ndb); } else if(type == map::HOLDING) { - map::MapHolding holding = getHoldingById(id); + MapHolding holding = getHoldingById(id); if(holding.isValid()) result.holdings.append(holding); } else if(type == map::WAYPOINT) { - map::MapWaypoint waypoint = NavApp::getWaypointTrackQueryGui()->getWaypointById(id); + MapWaypoint waypoint = NavApp::getWaypointTrackQueryGui()->getWaypointById(id); if(waypoint.isValid()) result.waypoints.append(waypoint); } else if(type == map::USERPOINT) { - map::MapUserpoint userPoint = NavApp::getUserdataController()->getUserpointById(id); + MapUserpoint userPoint = NavApp::getUserdataController()->getUserpointById(id); if(userPoint.isValid()) result.userpoints.append(userPoint); } @@ -438,7 +462,7 @@ void MapQuery::getMapObjectById(map::MapResult& result, map::MapTypes type, map: } else if(type == map::ILS) { - map::MapIls ils = getIlsById(id); + MapIls ils = getIlsById(id); if(ils.isValid()) result.ils.append(ils); } @@ -472,7 +496,7 @@ void MapQuery::getMapObjectById(map::MapResult& result, map::MapTypes type, map: map::MapVor MapQuery::getVorById(int id) { - map::MapVor vor; + MapVor vor; vorByIdQuery->bindValue(":id", id); vorByIdQuery->exec(); if(vorByIdQuery->next()) @@ -483,7 +507,7 @@ map::MapVor MapQuery::getVorById(int id) map::MapNdb MapQuery::getNdbById(int id) { - map::MapNdb ndb; + MapNdb ndb; ndbByIdQuery->bindValue(":id", id); ndbByIdQuery->exec(); if(ndbByIdQuery->next()) @@ -494,7 +518,7 @@ map::MapNdb MapQuery::getNdbById(int id) map::MapIls MapQuery::getIlsById(int id) { - map::MapIls ils; + MapIls ils; ilsByIdQuery->bindValue(":id", id); ilsByIdQuery->exec(); if(ilsByIdQuery->next()) @@ -503,9 +527,23 @@ map::MapIls MapQuery::getIlsById(int id) return ils; } +map::MapAirportMsa MapQuery::getAirportMsaById(int id) +{ + MapAirportMsa msa; + if(airportMsaByIdQuery != nullptr) + { + airportMsaByIdQuery->bindValue(":id", id); + airportMsaByIdQuery->exec(); + if(airportMsaByIdQuery->next()) + mapTypesFactory->fillAirportMsa(airportMsaByIdQuery->record(), msa); + airportMsaByIdQuery->finish(); + } + return msa; +} + map::MapHolding MapQuery::getHoldingById(int id) { - map::MapHolding holding; + MapHolding holding; if(holdingByIdQuery != nullptr) { holdingByIdQuery->bindValue(":id", id); @@ -519,7 +557,7 @@ map::MapHolding MapQuery::getHoldingById(int id) QVector MapQuery::getIlsByAirportAndRunway(const QString& airportIdent, const QString& runway) { - QVector ils; + QVector ils; for(const QString& rname : atools::fs::util::runwayNameZeroPrefixVariants(runway)) { ils = ilsByAirportAndRunway(airportIdent, rname); @@ -531,13 +569,13 @@ QVector MapQuery::getIlsByAirportAndRunway(const QString& airportId QVector MapQuery::getIlsByAirportAndIdent(const QString& airportIdent, const QString& ilsIdent) { - QVector ilsList; + QVector ilsList; ilsQuerySimByAirportAndIdent->bindValue(":apt", airportIdent); ilsQuerySimByAirportAndIdent->bindValue(":ident", ilsIdent); ilsQuerySimByAirportAndIdent->exec(); while(ilsQuerySimByAirportAndIdent->next()) { - map::MapIls ils; + MapIls ils; mapTypesFactory->fillIls(ilsQuerySimByAirportAndIdent->record(), ils); ilsList.append(ils); } @@ -546,13 +584,13 @@ QVector MapQuery::getIlsByAirportAndIdent(const QString& airportIdent, c QVector MapQuery::ilsByAirportAndRunway(const QString& airportIdent, const QString& runway) { - QVector ilsList; + QVector ilsList; ilsQuerySimByAirportAndRw->bindValue(":apt", airportIdent); ilsQuerySimByAirportAndRw->bindValue(":rwy", runway); ilsQuerySimByAirportAndRw->exec(); while(ilsQuerySimByAirportAndRw->next()) { - map::MapIls ils; + MapIls ils; mapTypesFactory->fillIls(ilsQuerySimByAirportAndRw->record(), ils); ilsList.append(ils); } @@ -590,6 +628,17 @@ void MapQuery::getNearestScreenObjects(const CoordinateConverter& conv, const Ma } } + if(mapLayer->isAirportMsa() && types.testFlag(map::AIRPORT_MSA)) + { + for(int i = airportMsaCache.list.size() - 1; i >= 0; i--) + { + const MapAirportMsa& msa = airportMsaCache.list.at(i); + if(conv.wToS(msa.position, x, y)) + if((atools::geo::manhattanDistance(x, y, xs, ys)) < screenDistance) + insertSortedByDistance(conv, result.airportMsa, &result.airportMsaIds, xs, ys, msa); + } + } + if(mapLayer->isVor() && types.testFlag(map::VOR)) { for(int i = vorCache.list.size() - 1; i >= 0; i--) @@ -616,7 +665,7 @@ void MapQuery::getNearestScreenObjects(const CoordinateConverter& conv, const Ma { for(int i = holdingCache.list.size() - 1; i >= 0; i--) { - const map::MapHolding& holding = holdingCache.list.at(i); + const MapHolding& holding = holdingCache.list.at(i); if(conv.wToS(holding.position, x, y)) if((atools::geo::manhattanDistance(x, y, xs, ys)) < screenDistance) insertSortedByDistance(conv, result.holdings, &result.holdingIds, xs, ys, holding); @@ -668,7 +717,7 @@ void MapQuery::getNearestScreenObjects(const CoordinateConverter& conv, const Ma { if(airportDiagram) { - QHash > parkingCache = NavApp::getAirportQuerySim()->getParkingCache(); + QHash > parkingCache = NavApp::getAirportQuerySim()->getParkingCache(); // Also check parking and helipads in airport diagrams for(const QList& parkings : parkingCache) @@ -680,7 +729,7 @@ void MapQuery::getNearestScreenObjects(const CoordinateConverter& conv, const Ma } } - QHash > helipadCache = NavApp::getAirportQuerySim()->getHelipadCache(); + QHash > helipadCache = NavApp::getAirportQuerySim()->getHelipadCache(); for(const QList& helipads : helipadCache) { @@ -749,7 +798,7 @@ const QList *MapQuery::getVors(const GeoDataLatLonBox& rect, const vorsByRectQuery->exec(); while(vorsByRectQuery->next()) { - map::MapVor vor; + MapVor vor; mapTypesFactory->fillVor(vorsByRectQuery->record(), vor); vorCache.list.append(vor); } @@ -777,7 +826,7 @@ const QList *MapQuery::getNdbs(const GeoDataLatLonBox& rect, const ndbsByRectQuery->exec(); while(ndbsByRectQuery->next()) { - map::MapNdb ndb; + MapNdb ndb; mapTypesFactory->fillNdb(ndbsByRectQuery->record(), ndb); ndbCache.list.append(ndb); } @@ -792,7 +841,7 @@ const QList MapQuery::getUserdataPoints(const GeoDataLatLonBo float distance) { // No caching here since points can change and the dataset is usually small - QList retval; + QList retval; userpointCache.clear(); // Display either unknown or any type @@ -829,7 +878,7 @@ const QList MapQuery::getUserdataPoints(const GeoDataLatLonBo continue; } - map::MapUserpoint userPoint; + MapUserpoint userPoint; mapTypesFactory->fillUserdataPoint(userdataPointByRectQuery->record(), userPoint); retval.append(userPoint); @@ -921,11 +970,10 @@ const QList *MapQuery::getHoldings(const Marble::GeoDataLatLonB { if(holdingByRectQuery != nullptr) { - holdingCache.updateCache(rect, mapLayer, queryRectInflationFactor, queryRectInflationIncrement, lazy, [](const MapLayer *curLayer, const MapLayer *newLayer) -> bool { - return curLayer->hasSameQueryParametersMarker(newLayer); + return curLayer->hasSameQueryParametersHolding(newLayer); }); if(holdingCache.list.isEmpty() && !lazy) @@ -937,7 +985,7 @@ const QList *MapQuery::getHoldings(const Marble::GeoDataLatLonB holdingByRectQuery->exec(); while(holdingByRectQuery->next()) { - map::MapHolding holding; + MapHolding holding; mapTypesFactory->fillHolding(holdingByRectQuery->record(), holding); holdingCache.list.append(holding); } @@ -949,6 +997,39 @@ const QList *MapQuery::getHoldings(const Marble::GeoDataLatLonB return nullptr; } +const QList *MapQuery::getAirportMsa(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, + bool& overflow) +{ + if(airportMsaByRectQuery != nullptr) + { + airportMsaCache.updateCache(rect, mapLayer, queryRectInflationFactor, queryRectInflationIncrement, lazy, + [](const MapLayer *curLayer, const MapLayer *newLayer) -> bool + { + return curLayer->hasSameQueryParametersAirportMsa(newLayer); + }); + + if(airportMsaCache.list.isEmpty() && !lazy) + { + for(const GeoDataLatLonBox& r : + query::splitAtAntiMeridian(rect, queryRectInflationFactor, queryRectInflationIncrement)) + { + query::bindRect(r, airportMsaByRectQuery); + + airportMsaByRectQuery->exec(); + while(airportMsaByRectQuery->next()) + { + MapAirportMsa msa; + mapTypesFactory->fillAirportMsa(airportMsaByRectQuery->record(), msa); + airportMsaCache.list.append(msa); + } + } + } + overflow = airportMsaCache.validate(queryMaxRows); + return &airportMsaCache.list; + } + return nullptr; +} + const QList *MapQuery::getIls(GeoDataLatLonBox rect, const MapLayer *mapLayer, bool lazy, bool& overflow) { ilsCache.updateCache(rect, mapLayer, queryRectInflationFactor, queryRectInflationIncrement, lazy, @@ -974,7 +1055,7 @@ const QList *MapQuery::getIls(GeoDataLatLonBox rect, const MapLayer ilsByRectQuery->exec(); while(ilsByRectQuery->next()) { - map::MapIls ils; + MapIls ils; mapTypesFactory->fillIls(ilsByRectQuery->record(), ils); ilsCache.list.append(ils); } @@ -1011,7 +1092,7 @@ const QList *MapQuery::fetchAirports(const Marble::GeoDataLatLo query->exec(); while(query->next()) { - map::MapAirport ap; + MapAirport ap; if(overview) // Fill only a part of the object mapTypesFactory->fillAirportForOverview(query->record(), ap, navdata, @@ -1032,7 +1113,7 @@ const QList *MapQuery::fetchAirports(const Marble::GeoDataLatLo airportAddonByRectQuery->exec(); while(airportAddonByRectQuery->next()) { - map::MapAirport ap; + MapAirport ap; if(overview) // Fill only a part of the object mapTypesFactory->fillAirportForOverview(airportAddonByRectQuery->record(), ap, navdata, @@ -1132,31 +1213,31 @@ void MapQuery::runwayEndByNameFuzzy(QList& runwayEnds, const void MapQuery::initQueries() { // Common where clauses - static const QString whereRect("lonx between :leftx and :rightx and laty between :bottomy and :topy"); - static const QString whereIdentRegion("ident = :ident and region like :region"); + static const QLatin1String whereRect("lonx between :leftx and :rightx and laty between :bottomy and :topy"); + static const QLatin1String whereIdentRegion("ident = :ident and region like :region"); static const QString whereLimit("limit " + QString::number(queryMaxRows)); // Common select statements QStringList const airportQueryBase = AirportQuery::airportColumns(dbSim); QStringList const airportQueryBaseOverview = AirportQuery::airportOverviewColumns(dbSim); - static const QString vorQueryBase( - "vor_id, ident, name, region, type, name, frequency, channel, range, dme_only, dme_altitude, " - "mag_var, altitude, lonx, laty "); - static const QString ndbQueryBase( - "ndb_id, ident, name, region, type, name, frequency, range, mag_var, altitude, lonx, laty "); + static const QLatin1String vorQueryBase("vor_id, ident, name, region, type, name, frequency, channel, range, dme_only, dme_altitude, " + "mag_var, altitude, lonx, laty "); + static const QLatin1String ndbQueryBase("ndb_id, ident, name, region, type, name, frequency, range, mag_var, altitude, lonx, laty "); + + QString ilsQueryBase("ils_id, ident, name, region, mag_var, loc_heading, has_backcourse, loc_runway_end_id, loc_airport_ident, " + "loc_runway_name, gs_pitch, frequency, range, dme_range, loc_width, " + "end1_lonx, end1_laty, end_mid_lonx, end_mid_laty, end2_lonx, end2_laty, altitude, lonx, laty"); - QString ilsQueryBase( - "ils_id, ident, name, region, mag_var, loc_heading, has_backcourse, loc_runway_end_id, loc_airport_ident, " - "loc_runway_name, gs_pitch, frequency, range, dme_range, loc_width, " - "end1_lonx, end1_laty, end_mid_lonx, end_mid_laty, end2_lonx, end2_laty, altitude, lonx, laty"); + static const QLatin1String holdingQueryBase("holding_id, airport_ident, nav_ident, nav_type, vor_type, vor_dme_only, vor_has_dme, " + "name, mag_var, course, turn_direction, leg_length, leg_time, " + "minimum_altitude, maximum_altitude, speed_limit, lonx, laty"); - static const QString holdingQueryBase( - "holding_id, airport_ident, nav_ident, nav_type, vor_type, vor_dme_only, vor_has_dme, name, mag_var, " - "course, turn_direction, leg_length, leg_time, minimum_altitude, maximum_altitude, speed_limit, lonx, laty"); + static const QLatin1String msaQueryBase("airport_msa_id, airport_ident, nav_ident, nav_type, vor_type, " + "vor_dme_only, vor_has_dme, region, multiple_code, true_bearing, mag_var, " + "left_lonx, top_laty, right_lonx, bottom_laty, radius, lonx, laty, geometry "); - QString extraIlsCols = SqlUtil(dbSim).buildColumnListIf( - "ils", {"type", "perf_indicator", "provider"}).join(", "); + QString extraIlsCols = SqlUtil(dbSim).buildColumnListIf("ils", {"type", "perf_indicator", "provider"}).join(", "); if(!extraIlsCols.isEmpty()) ilsQueryBase.append(", " + extraIlsCols); @@ -1166,6 +1247,7 @@ void MapQuery::initQueries() SqlDatabase *holdingDb = SqlUtil::getDbWithTableAndRows("holding", {dbNav, dbSim}); qDebug() << Q_FUNC_INFO << "Holding database" << (holdingDb == nullptr ? "None" : holdingDb->databaseName()); + // Same as above for airport MSA table SqlDatabase *msaDb = SqlUtil::getDbWithTableAndRows("airport_msa", {dbNav, dbSim}); qDebug() << Q_FUNC_INFO << "Airport MSA database" << (msaDb == nullptr ? "None" : msaDb->databaseName()); @@ -1225,10 +1307,8 @@ void MapQuery::initQueries() " from ils where loc_airport_ident = :apt and ident = :ident"); airportByRectQuery = new SqlQuery(dbSim); - airportByRectQuery->prepare( - "select " + airportQueryBase.join(", ") + " from airport where " + whereRect + - " and longest_runway_length >= :minlength " - + whereLimit); + airportByRectQuery->prepare("select " + airportQueryBase.join(", ") + " from airport where " + whereRect + + " and longest_runway_length >= :minlength " + whereLimit); airportAddonByRectQuery = new SqlQuery(dbSim); airportAddonByRectQuery->prepare( @@ -1254,6 +1334,19 @@ void MapQuery::initQueries() ndbsByRectQuery = new SqlQuery(dbNav); ndbsByRectQuery->prepare("select " + ndbQueryBase + " from ndb where " + whereRect + " " + whereLimit); + if(msaDb != nullptr) + { + airportMsaByRectQuery = new SqlQuery(msaDb); + airportMsaByRectQuery->prepare("select " + msaQueryBase + " from airport_msa where " + whereRect + " " + whereLimit); + + airportMsaByIdentQuery = new SqlQuery(msaDb); + airportMsaByIdentQuery->prepare("select " + msaQueryBase + " from airport_msa " + + "where airport_ident like :airportident and nav_ident like :navident and nav_type like :navtype"); + + airportMsaByIdQuery = new SqlQuery(msaDb); + airportMsaByIdQuery->prepare("select " + msaQueryBase + " from airport_msa where airport_msa_id = :id"); + } + userdataPointByRectQuery = new SqlQuery(dbUser); userdataPointByRectQuery->prepare("select * from userdata " "where " + whereRect + " and visible_from > :dist and type like :type " + @@ -1282,6 +1375,7 @@ void MapQuery::initQueries() void MapQuery::deInitQueries() { airportCache.clear(); + airportMsaCache.clear(); vorCache.clear(); ndbCache.clear(); markerCache.clear(); @@ -1348,4 +1442,11 @@ void MapQuery::deInitQueries() delete ilsQuerySimByAirportAndIdent; ilsQuerySimByAirportAndIdent = nullptr; + + delete airportMsaByIdentQuery; + airportMsaByIdentQuery = nullptr; + delete airportMsaByRectQuery; + airportMsaByRectQuery = nullptr; + delete airportMsaByIdQuery; + airportMsaByIdQuery = nullptr; } diff --git a/src/query/mapquery.h b/src/query/mapquery.h index 9315c79b3..0df0e1087 100644 --- a/src/query/mapquery.h +++ b/src/query/mapquery.h @@ -84,6 +84,9 @@ class MapQuery /* Always from sim db */ map::MapIls getIlsById(int id); + /* Either nav or sim db */ + map::MapAirportMsa getAirportMsaById(int id); + /* True if table ils contains GLS/RNP approaches - GLS ground stations or GBAS threshold points */ bool hasGls() const { @@ -99,6 +102,12 @@ class MapQuery return holdingByIdQuery != nullptr; } + /* True if table is present in schema and has one row */ + bool hasAirportMsa() const + { + return airportMsaByIdQuery != nullptr; + } + /* Get ILS from sim database based on airport ident and runway name. * Runway name can be zero prefixed or prefixed with "RW". */ QVector getIlsByAirportAndRunway(const QString& airportIdent, const QString& runway); @@ -180,20 +189,19 @@ class MapQuery map::MapTypes types, bool& overflow); /* Similar to getAirports */ - const QList *getVors(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, - bool& overflow); + const QList *getVors(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, bool& overflow); /* Similar to getAirports */ - const QList *getNdbs(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, - bool& overflow); + const QList *getNdbs(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, bool& overflow); /* Similar to getAirports */ - const QList *getMarkers(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, - bool& overflow); + const QList *getMarkers(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, bool& overflow); /* Similar to getAirports */ - const QList *getHoldings(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, - bool& overflow); + const QList *getHoldings(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, bool& overflow); + + /* As above */ + const QList *getAirportMsa(const Marble::GeoDataLatLonBox& rect, const MapLayer *mapLayer, bool lazy, bool& overflow); /* Similar to getAirports */ const QList *getIls(Marble::GeoDataLatLonBox rect, const MapLayer *mapLayer, bool lazy, bool& overflow); @@ -203,8 +211,7 @@ class MapQuery /* Similar to getAirports but no caching since user points can change */ const QList getUserdataPoints(const Marble::GeoDataLatLonBox& rect, const QStringList& types, - const QStringList& typesAll, - bool unknownType, float distance); + const QStringList& typesAll, bool unknownType, float distance); /* Get related airport for navaids from current nav database. * found is true if navaid search was successful and max distance to pos is not exceeded. */ @@ -256,6 +263,7 @@ class MapQuery query::SimpleRectCache markerCache; query::SimpleRectCache holdingCache; query::SimpleRectCache ilsCache; + query::SimpleRectCache airportMsaCache; bool gls = false; @@ -268,7 +276,8 @@ class MapQuery /* Database queries */ atools::sql::SqlQuery *runwayOverviewQuery = nullptr, *airportByRectQuery = nullptr, *airportAddonByRectQuery = nullptr, - *airportMediumByRectQuery = nullptr, *airportLargeByRectQuery = nullptr; + *airportMediumByRectQuery = nullptr, *airportLargeByRectQuery = nullptr, + *airportMsaByRectQuery = nullptr, *airportMsaByIdentQuery = nullptr, *airportMsaByIdQuery = nullptr; atools::sql::SqlQuery *vorsByRectQuery = nullptr, *ndbsByRectQuery = nullptr, *markersByRectQuery = nullptr, *ilsByRectQuery = nullptr, *holdingByRectQuery = nullptr, *userdataPointByRectQuery = nullptr; diff --git a/src/route/routecontroller.cpp b/src/route/routecontroller.cpp index 435265a6a..33753012c 100644 --- a/src/route/routecontroller.cpp +++ b/src/route/routecontroller.cpp @@ -2144,7 +2144,6 @@ void RouteController::showInformationMenu() void RouteController::showInformationInternal(const RouteLeg& routeLeg) { - if(routeLeg.isAnyProcedure()) { if(routeLeg.getProcedureLeg().navaids.hasTypes(map::AIRPORT | map::WAYPOINT | map::VOR | map::NDB))