diff --git a/sdrbase/util/osndb.cpp b/sdrbase/util/osndb.cpp index f19357af5..e4a1214db 100644 --- a/sdrbase/util/osndb.cpp +++ b/sdrbase/util/osndb.cpp @@ -28,9 +28,11 @@ #endif #include "util/osndb.h" +#include "util/corsproxy.h" QHash AircraftInformation::m_airlineIcons; QHash AircraftInformation::m_airlineMissingIcons; +QHash AircraftInformation::m_sideviewIcons; QHash AircraftInformation::m_flagIcons; QHash *AircraftInformation::m_prefixMap; QHash *AircraftInformation::m_militaryMap; @@ -38,6 +40,7 @@ QMutex AircraftInformation::m_mutex; QSharedPointer> OsnDB::m_aircraftInformation; QSharedPointer> OsnDB::m_aircraftInformationByReg; +QSharedPointer> OsnDB::m_aircraftRouteInformation; QDateTime OsnDB::m_modifiedDateTime; @@ -54,9 +57,9 @@ OsnDB::~OsnDB() void OsnDB::downloadAircraftInformation() { - QString filename = OsnDB::getOSNDBZipFilename(); + QString filename = OsnDB::getZipFilename(); QString urlString = OSNDB_URL; - QUrl dbURL(urlString); + QUrl dbURL(CORSProxy::adjustHost(urlString)); qDebug() << "OsnDB::downloadAircraftInformation: Downloading " << urlString; emit downloadingURL(urlString); QNetworkReply *reply = m_dlm.download(dbURL, filename); @@ -72,38 +75,18 @@ void OsnDB::downloadFinished(const QString& filename, bool success) qWarning() << "OsnDB::downloadFinished: Failed to download: " << filename; emit downloadError(QString("Failed to download: %1").arg(filename)); } - else if (filename == OsnDB::getOSNDBZipFilename()) + else if (filename == OsnDB::getZipFilename()) { // Extract .csv file from .zip file QZipReader reader(filename); - QByteArray database = reader.fileData("media/data/samples/metadata/aircraftDatabase.csv"); - if (database.size() > 0) + if (reader.extractAll(getDataDir())) { - QFile file(OsnDB::getOSNDBFilename()); - if (file.open(QIODevice::WriteOnly)) - { - file.write(database); - file.close(); - emit downloadAircraftInformationFinished(); - } - else - { - qWarning() << "OsnDB::downloadFinished - Failed to open " << file.fileName() << " for writing"; - emit downloadError(QString("Failed to open %1 for writing").arg(file.fileName())); - } + emit downloadAircraftInformationFinished(); } else { - qWarning() << "OsnDB::downloadFinished - aircraftDatabase.csv not in expected dir. Extracting all."; - if (reader.extractAll(getDataDir())) - { - emit downloadAircraftInformationFinished(); - } - else - { - qWarning() << "OsnDB::downloadFinished - Failed to extract files from " << filename; - emit downloadError(QString("Failed to extract files from ").arg(filename)); - } + qWarning() << "OsnDB::downloadFinished - Failed to extract files from " << filename; + emit downloadError(QString("Failed to extract files from ").arg(filename)); } } else @@ -153,6 +136,14 @@ QSharedPointer> OsnDB::getAircraftIn return m_aircraftInformationByReg; } +QSharedPointer> OsnDB::getAircraftRouteInformation() +{ + if (!m_aircraftRouteInformation) { + m_aircraftRouteInformation = QSharedPointer>(OsnDB::readRouteDB(getRouteDBFilename())); + } + return m_aircraftRouteInformation; +} + QHash *OsnDB::readOSNDB(const QString &filename) { int cnt = 0; @@ -163,6 +154,7 @@ QHash *OsnDB::readOSNDB(const QString &filename) int registrationCol = 1; int manufacturerNameCol = 3; int modelCol = 4; + int typeCodeCol = 5; int ownerCol = 13; int operatorCol = 9; int operatorICAOCol = 11; @@ -193,6 +185,8 @@ QHash *OsnDB::readOSNDB(const QString &filename) manufacturerNameCol = idx; else if (!strcmp(p, "model")) modelCol = idx; + else if (!strcmp(p, "typecode")) + typeCodeCol = idx; else if (!strcmp(p, "owner")) ownerCol = idx; else if (!strcmp(p, "operator")) @@ -215,6 +209,8 @@ QHash *OsnDB::readOSNDB(const QString &filename) size_t manufacturerNameLen = 0; char *model = NULL; size_t modelLen = 0; + char *typeCode = NULL; + size_t typeCodeLen = 0; char *owner = NULL; size_t ownerLen = 0; char *operatorName = NULL; @@ -264,6 +260,12 @@ QHash *OsnDB::readOSNDB(const QString &filename) modelLen = strlen(model)-1; model[modelLen] = '\0'; } + else if (idx == typeCodeCol) + { + typeCode = p+1; + typeCodeLen = strlen(typeCode)-1; + typeCode[typeCodeLen] = '\0'; + } else if (idx == ownerCol) { owner = p+1; @@ -315,6 +317,7 @@ QHash *OsnDB::readOSNDB(const QString &filename) aircraft->m_registration = QString(registration); aircraft->m_manufacturerName = QString(manufacturerName); aircraft->m_model = modelQ; + aircraft->m_type = QString(typeCode); aircraft->m_owner = QString(owner); aircraft->m_operator = QString(operatorName); aircraft->m_operatorICAO = QString(operatorICAO); @@ -353,7 +356,7 @@ bool OsnDB::writeFastDB(const QString &filename, const QHash::const_iterator i = aircraftInfo->begin(); while (i != aircraftInfo->end()) { @@ -366,6 +369,8 @@ bool OsnDB::writeFastDB(const QString &filename, const QHashm_model.toUtf8()); file.write(","); + file.write(info->m_type.toUtf8()); + file.write(","); file.write(info->m_owner.toUtf8()); file.write(","); file.write(info->m_operator.toUtf8()); @@ -402,7 +407,7 @@ QHash *OsnDB::readFastDB(const QString &filename) if (fgets(row, sizeof(row), file)) { // Check header - if (!strcmp(row, "icao24,registration,manufacturername,model,owner,operator,operatoricao,registered\n")) + if (!strcmp(row, "icao24,registration,manufacturername,model,typecode,owner,operator,operatoricao,registered\n")) { aircraftInfo = new QHash(); aircraftInfo->reserve(500000); @@ -417,6 +422,7 @@ QHash *OsnDB::readFastDB(const QString &filename) aircraft->m_registration = QString(csvNext(&p)); aircraft->m_manufacturerName = QString(csvNext(&p)); aircraft->m_model = QString(csvNext(&p)); + aircraft->m_type = QString(csvNext(&p)); aircraft->m_owner = QString(csvNext(&p)); aircraft->m_operator = QString(csvNext(&p)); aircraft->m_operatorICAO = QString(csvNext(&p)); @@ -426,7 +432,7 @@ QHash *OsnDB::readFastDB(const QString &filename) } } else - qDebug() << "AircraftInformation::readFastDB: Unexpected header"; + qDebug() << "AircraftInformation::readFastDB: Unexpected header" << row; } else qDebug() << "AircraftInformation::readFastDB: Empty file"; @@ -440,6 +446,60 @@ QHash *OsnDB::readFastDB(const QString &filename) return aircraftInfo; } +QHash *OsnDB::readRouteDB(const QString &filename) +{ + int cnt = 0; + QHash *routeInfo = nullptr; + + qDebug() << "AircraftInformation::readRouteDB: " << filename; + + FILE *file; + QByteArray utfFilename = filename.toUtf8(); + if ((file = fopen(utfFilename.constData(), "r")) != NULL) + { + char row[2048]; + + if (fgets(row, sizeof(row), file)) + { + // Check header + if (!strcmp(row, "callsign,dep,arr,stops\n")) + { + routeInfo = new QHash(); + routeInfo->reserve(600000); + // Read data + while (fgets(row, sizeof(row), file)) + { + char *p = row; + AircraftRouteInformation *route = new AircraftRouteInformation(); + route->m_callsign = QString(csvNext(&p)); + route->m_dep = QString(csvNext(&p)); + route->m_arr = QString(csvNext(&p)); + route->m_stops = QString(csvNext(&p)); + routeInfo->insert(route->m_callsign, route); + cnt++; + } + } + else + { + qDebug() << "AircraftInformation::readRouteDB: Unexpected header" << row; + } + } + else + { + qDebug() << "AircraftInformation::readRouteDB: Empty file"; + } + fclose(file); + } + else + { + qDebug() << "AircraftInformation::readRouteDB: Failed to open " << filename; + } + + qDebug() << "AircraftInformation::readRouteDB - read " << cnt << " routes"; + + return routeInfo; +} + QString AircraftInformation::getFlag() const { QString flag; @@ -499,7 +559,7 @@ QString AircraftInformation::getFlag() const QString AircraftInformation::getAirlineIconPath(const QString &operatorICAO) { - QString endPath = QString("/airlinelogos/%1.bmp").arg(operatorICAO); + QString endPath = QString("/airlinelogos/%1.png").arg(operatorICAO); // Try in user directory first, so they can customise QString userIconPath = OsnDB::getDataDir() + endPath; QFile file(userIconPath); @@ -539,7 +599,7 @@ QIcon *AircraftInformation::getAirlineIcon(const QString &operatorICAO) { if (!m_airlineMissingIcons.contains(operatorICAO)) { - qDebug() << "ADSBDemodGUI: No airline logo for " << operatorICAO; + qDebug() << "AircraftInformation: No airline logo for " << operatorICAO; m_airlineMissingIcons.insert(operatorICAO, true); } } @@ -547,6 +607,75 @@ QIcon *AircraftInformation::getAirlineIcon(const QString &operatorICAO) } } +QString AircraftInformation::getSideviewIconPath(const QString ®istration, const QString &operatorICAO, const QString &modelICAO) +{ + QString p1 = QString(":/sideviews/%1.png").arg(registration); + QResource r1(p1); + if (r1.isValid()) { + return p1; + } + + QString opModel = operatorICAO + modelICAO; + QString p2 = QString(":/sideviews/%1.png").arg(opModel); + QResource r2(p2); + if (r2.isValid()) { + return p2; + } + + QString p3 = QString(":/sideviews/%1.png").arg(modelICAO); + QResource r3(p3); + if (r3.isValid()) { + return p3; + } + + return QString(); +} + +QIcon *AircraftInformation::getSideviewIcon(const QString ®istration, const QString &operatorICAO, const QString &modelICAO) +{ + QIcon *icon = nullptr; + + if (m_sideviewIcons.contains(registration)) { + return m_sideviewIcons.value(registration); + } + QString p1 = QString(":/sideviews/%1.png").arg(registration); + QResource r1(p1); + if (r1.isValid()) + { + icon = new QIcon(p1); + m_sideviewIcons.insert(registration, icon); + return icon; + } + + QString opModel = operatorICAO + modelICAO; + if (m_sideviewIcons.contains(opModel)) { + return m_sideviewIcons.value(opModel); + } + QString p2 = QString(":/sideviews/%1.png").arg(opModel); + QResource r2(p2); + if (r2.isValid()) + { + icon = new QIcon(p2); + m_sideviewIcons.insert(opModel, icon); + return icon; + } + + if (m_sideviewIcons.contains(modelICAO)) { + return m_sideviewIcons.value(modelICAO); + } + QString p3 = QString(":/sideviews/%1.png").arg(modelICAO); + QResource r3(p3); + if (r3.isValid()) + { + icon = new QIcon(p3); + m_sideviewIcons.insert(modelICAO, icon); + return icon; + } + + qDebug() << "AircraftInformation: No sideview for " << opModel; + return nullptr; +} + QString AircraftInformation::getFlagIconPath(const QString &country) { QString endPath = QString("/flags/%1.bmp").arg(country); @@ -572,9 +701,13 @@ QString AircraftInformation::getFlagIconPath(const QString &country) QString AircraftInformation::getFlagIconURL(const QString &country) { - QString path = getFlagIconPath(country); + return resourcePathToURL(getFlagIconPath(country)); +} + +QString AircraftInformation::resourcePathToURL(const QString &path) +{ if (path.startsWith(':')) { - path = "qrc://" + path.mid(1); + return "qrc://" + path.mid(1); } return path; } diff --git a/sdrbase/util/osndb.h b/sdrbase/util/osndb.h index a33278f2f..2919090d5 100644 --- a/sdrbase/util/osndb.h +++ b/sdrbase/util/osndb.h @@ -34,7 +34,15 @@ #include "util/httpdownloadmanager.h" #include "export.h" -#define OSNDB_URL "https://s3.opensky-network.org/data-samples/metadata/aircraftDatabase.zip" +//#define OSNDB_URL "https://s3.opensky-network.org/data-samples/metadata/aircraftDatabase.zip" +#define OSNDB_URL "https://sdrangel.org/downloads/aircraftDatabase.zip" + +struct SDRBASE_API AircraftRouteInformation { + QString m_callsign; // Aircraft callsign + QString m_dep; // Departure airport ICAO + QString m_arr; // Arrival airport ICAO + QString m_stops; // Airport ICAO of stops on route '-' separated list +}; struct SDRBASE_API AircraftInformation { @@ -42,6 +50,7 @@ struct SDRBASE_API AircraftInformation { QString m_registration; QString m_manufacturerName; QString m_model; + QString m_type; QString m_owner; QString m_operator; QString m_operatorICAO; @@ -68,16 +77,22 @@ struct SDRBASE_API AircraftInformation { // Try to find an airline logo based on ICAO static QIcon *getAirlineIcon(const QString &operatorICAO); + static QIcon *getSideviewIcon(const QString ®istration, const QString &operatorICAO, const QString &modelICAO); + static QString getSideviewIconPath(const QString ®istration, const QString &operatorICAO, const QString &modelICAO); + static QString getFlagIconPath(const QString &country); static QString getFlagIconURL(const QString &country); // Try to find an flag logo based on a country static QIcon *getFlagIcon(const QString &country); + static QString resourcePathToURL(const QString &path); + private: static QHash m_airlineIcons; // Hashed on airline ICAO static QHash m_airlineMissingIcons; // Hash containing which ICAOs we don't have icons for + static QHash m_sideviewIcons; static QHash m_flagIcons; // Hashed on country static QHash *m_prefixMap; // Registration to country (flag name) static QHash *m_militaryMap; // Operator airforce to military (flag name) @@ -97,8 +112,9 @@ public: static QSharedPointer> getAircraftInformation(); static QSharedPointer> getAircraftInformationByReg(); + static QSharedPointer> getAircraftRouteInformation(); - static QString getOSNDBZipFilename() + static QString getZipFilename() { return getDataDir() + "/aircraftDatabase.zip"; } @@ -121,6 +137,7 @@ private: static QSharedPointer> m_aircraftInformation; static QSharedPointer> m_aircraftInformationByReg; + static QSharedPointer> m_aircraftRouteInformation; static QDateTime m_modifiedDateTime; // Write a reduced size and validated version of the DB, so it loads quicker @@ -129,6 +146,8 @@ private: // Read smaller CSV file with no validation. Takes about 0.5s instead of 2s. static QHash *readFastDB(const QString &filename); + static QHash *readRouteDB(const QString &filename); + // Read OpenSky Network CSV file // This is large and contains lots of data we don't want, so we convert to // a smaller version to speed up loading time @@ -141,6 +160,11 @@ private: return getDataDir() + "/aircraftDatabaseFast.csv"; } + static QString getRouteDBFilename() + { + return getDataDir() + "/aircraftRouteDatabase.csv"; + } + // Create hash table using registration as key static QHash *registrationHash(const QHash *in);