Skip to content

Commit

Permalink
Fixed not working airport procedure filter #1055
Browse files Browse the repository at this point in the history
Examples EDFQ, EDGS, EDME and EKSN with MSFS and Navigraph in mixed data mode.
Fixed issue where cache was not cleaned when switching databases resulting in wrong nearest airport
with procedures indications.
  • Loading branch information
albar965 committed Oct 3, 2023
1 parent 03da99a commit 48fff01
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 78 deletions.
55 changes: 27 additions & 28 deletions src/common/htmlinfobuilder.cpp
Expand Up @@ -552,15 +552,17 @@ void HtmlInfoBuilder::nearestText(const MapAirport& airport, HtmlBuilder& html)
airportTitle(airport, html, -1, true /* procedures */);

// Get nearest airports that have procedures ====================================
const MapResultIndex *nearestAirports =
const MapResultIndex *nearestAirportsNav =
airportQueryNav->getNearestProcAirports(airport.position, airport.ident, NEAREST_MAX_DISTANCE_AIRPORT_NM);
if(!nearestMapObjectsText(airport, html, nearestAirports, tr("Nearest Airports with Procedures"), false, true, NEAREST_MAX_NUM_AIRPORT))

if(!nearestMapObjectsText(airport, html, nearestAirportsNav, tr("Nearest Airports with Procedures"), false, true,
NEAREST_MAX_NUM_AIRPORT))
html.p().b(tr("No airports with procedures within a radius of %1.").arg(Unit::distNm(NEAREST_MAX_DISTANCE_AIRPORT_NM * 4.f))).pEnd();

// Get nearest VOR and NDB ====================================
MapResultIndex *nearestNavaids =
mapWidget->getMapQuery()->getNearestNavaids(airport.position, NEAREST_MAX_DISTANCE_NAVAID_NM,
map::VOR | map::NDB | map::ILS, 3 /* max ILS */, 4.f /* max ILS dist NM */);
map::VOR | map::NDB | map::ILS, 3 /* maxIls */, 4.f /* maxIlsDistNm */);

if(!nearestMapObjectsText(airport, html, nearestNavaids, tr("Nearest Radio Navaids"), true, false, NEAREST_MAX_NUM_NAVAID))
html.p().b(tr("No navaids within a radius of %1.").arg(Unit::distNm(NEAREST_MAX_DISTANCE_NAVAID_NM * 4.f))).pEnd();
Expand Down Expand Up @@ -608,11 +610,10 @@ void HtmlInfoBuilder::nearestMapObjectsTextRow(const MapAirport& airport, HtmlBu
trEnd();
}

bool HtmlInfoBuilder::nearestMapObjectsText(const MapAirport& airport, HtmlBuilder& html,
const map::MapResultIndex *nearest, const QString& header,
bool frequencyCol, bool airportCol, int maxRows) const
bool HtmlInfoBuilder::nearestMapObjectsText(const MapAirport& airport, HtmlBuilder& html, const map::MapResultIndex *nearestNav,
const QString& header, bool frequencyCol, bool airportCol, int maxRows) const
{
if(nearest != nullptr && !nearest->isEmpty())
if(nearestNav != nullptr && !nearestNav->isEmpty())
{
html.br().br().text(header, ahtml::BOLD | ahtml::BIG);
html.table();
Expand All @@ -636,46 +637,44 @@ bool HtmlInfoBuilder::nearestMapObjectsText(const MapAirport& airport, HtmlBuild

// Go through mixed list of map objects ============================================
int row = 1;
for(const map::MapBase *base : *nearest)
for(const map::MapBase *baseNav : *nearestNav)
{
if(row++ > maxRows)
// Stop at max
break;

// Airport ======================================
const map::MapAirport *ap = base->asPtr<map::MapAirport>();
if(ap != nullptr)
const map::MapAirport *apNav = baseNav->asPtr<map::MapAirport>();
if(apNav != nullptr)
{
// Airport comes from navdatabase having procedures - convert to simulator airport to get sim name
// Convert navdatabase airport to simulator
map::MapAirport simAp = mapWidget->getMapQuery()->getAirportSim(*ap);
map::MapAirport apSim = mapWidget->getMapQuery()->getAirportSim(*apNav);

// Omit center airport used as reference
if(simAp.isValid() && simAp.id != airport.id)
nearestMapObjectsTextRow(airport, html, QString(), simAp.displayIdent(), simAp.name,
QString(), &simAp, simAp.magvar, frequencyCol, airportCol);
if(apSim.isValid() && apSim.id != airport.id)
nearestMapObjectsTextRow(airport, html, QString(), apSim.displayIdent(), apSim.name, QString(), &apSim, apSim.magvar,
frequencyCol, airportCol);
}

const map::MapVor *vor = base->asPtr<map::MapVor>();
const map::MapVor *vor = baseNav->asPtr<map::MapVor>();
if(vor != nullptr)
nearestMapObjectsTextRow(airport, html, map::vorType(*vor), vor->ident, vor->name,
locale.toString(vor->frequency / 1000., 'f',
2), vor, vor->magvar, frequencyCol, airportCol);
nearestMapObjectsTextRow(airport, html, map::vorType(*vor), vor->ident, vor->name, locale.toString(vor->frequency / 1000., 'f', 2),
vor, vor->magvar, frequencyCol, airportCol);

const map::MapNdb *ndb = base->asPtr<map::MapNdb>();
const map::MapNdb *ndb = baseNav->asPtr<map::MapNdb>();
if(ndb != nullptr)
nearestMapObjectsTextRow(airport, html, tr("NDB"), ndb->ident, ndb->name,
locale.toString(ndb->frequency / 100., 'f',
1), ndb, ndb->magvar, frequencyCol, airportCol);
nearestMapObjectsTextRow(airport, html, tr("NDB"), ndb->ident, ndb->name, locale.toString(ndb->frequency / 100., 'f', 1),
ndb, ndb->magvar, frequencyCol, airportCol);

const map::MapWaypoint *waypoint = base->asPtr<map::MapWaypoint>();
const map::MapWaypoint *waypoint = baseNav->asPtr<map::MapWaypoint>();
if(waypoint != nullptr)
nearestMapObjectsTextRow(airport, html, tr("Waypoint"), waypoint->ident, QString(),
QString(), waypoint, waypoint->magvar, frequencyCol, airportCol);
nearestMapObjectsTextRow(airport, html, tr("Waypoint"), waypoint->ident, QString(), QString(), waypoint, waypoint->magvar,
frequencyCol, airportCol);

const map::MapIls *ils = base->asPtr<map::MapIls>();
const map::MapIls *ils = baseNav->asPtr<map::MapIls>();
if(ils != nullptr && !ils->isAnyGlsRnp())
nearestMapObjectsTextRow(airport, html, map::ilsType(*ils, true /* gs */, true /* dme */, tr(", ")),
ils->ident, ils->name,
nearestMapObjectsTextRow(airport, html, map::ilsType(*ils, true /* gs */, true /* dme */, tr(", ")), ils->ident, ils->name,
ils->freqMHzLocale(), ils, ils->magvar, frequencyCol, airportCol);
}
html.tableEnd();
Expand Down
2 changes: 1 addition & 1 deletion src/common/htmlinfobuilder.h
Expand Up @@ -326,7 +326,7 @@ class HtmlInfoBuilder
void head(atools::util::HtmlBuilder& html, const QString& text) const;

bool nearestMapObjectsText(const map::MapAirport& airport, atools::util::HtmlBuilder& html,
const map::MapResultIndex *nearest, const QString& header, bool frequencyCol,
const map::MapResultIndex *nearestNav, const QString& header, bool frequencyCol,
bool airportCol,
int maxRows) const;
void nearestMapObjectsTextRow(const map::MapAirport& airport, atools::util::HtmlBuilder& html, const QString& type,
Expand Down
15 changes: 2 additions & 13 deletions src/common/maptypesfactory.cpp
Expand Up @@ -23,24 +23,11 @@
#include "io/binaryutil.h"
#include "sql/sqlrecord.h"
#include "fs/util/fsutil.h"
#include "app/navapp.h"

#include <cmath>

using namespace atools::geo;
using atools::sql::SqlRecord;
using namespace map;

MapTypesFactory::MapTypesFactory()
{

}

MapTypesFactory::~MapTypesFactory()
{

}

void MapTypesFactory::fillAirport(const SqlRecord& record, map::MapAirport& airport, bool complete, bool nav, bool xplane)
{
fillAirportBase(record, airport, complete);
Expand Down Expand Up @@ -187,6 +174,8 @@ map::MapAirportFlags MapTypesFactory::fillAirportFlags(const SqlRecord& record,

if(!overview)
{
// The procedure flag is not accurate for mixed mode databases and is updated later on by
// AirportQuery::hasAirportProcedures()
flags |= airportFlag(record, "num_approach", AP_PROCEDURE);
flags |= airportFlag(record, "num_runway_light", AP_LIGHT);
flags |= airportFlag(record, "num_runway_end_ils", AP_ILS);
Expand Down
11 changes: 2 additions & 9 deletions src/common/maptypesfactory.h
@@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright 2015-2020 Alexander Barthel alex@littlenavmap.org
* Copyright 2015-2023 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
Expand Down Expand Up @@ -54,9 +54,6 @@ struct MapAirportMsa;
class MapTypesFactory
{
public:
MapTypesFactory();
~MapTypesFactory();

/*
* Populate airport object.
* @param complete if false only id and position are present in the record. Used for creating the object
Expand Down Expand Up @@ -102,13 +99,9 @@ class MapTypesFactory

private:
void fillVorBase(const atools::sql::SqlRecord& record, map::MapVor& vor);

void fillAirportBase(const atools::sql::SqlRecord& record, map::MapAirport& ap, bool complete);

map::MapAirportFlags airportFlag(const atools::sql::SqlRecord& record, const QString& field,
map::MapAirportFlags airportFlag);
map::MapAirportFlags airportFlag(const atools::sql::SqlRecord& record, const QString& field, map::MapAirportFlags airportFlag);
map::MapAirportFlags fillAirportFlags(const atools::sql::SqlRecord& record, bool overview);

map::MapType strToType(const QString& navType);

};
Expand Down
65 changes: 59 additions & 6 deletions src/query/airportquery.cpp
Expand Up @@ -101,6 +101,41 @@ AirportQuery::~AirportQuery()
delete mapTypesFactory;
}

void AirportQuery::loadAirportProcedureCache()
{
// Load all airport idents having procedures from navdatabase
airportsWithProceduresIdent.clear();
airportsWithProceduresIata.clear();

if(navdata && NavApp::isNavdataMixed())
{
SqlQuery query(db);
query.exec("select ident, iata from airport where num_approach > 0");
while(query.next())
{
airportsWithProceduresIdent.insert(query.valueStr(0));
airportsWithProceduresIata.insert(query.valueStr(1));
}
}
}

void AirportQuery::correctAirportProcedureFlag(MapAirport& airport)
{
if(!airportsWithProceduresIdent.isEmpty())
airport.flags.setFlag(map::AP_PROCEDURE, hasAirportProcedures(airport.ident, airport.iata));
}

bool AirportQuery::hasAirportProcedures(const QString& ident, const QString& iata)
{
if(!ident.isEmpty() && airportsWithProceduresIdent.contains(ident))
return true;

if(!iata.isEmpty() && airportsWithProceduresIata.contains(iata))
return true;

return false;
}

void AirportQuery::getAirportAdminNamesById(int airportId, QString& city, QString& state, QString& country)
{
if(!query::valid(Q_FUNC_INFO, airportAdminByIdQuery))
Expand Down Expand Up @@ -144,6 +179,9 @@ void AirportQuery::getAirportById(map::MapAirport& airport, int airportId)
mapTypesFactory->fillAirport(airportByIdQuery->record(), *ap, true /* complete */, navdata, NavApp::isAirportDatabaseXPlane(navdata));
airportByIdQuery->finish();

if(!navdata)
NavApp::getAirportQueryNav()->correctAirportProcedureFlag(ap);

airport = *ap;
airportIdCache.insert(airportId, ap);
}
Expand Down Expand Up @@ -177,6 +215,8 @@ void AirportQuery::getAirportByIdent(map::MapAirport& airport, const QString& id
mapTypesFactory->fillAirport(airportByIdentQuery->record(), *ap, true /* complete */, navdata,
NavApp::isAirportDatabaseXPlane(navdata));
airportByIdentQuery->finish();
if(!navdata)
NavApp::getAirportQueryNav()->correctAirportProcedureFlag(ap);

airport = *ap;
airportIdentCache.insert(ident, ap);
Expand All @@ -202,10 +242,12 @@ void AirportQuery::getAirportsByTruncatedIdent(QList<map::MapAirport>& airports,
airportsByTruncatedIdentQuery->exec();
while(airportsByTruncatedIdentQuery->next())
{
map::MapAirport ap;
mapTypesFactory->fillAirport(airportsByTruncatedIdentQuery->record(), ap, true /* complete */, navdata,
map::MapAirport airport;
mapTypesFactory->fillAirport(airportsByTruncatedIdentQuery->record(), airport, true /* complete */, navdata,
NavApp::isAirportDatabaseXPlane(navdata));
airports.append(ap);
if(!navdata)
NavApp::getAirportQueryNav()->correctAirportProcedureFlag(airport);
airports.append(airport);
}
}

Expand Down Expand Up @@ -264,6 +306,8 @@ void AirportQuery::getAirportsByOfficialIdent(QList<map::MapAirport>& airports,
map::MapAirport airport;
mapTypesFactory->fillAirport(airportByOfficialQuery->record(), airport, true /* complete */, navdata,
NavApp::isAirportDatabaseXPlane(navdata));
if(!navdata)
NavApp::getAirportQueryNav()->correctAirportProcedureFlag(airport);
airports.append(airport);
}
}
Expand Down Expand Up @@ -331,9 +375,11 @@ void AirportQuery::getAirportFuzzy(map::MapAirport& airport, const map::MapAirpo
ageo::Rect rect(airportCopy.position, ageo::nmToMeter(10.f), true /* fast */);

query::fetchObjectsForRect(rect, airportByPosQuery, [ =, &airports](atools::sql::SqlQuery *query) -> void {
map::MapAirport obj;
mapTypesFactory->fillAirport(query->record(), obj, true /* complete */, navdata, NavApp::isAirportDatabaseXPlane(navdata));
airports.append(obj);
map::MapAirport a;
mapTypesFactory->fillAirport(query->record(), a, true /* complete */, navdata, NavApp::isAirportDatabaseXPlane(navdata));
if(!navdata)
NavApp::getAirportQueryNav()->correctAirportProcedureFlag(ap);
airports.append(a);
});
}

Expand Down Expand Up @@ -948,6 +994,8 @@ void AirportQuery::getRunwaysAndAirports(map::MapResultIndex& runwayAirports, co
{
map::MapAirport airport;
mapTypesFactory->fillAirport(query.record(), airport, false /* complete */, navdata, xp);
if(!navdata)
NavApp::getAirportQueryNav()->correctAirportProcedureFlag(airport);
runwayAirports.add(map::MapResult::createFromMapBase(&airport));
}
}
Expand Down Expand Up @@ -1363,6 +1411,8 @@ void AirportQuery::initQueries()
"join runway_end p on r.primary_end_id = p.runway_end_id "
"join runway_end s on r.secondary_end_id = s.runway_end_id "
"where r.airport_id = :airportId");

loadAirportProcedureCache();
}

void AirportQuery::deInitQueries()
Expand All @@ -1376,6 +1426,9 @@ void AirportQuery::deInitQueries()
airportIdentCache.clear();
airportIdCache.clear();
airportFuzzyIdCache.clear();
airportsWithProceduresIdent.clear();
airportsWithProceduresIata.clear();
nearestAirportCache.clear();

delete runwayOverviewQuery;
runwayOverviewQuery = nullptr;
Expand Down
19 changes: 19 additions & 0 deletions src/query/airportquery.h
Expand Up @@ -19,7 +19,9 @@
#define LITTLENAVMAP_AIRPORTQUERY_H

#include "common/mapflags.h"

#include <QCache>
#include <QSet>

namespace Marble {
class GeoDataLatLonBox;
Expand Down Expand Up @@ -123,6 +125,15 @@ class AirportQuery
/* Used as callback for loading METAR data to fetch airport coordinates for nearest. Looks for either ident or icao. */
atools::geo::Pos getAirportPosByIdentOrIcao(const QString& identOrIcao);

/* Checks ident or IATA from hash if the airport has procedures and applies the flag.
* Fast but not 100 percent accurate for airports with not matching idents since no fuzzy search is done. */
void correctAirportProcedureFlag(map::MapAirport& airport);

void correctAirportProcedureFlag(map::MapAirport *airport)
{
correctAirportProcedureFlag(*airport);
}

/* true if airport has procedures. Airport must be available in this database (sim or nav). */
bool hasProcedures(const map::MapAirport& airport) const;

Expand Down Expand Up @@ -226,6 +237,13 @@ class AirportQuery
void getRunwaysAndAirports(map::MapResultIndex& runwayAirports, const atools::geo::Rect& rect, const atools::geo::Pos& pos,
bool noRunway);

/* Load all airport idents of airports with procedures if this is a navdata query object */
void loadAirportProcedureCache();

/* Checks ident or IATA from hash if the airport has procedures.
* Fast but not 100 percent accurate for airports with not matching idents since no fuzzy search is done. */
bool hasAirportProcedures(const QString& ident, const QString& iata);

/* true if third party navdata */
bool navdata;

Expand All @@ -243,6 +261,7 @@ class AirportQuery
QCache<QString, map::MapAirport> airportIdentCache;
QCache<int, map::MapAirport> airportIdCache, airportFuzzyIdCache;
QCache<NearestCacheKeyAirport, map::MapResultIndex> nearestAirportCache;
QSet<QString> airportsWithProceduresIdent, airportsWithProceduresIata;

/* Available ident columns in airport table. Set to true if column exists and has not null values. */
bool icaoCol = false, faaCol = false, iataCol = false, localCol = false;
Expand Down

0 comments on commit 48fff01

Please sign in to comment.