From aed22158f66e0fe544b38562b9102aa51d3ddda4 Mon Sep 17 00:00:00 2001 From: Holden Date: Fri, 25 Oct 2024 18:53:47 -0400 Subject: [PATCH] QtLocationPlugin: Create QGCTileCacheDatabase Class --- src/QtLocationPlugin/CMakeLists.txt | 2 + src/QtLocationPlugin/QGCCacheTile.h | 2 +- src/QtLocationPlugin/QGCTileCacheDatabase.cpp | 924 +++++++++++++++ src/QtLocationPlugin/QGCTileCacheDatabase.h | 77 ++ src/QtLocationPlugin/QGCTileCacheWorker.cpp | 1022 +++++------------ src/QtLocationPlugin/QGCTileCacheWorker.h | 8 +- 6 files changed, 1287 insertions(+), 748 deletions(-) create mode 100644 src/QtLocationPlugin/QGCTileCacheDatabase.cpp create mode 100644 src/QtLocationPlugin/QGCTileCacheDatabase.h diff --git a/src/QtLocationPlugin/CMakeLists.txt b/src/QtLocationPlugin/CMakeLists.txt index 34ff89b4602..a727a0fe2b9 100644 --- a/src/QtLocationPlugin/CMakeLists.txt +++ b/src/QtLocationPlugin/CMakeLists.txt @@ -26,6 +26,8 @@ qt_add_plugin(QGCLocation STATIC QGCMapUrlEngine.cpp QGCMapUrlEngine.h QGCTile.h + QGCTileCacheDatabase.cpp + QGCTileCacheDatabase.h QGCTileCacheWorker.cpp QGCTileCacheWorker.h QGCTileSet.h diff --git a/src/QtLocationPlugin/QGCCacheTile.h b/src/QtLocationPlugin/QGCCacheTile.h index 056c562c35f..e58340b16ea 100644 --- a/src/QtLocationPlugin/QGCCacheTile.h +++ b/src/QtLocationPlugin/QGCCacheTile.h @@ -40,6 +40,6 @@ class QGCCacheTile const QString m_hash; const QByteArray m_img; const QString m_format; - const QString m_type; + const QString m_type; // TODO: type should be int? }; Q_DECLARE_METATYPE(QGCCacheTile) diff --git a/src/QtLocationPlugin/QGCTileCacheDatabase.cpp b/src/QtLocationPlugin/QGCTileCacheDatabase.cpp new file mode 100644 index 00000000000..645647c87ed --- /dev/null +++ b/src/QtLocationPlugin/QGCTileCacheDatabase.cpp @@ -0,0 +1,924 @@ +#include "QGCTileCacheDatabase.h" +#include "QGCCacheTile.h" +#include "QGCCachedTileSet.h" +#include "QGCMapUrlEngine.h" +#include "QGCLoggingCategory.h" + +#include +#include +#include +#include +#include + +QGC_LOGGING_CATEGORY(QGCTileCacheDBLog, "qgc.qtlocationplugin.qgctilecachedb") + +/*===========================================================================*/ + +bool SetTilesTableModel::create(std::shared_ptr db) +{ + QSqlQuery query(*db); + + (void) query.prepare( + "CREATE TABLE IF NOT EXISTS SetTiles (" + "setID INTEGER NOT NULL, " + "tileID INTEGER NOT NULL, " + "PRIMARY KEY (setID, tileID)" + ")" + ); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool SetTilesTableModel::selectFromSetID(std::shared_ptr db, quint64 setID) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT * FROM SetTiles WHERE setID = ?"); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool SetTilesTableModel::insertSetTiles(std::shared_ptr db, quint64 setID, quint64 tileID) +{ + QSqlQuery query(*db); + + (void) query.prepare("INSERT INTO SetTiles(tileID, setID) VALUES(?, ?)"); // INSERT OR IGNORE? + query.addBindValue(tileID); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool SetTilesTableModel::deleteTileSet(std::shared_ptr db, quint64 setID) +{ + QSqlQuery query(*db); + + (void) query.prepare("DELETE FROM SetTiles WHERE setID = ?"); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool SetTilesTableModel::drop(std::shared_ptr db) +{ + QSqlQuery query(*db); + (void) query.prepare("DROP TABLE IF EXISTS SetTiles"); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return query.exec(); +} + +/*===========================================================================*/ + +bool TileSetsTableModel::create(std::shared_ptr db) +{ + QSqlQuery query(*db); + (void) query.prepare( + "CREATE TABLE IF NOT EXISTS TileSets (" + "setID INTEGER PRIMARY KEY NOT NULL, " + "name TEXT NOT NULL UNIQUE, " + "typeStr TEXT, " + "topleftLat REAL DEFAULT 0.0, " + "topleftLon REAL DEFAULT 0.0, " + "bottomRightLat REAL DEFAULT 0.0, " + "bottomRightLon REAL DEFAULT 0.0, " + "minZoom INTEGER DEFAULT 3, " + "maxZoom INTEGER DEFAULT 3, " + "type INTEGER DEFAULT -1, " + "numTiles INTEGER DEFAULT 0, " + "defaultSet INTEGER DEFAULT 0, " + "date INTEGER DEFAULT 0)" + ); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TileSetsTableModel::insertTileSet(std::shared_ptr db, const QGCCachedTileSet &tileSet, quint64 &setID) +{ + QSqlQuery query(*db); + + (void) query.prepare( + "INSERT INTO TileSets" + "(name, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, date) " + "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + ); + query.addBindValue(tileSet.name()); + query.addBindValue(tileSet.mapTypeStr()); + query.addBindValue(tileSet.topleftLat()); + query.addBindValue(tileSet.topleftLon()); + query.addBindValue(tileSet.bottomRightLat()); + query.addBindValue(tileSet.bottomRightLon()); + query.addBindValue(tileSet.minZoom()); + query.addBindValue(tileSet.maxZoom()); + query.addBindValue(UrlFactory::getQtMapIdFromProviderType(tileSet.type())); + query.addBindValue(tileSet.totalTileCount()); + query.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + setID = query.lastInsertId().toULongLong(); + + return true; +} + +bool TileSetsTableModel::getTileSets(std::shared_ptr db, QList &tileSets) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT * FROM TileSets ORDER BY defaultSet DESC, name ASC"); + + if (!query.exec()) { + qCDebug(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + while (query.next()) { + const QString name = query.value("name").toString(); + QGCCachedTileSet* const set = new QGCCachedTileSet(name); + set->setId(query.value("setID").toULongLong()); + set->setMapTypeStr(query.value("typeStr").toString()); + set->setTopleftLat(query.value("topleftLat").toDouble()); + set->setTopleftLon(query.value("topleftLon").toDouble()); + set->setBottomRightLat(query.value("bottomRightLat").toDouble()); + set->setBottomRightLon(query.value("bottomRightLon").toDouble()); + set->setMinZoom(query.value("minZoom").toInt()); + set->setMaxZoom(query.value("maxZoom").toInt()); + set->setType(UrlFactory::getProviderTypeFromQtMapId(query.value("type").toInt())); + set->setTotalTileCount(query.value("numTiles").toUInt()); + set->setDefaultSet(query.value("defaultSet").toInt() != 0); + set->setCreationDate(QDateTime::fromSecsSinceEpoch(query.value("date").toUInt())); + (void) tileSets.append(set); + } + + return true; +} + +bool TileSetsTableModel::setName(std::shared_ptr db, quint64 setID, const QString &newName) +{ + QSqlQuery query(*db); + + (void) query.prepare("UPDATE TileSets SET name = ? WHERE setID = ?"); + query.addBindValue(newName); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TileSetsTableModel::setNumTiles(std::shared_ptr db, quint64 setID, quint64 numTiles) +{ + QSqlQuery query(*db); + + (void) query.prepare("UPDATE TileSets SET numTiles = ? WHERE setID = ?"); + query.addBindValue(numTiles); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TileSetsTableModel::getTileSetID(std::shared_ptr db, quint64 &setID, const QString &name) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT setID FROM TileSets WHERE name = ?"); + query.addBindValue(name); + + if (query.exec() && query.next()) { + setID = query.value(0).toULongLong(); + return true; + } + + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "TileSet not found for name" << name; + return false; +} + +bool TileSetsTableModel::getDefaultTileSet(std::shared_ptr db, quint64 &setID) +{ + static quint64 defaultSet = UINT64_MAX; + + if (defaultSet != UINT64_MAX) { + setID = defaultSet; + return true; + } + + QSqlQuery query(*db); + + (void) query.prepare("SELECT setID FROM TileSets WHERE defaultSet = 1"); + + if (!query.exec() || !query.next()) { + setID = 1; // Default fallback if no set found. + return false; + } + + defaultSet = query.value(0).toULongLong(); + setID = defaultSet; + + return true; +} + +bool TileSetsTableModel::deleteTileSet(std::shared_ptr db, quint64 setID) +{ + QSqlQuery query(*db); + + (void) query.prepare("DELETE FROM TileSets WHERE setID = ?"); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TileSetsTableModel::drop(std::shared_ptr db) +{ + QSqlQuery query(*db); + + (void) query.prepare("DROP TABLE IF EXISTS TileSets"); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TileSetsTableModel::createDefaultTileSet(std::shared_ptr db) +{ + static const QString kDefaultSet = QStringLiteral("Default Tile Set"); + + QSqlQuery query(*db); + + (void) query.prepare("SELECT name FROM TileSets WHERE name = ?"); + query.addBindValue(kDefaultSet); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + if (query.next()) { + return true; + } + + (void) query.prepare("INSERT INTO TileSets(name, defaultSet, date) VALUES(?, ?, ?)"); + query.addBindValue(kDefaultSet); + query.addBindValue(1); + query.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +/*===========================================================================*/ + +bool TilesTableModel::create(std::shared_ptr db) +{ + QSqlQuery query(*db); + (void) query.prepare( + "CREATE TABLE IF NOT EXISTS Tiles (" + "tileID INTEGER PRIMARY KEY NOT NULL, " + "hash TEXT NOT NULL UNIQUE, " + "format TEXT NOT NULL, " + "tile BLOB, " + "size INTEGER, " + "type INTEGER, " + "date INTEGER DEFAULT 0)" + ); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + (void) query.prepare( + "CREATE INDEX IF NOT EXISTS hash ON Tiles (" + "hash, size, type)" + ); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TilesTableModel::getTile(std::shared_ptr db, QGCCacheTile *tile, const QString &hash) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT tile, format, type FROM Tiles WHERE hash = ?"); + query.addBindValue(hash); + + if (!query.exec() || !query.next()) { + qCDebug(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text();; + return false; + } + + const QByteArray array = query.value(0).toByteArray(); + const QString format = query.value(1).toString(); + const QString type = query.value(2).toString(); + tile = new QGCCacheTile(hash, array, format, type); + + return true; +} + +bool TilesTableModel::getTileCount(std::shared_ptr db, quint64 &tileCount) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT COUNT(tileID) FROM Tiles"); + if (!query.exec() || !query.next()) { + qCDebug(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text();; + return false; + } + + tileCount = query.value(0).toULongLong(); + + return true; +} + +bool TilesTableModel::getTileID(std::shared_ptr db, quint64 &tileID, const QString &hash) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT tileID FROM Tiles WHERE hash = ?"); + query.addBindValue(hash); + + if (!query.exec() || !query.next()) { + qCDebug(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text();; + return false; + } + + tileID = query.value(0).toULongLong(); + + return true; +} + +bool TilesTableModel::selectFromTileID(std::shared_ptr db, quint64 tileID) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT * FROM Tiles WHERE tileID = ?"); + query.addBindValue(tileID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + if (!query.next()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "No rows found for tileID" << tileID; + return false; + } + + return true; +} + +bool TilesTableModel::insertTile(std::shared_ptr db, quint64 &tileID, const QGCCacheTile &tile) +{ + return insertTile(db, tileID, tile.hash(), tile.format(), tile.img(), tile.type()); +} + +bool TilesTableModel::insertTile(std::shared_ptr db, quint64 &tileID, const QString &hash, const QString &format, const QByteArray &img, const QString &type) +{ + QSqlQuery query(*db); + + (void) query.prepare("INSERT INTO Tiles(hash, format, tile, size, type, date) VALUES(?, ?, ?, ?, ?, ?)"); + query.addBindValue(hash); + query.addBindValue(format); + query.addBindValue(img); + query.addBindValue(img.size()); + query.addBindValue(type); + query.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + tileID = query.lastInsertId().toULongLong(); + + return true; +} + +bool TilesTableModel::deleteTileSet(std::shared_ptr db, quint64 setID) +{ + QSqlQuery query(*db); + + (void) query.prepare( + "DELETE FROM Tiles WHERE tileID IN " + "(SELECT A.tileID FROM SetTiles A " + "JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = ? " + "GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)" + ); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TilesTableModel::getSetTotal(std::shared_ptr db, quint64 &count, quint64 setID) +{ + QSqlQuery query(*db); + + (void) query.prepare( + "SELECT COUNT(size) FROM Tiles A " + "INNER JOIN SetTiles B on A.tileID = B.tileID " + "WHERE B.setID = ?" + ); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "Failed to execute COUNT query:" << query.lastError().text(); + return false; + } + + if (!query.next()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "No result from COUNT query."; + return false; + } + + count = query.value(0).toULongLong(); + + return true; +} + +bool TilesTableModel::updateSetTotals(std::shared_ptr db, QGCCachedTileSet &set) +{ + QSqlQuery query(*db); + + (void) query.prepare( + "SELECT COUNT(size), SUM(size) FROM Tiles A " + "INNER JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = ?" + ); + query.addBindValue(set.id()); + + if (!query.exec() || !query.next()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "query failed"; + return false; + } + + set.setSavedTileCount(query.value(0).toUInt()); + set.setSavedTileSize(query.value(1).toULongLong()); + qCDebug(QGCTileCacheDBLog) << "Set" << set.id() << "Totals:" << set.savedTileCount() << set.savedTileSize() << "Expected:" << set.totalTileCount() << set.totalTilesSize(); + + quint64 avg = UrlFactory::averageSizeForType(set.type()); + if (set.totalTileCount() <= set.savedTileCount()) { + set.setTotalTileSize(set.savedTileSize()); + } else { + if ((set.savedTileCount() > 10) && (set.savedTileSize() > 0)) { + avg = (set.savedTileSize() / set.savedTileCount()); + } + set.setTotalTileSize(avg * set.totalTileCount()); + } + + quint32 ucount = 0; + quint64 usize = 0; + (void) query.prepare( + "SELECT COUNT(size), SUM(size) FROM Tiles " + "WHERE tileID IN (SELECT A.tileID FROM SetTiles A " + "JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = ? " + "GROUP by A.tileID HAVING COUNT(A.tileID) = 1)" + ); + query.addBindValue(set.id()); + + if (query.exec() && query.next()) { + ucount = query.value(0).toUInt(); + usize = query.value(1).toULongLong(); + } + + quint32 expectedUcount = set.totalTileCount() - set.savedTileCount(); + if (ucount == 0) { + usize = expectedUcount * avg; + } else { + expectedUcount = ucount; + } + set.setUniqueTileCount(expectedUcount); + set.setUniqueTileSize(usize); + + return true; +} + +bool TilesTableModel::updateTotals(std::shared_ptr db, quint32 &totalCount, quint64 &totalSize, quint32 &defaultCount, quint64 &defaultSize, quint64 defaultTileSetID) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT COUNT(size), SUM(size) FROM Tiles"); + if (!query.exec() || !query.next()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + totalCount = query.value(0).toUInt(); + totalSize = query.value(1).toULongLong(); + + (void) query.prepare( + "SELECT COUNT(size), SUM(size) FROM Tiles WHERE tileID IN " + "(SELECT A.tileID FROM SetTiles A " + "JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = ? " + "GROUP by A.tileID HAVING COUNT(A.tileID) = 1)" + ); + query.addBindValue(defaultTileSetID); + + if (!query.exec() || !query.next()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + defaultCount = query.value(0).toUInt(); + defaultSize = query.value(1).toULongLong(); + + return true; +} + +bool TilesTableModel::prune(std::shared_ptr db, quint64 defaultTileSetID, qint64 amount) +{ + QSqlQuery query(*db); + + (void) query.prepare( + "SELECT tileID, size, hash FROM Tiles WHERE tileID IN " + "(SELECT A.tileID FROM SetTiles A JOIN SetTiles B on A.tileID = B.tileID " + "WHERE B.setID = ? GROUP by A.tileID HAVING COUNT(A.tileID) = 1) " + "ORDER BY DATE ASC LIMIT 128" + ); + query.addBindValue(defaultTileSetID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + QQueue tilelist; + while (query.next() && (amount >= 0)) { + tilelist.enqueue(query.value(0).toULongLong()); + amount -= query.value(1).toULongLong(); + } + + while (!tilelist.isEmpty()) { + const quint64 tileID = tilelist.dequeue(); + + (void) query.prepare("DELETE FROM Tiles WHERE tileID = ?"); + query.addBindValue(tileID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + } + + return true; +} + +static bool _initDataFromResources(QByteArray &bingNoTileImage) +{ + if (bingNoTileImage.isEmpty()) { + QFile file("://res/BingNoTileBytes.dat"); + if (file.open(QFile::ReadOnly)) { + bingNoTileImage = file.readAll(); + file.close(); + } + + if (bingNoTileImage.isEmpty()) { + qCWarning(QGCTileCacheDBLog) << "Unable to read BingNoTileBytes"; + return false; + } + } + + return true; +} + +bool TilesTableModel::deleteBingNoTileImageTiles(std::shared_ptr db) +{ + static const QString alreadyDoneKey = QStringLiteral("_deleteBingNoTileTilesDone"); + + QSettings settings; + if (settings.value(alreadyDoneKey, false).toBool()) { + return true; + } + settings.setValue(alreadyDoneKey, true); + + static QByteArray bingNoTileImage; + if (!_initDataFromResources(bingNoTileImage)) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "Error getting BingNoTileBytes.dat"; + return false; + } + + QSqlQuery query(*db); + + (void) query.prepare("SELECT tileID, tile FROM Tiles WHERE LENGTH(tile) = ?"); + query.addBindValue(bingNoTileImage.length()); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + QList idsToDelete; + while (query.next()) { + if (query.value(1).toByteArray() == bingNoTileImage) { + (void) idsToDelete.append(query.value(0).toULongLong()); + } + } + + for (const quint64 tileId: idsToDelete) { + (void) query.prepare("DELETE FROM Tiles WHERE tileID = ?"); + query.addBindValue(tileId); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + } + + return true; +} + +bool TilesTableModel::drop(std::shared_ptr db) +{ + QSqlQuery query(*db); + + (void) query.prepare("DROP TABLE IF EXISTS Tiles"); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +/*===========================================================================*/ + +bool TilesDownloadTableModel::create(std::shared_ptr db) +{ + QSqlQuery query(*db); + + (void) query.prepare( + "CREATE TABLE IF NOT EXISTS TilesDownload (" + "setID INTEGER PRIMARY KEY NOT NULL, " + "hash TEXT NOT NULL UNIQUE, " + "type INTEGER, " + "x INTEGER, " + "y INTEGER, " + "z INTEGER, " + "state INTEGER DEFAULT 0)" + ); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TilesDownloadTableModel::insertTilesDownload(std::shared_ptr db, const QGCCachedTileSet *tileSet) +{ + QSqlQuery query(*db); + + if (!db->transaction()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "Transaction start failed"; + return false; + } + + for (int z = tileSet->minZoom(); z <= tileSet->maxZoom(); z++) { + const QGCTileSet set = UrlFactory::getTileCount( + z, + tileSet->topleftLon(), + tileSet->topleftLat(), + tileSet->bottomRightLon(), + tileSet->bottomRightLat(), + tileSet->type() + ); + const QString type = tileSet->type(); + + for (int x = set.tileX0; x <= set.tileX1; x++) { + for (int y = set.tileY0; y <= set.tileY1; y++) { + const QString hash = UrlFactory::getTileHash(type, x, y, z); + const quint64 setID = tileSet->id(); + const quint64 tileID = 0; // TODO: _findTile(hash); + // _tilesTable->getTileID(tileID, hash); + if (!tileID) { + (void) query.prepare(QStringLiteral( + "INSERT OR IGNORE INTO TilesDownload(setID, hash, type, x, y, z, state) " + "VALUES(?, ?, ?, ?, ? ,? ,?)" + )); + query.addBindValue(setID); + query.addBindValue(hash); + query.addBindValue(UrlFactory::getQtMapIdFromProviderType(type)); + query.addBindValue(x); + query.addBindValue(y); + query.addBindValue(z); + query.addBindValue(0); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << "Map Cache SQL error (add tile into TilesDownload):" + << query.lastError().text(); + return false; + } + } else { + (void) query.prepare("INSERT OR IGNORE INTO SetTiles(tileID, setID) VALUES(?, ?)"); + query.addBindValue(tileID); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << "Map Cache SQL error (add tile into SetTiles):" + << query.lastError().text(); + } + + qCDebug(QGCTileCacheDBLog) << Q_FUNC_INFO << "Already Cached HASH:" << hash; + } + } + } + } + + if (!db->commit()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << "Transaction commit failed"; + } + + return true; +} + +bool TilesDownloadTableModel::setState(std::shared_ptr db, quint64 setID, const QString &hash, int state) +{ + QSqlQuery query(*db); + + (void) query.prepare("UPDATE TilesDownload SET state = ? WHERE setID = ? AND hash = ?"); + query.addBindValue(state); + query.addBindValue(setID); + query.addBindValue(hash); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TilesDownloadTableModel::setState(std::shared_ptr db, quint64 setID, int state) +{ + QSqlQuery query(*db); + + (void) query.prepare("UPDATE TilesDownload SET state = ? WHERE setID = ?"); + query.addBindValue(state); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TilesDownloadTableModel::deleteTileSet(std::shared_ptr db, quint64 setID) +{ + QSqlQuery query(*db); + + (void) query.prepare("DELETE FROM TilesDownload WHERE setID = ?"); + query.addBindValue(setID); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TilesDownloadTableModel::getTileDownloadList(std::shared_ptr db, QQueue &tiles, quint64 setID, int count) +{ + QSqlQuery query(*db); + + (void) query.prepare("SELECT hash, type, x, y, z FROM TilesDownload WHERE setID = ? AND state = 0 LIMIT ?"); + query.addBindValue(setID); + query.addBindValue(count); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + while (query.next()) { + QGCTile* const tile = new QGCTile(); + // tile->setTileSet(task->setID()); + tile->setHash(query.value("hash").toString()); + tile->setType(UrlFactory::getProviderTypeFromQtMapId(query.value("type").toInt())); + tile->setX(query.value("x").toInt()); + tile->setY(query.value("y").toInt()); + tile->setZ(query.value("z").toInt()); + tiles.enqueue(tile); + } + + for (qsizetype i = 0; i < tiles.size(); i++) { + (void) query.prepare("UPDATE TilesDownload SET state = ? WHERE setID = ? AND hash = ?"); + query.addBindValue(static_cast(QGCTile::StateDownloading)); + query.addBindValue(setID); + query.addBindValue(tiles[i]->hash()); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + } + + return true; +} + +bool TilesDownloadTableModel::updateTilesDownloadSet(std::shared_ptr db, QGCTile::TileState state, quint64 setID, const QString &hash) +{ + QSqlQuery query(*db); + + if (state == QGCTile::StateComplete) { + (void) query.prepare("DELETE FROM TilesDownload WHERE setID = ? AND hash = ?"); + query.addBindValue(setID); + query.addBindValue(hash); + } else if (hash == "*") { + (void) query.prepare("UPDATE TilesDownload SET state = ? WHERE setID = ?"); + query.addBindValue(state); + query.addBindValue(setID); + } else { + (void) query.prepare("UPDATE TilesDownload SET state = ? WHERE setID = ? AND hash = ?"); + query.addBindValue(state); + query.addBindValue(setID); + query.addBindValue(hash); + } + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +bool TilesDownloadTableModel::drop(std::shared_ptr db) +{ + QSqlQuery query(*db); + + (void) query.prepare("DROP TABLE IF EXISTS TilesDownload"); + + if (!query.exec()) { + qCWarning(QGCTileCacheDBLog) << Q_FUNC_INFO << query.lastError().text(); + return false; + } + + return true; +} + +/*===========================================================================*/ diff --git a/src/QtLocationPlugin/QGCTileCacheDatabase.h b/src/QtLocationPlugin/QGCTileCacheDatabase.h new file mode 100644 index 00000000000..b120ef7da37 --- /dev/null +++ b/src/QtLocationPlugin/QGCTileCacheDatabase.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include + +#include "QGCTile.h" + +class QGCCacheTile; +class QGCCachedTileSet; +class QSqlDatabase; + +Q_DECLARE_LOGGING_CATEGORY(QGCTileCacheDBLog) + +/*===========================================================================*/ + +namespace SetTilesTableModel +{ + bool create(std::shared_ptr db); + bool selectFromSetID(std::shared_ptr db, quint64 setID); + bool insertSetTiles(std::shared_ptr db, quint64 setID, quint64 tileID); + bool deleteTileSet(std::shared_ptr db, quint64 setID); + bool drop(std::shared_ptr db); +}; + +/*===========================================================================*/ + +namespace TileSetsTableModel +{ + bool create(std::shared_ptr db); + bool insertTileSet(std::shared_ptr db, const QGCCachedTileSet &tileSet, quint64 &setID); + bool getTileSets(std::shared_ptr db, QList &tileSets); + bool setName(std::shared_ptr db, quint64 setID, const QString &newName); + bool setNumTiles(std::shared_ptr db, quint64 setID, quint64 numTiles); + bool getTileSetID(std::shared_ptr db, quint64 &setID, const QString &name); + bool getDefaultTileSet(std::shared_ptr db, quint64 &setID); + bool deleteTileSet(std::shared_ptr db, quint64 setID); + bool drop(std::shared_ptr db); + bool createDefaultTileSet(std::shared_ptr db); +}; + +/*===========================================================================*/ + +namespace TilesTableModel +{ + bool create(std::shared_ptr db); + bool getTile(std::shared_ptr db, QGCCacheTile *tile, const QString &hash); + bool getTileCount(std::shared_ptr db, quint64 &tileCount); + bool getTileID(std::shared_ptr db, quint64 &tileID, const QString &hash); + bool selectFromTileID(std::shared_ptr db, quint64 tileID); + bool insertTile(std::shared_ptr db, quint64 &tileID, const QGCCacheTile &tile); + bool insertTile(std::shared_ptr db, quint64 &tileID, const QString &hash, const QString &format, const QByteArray &img, const QString &type); + bool deleteTileSet(std::shared_ptr db, quint64 setID); + bool getSetTotal(std::shared_ptr db, quint64 &count, quint64 setID); + bool updateSetTotals(std::shared_ptr db, QGCCachedTileSet &set); + bool updateTotals(std::shared_ptr db, quint32 &totalCount, quint64 &totalSize, quint32 &defaultCount, quint64 &defaultSize, quint64 defaultTileSetID); + bool prune(std::shared_ptr db, quint64 defaultTileSetID, qint64 amount); + bool deleteBingNoTileImageTiles(std::shared_ptr db); + bool drop(std::shared_ptr db); +}; + +/*===========================================================================*/ + +namespace TilesDownloadTableModel +{ + bool create(std::shared_ptr db); + bool insertTilesDownload(std::shared_ptr db, const QGCCachedTileSet *tileSet); + bool setState(std::shared_ptr db, quint64 setID, const QString &hash, int state); + bool setState(std::shared_ptr db, quint64 setID, int state); + bool deleteTileSet(std::shared_ptr db, quint64 setID); + bool getTileDownloadList(std::shared_ptr db, QQueue &tiles, quint64 setID, int count); + bool updateTilesDownloadSet(std::shared_ptr db, QGCTile::TileState state, quint64 setID, const QString &hash); + bool drop(std::shared_ptr db); +}; + +/*===========================================================================*/ diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.cpp b/src/QtLocationPlugin/QGCTileCacheWorker.cpp index 25e2210e658..f76e8e3f9a8 100644 --- a/src/QtLocationPlugin/QGCTileCacheWorker.cpp +++ b/src/QtLocationPlugin/QGCTileCacheWorker.cpp @@ -7,20 +7,12 @@ * ****************************************************************************/ - -/** - * @file - * @brief Map Tile Cache Worker Thread - * - * @author Gus Grubba - * - */ - #include "QGCTileCacheWorker.h" #include "QGCCachedTileSet.h" #include "QGCMapTasks.h" #include "QGCMapUrlEngine.h" #include "QGCLoggingCategory.h" +#include "QGCTileCacheDatabase.h" #include #include @@ -29,8 +21,7 @@ #include #include #include - -QByteArray QGCCacheWorker::_bingNoTileImage; +#include QGC_LOGGING_CATEGORY(QGCTileCacheWorkerLog, "qgc.qtlocationplugin.qgctilecacheworker") @@ -91,7 +82,7 @@ QGCCacheWorker::run() if (_valid) { if (_connectDB()) { - _deleteBingNoTileTiles(); + (void) TilesTableModel::deleteBingNoTileImageTiles(_db); } } @@ -132,9 +123,16 @@ QGCCacheWorker::run() void QGCCacheWorker::_runTask(QGCMapTask *task) { + if (task->type() == QGCMapTask::taskInit) { + return; + } + + if (!_valid) { + task->setError(tr("No Cache Database")); + return; + } + switch (task->type()) { - case QGCMapTask::taskInit: - break; case QGCMapTask::taskCacheTile: _saveTile(task); break; @@ -177,182 +175,64 @@ void QGCCacheWorker::_runTask(QGCMapTask *task) } } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_deleteBingNoTileTiles() +bool QGCCacheWorker::_findTileSetID(const QString &name, quint64 &setID) { - QSettings settings; - static const char* alreadyDoneKey = "_deleteBingNoTileTilesDone"; - - if (settings.value(alreadyDoneKey, false).toBool()) { - return; - } - settings.setValue(alreadyDoneKey, true); - - // Previously we would store these empty tile graphics in the cache. This prevented the ability to zoom beyong the level - // of available tiles. So we need to remove only of these still hanging around to make higher zoom levels work. - QFile file(":/res/BingNoTileBytes.dat"); - file.open(QFile::ReadOnly); - QByteArray noTileBytes = file.readAll(); - file.close(); - - QSqlQuery query(*_db); - QString s; - //-- Select tiles in default set only, sorted by oldest. - s = QString("SELECT tileID, tile, hash FROM Tiles WHERE LENGTH(tile) = %1").arg(noTileBytes.length()); - QList idsToDelete; - if (query.exec(s)) { - while(query.next()) { - if (query.value(1).toByteArray() == noTileBytes) { - idsToDelete.append(query.value(0).toULongLong()); - qCDebug(QGCTileCacheWorkerLog) << "_deleteBingNoTileTiles HASH:" << query.value(2).toString(); - } - } - for (const quint64 tileId: idsToDelete) { - s = QString("DELETE FROM Tiles WHERE tileID = %1").arg(tileId); - if (!query.exec(s)) { - qCWarning(QGCTileCacheWorkerLog) << "Delete failed"; - } - } - } else { - qCWarning(QGCTileCacheWorkerLog) << "_deleteBingNoTileTiles query failed"; - } + return TileSetsTableModel::getTileSetID(_db, setID, name); } -//----------------------------------------------------------------------------- -bool -QGCCacheWorker::_findTileSetID(const QString &name, quint64& setID) +quint64 QGCCacheWorker::_getDefaultTileSet() { - QSqlQuery query(*_db); - QString s = QString("SELECT setID FROM TileSets WHERE name = \"%1\"").arg(name); - if(query.exec(s)) { - if(query.next()) { - setID = query.value(0).toULongLong(); - return true; - } - } - return false; + quint64 result; + (void) TileSetsTableModel::getDefaultTileSet(_db, result); + return result; } -//----------------------------------------------------------------------------- -quint64 -QGCCacheWorker::_getDefaultTileSet() +void QGCCacheWorker::_saveTile(QGCMapTask *mtask) { - if(_defaultSet != UINT64_MAX) - return _defaultSet; - QSqlQuery query(*_db); - QString s = QString("SELECT setID FROM TileSets WHERE defaultSet = 1"); - if(query.exec(s)) { - if(query.next()) { - _defaultSet = query.value(0).toULongLong(); - return _defaultSet; - } - } - return 1L; -} + QGCSaveTileTask *const task = static_cast(mtask); -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_saveTile(QGCMapTask *mtask) -{ - if(_valid) { - QGCSaveTileTask* task = static_cast(mtask); - QSqlQuery query(*_db); - query.prepare("INSERT INTO Tiles(hash, format, tile, size, type, date) VALUES(?, ?, ?, ?, ?, ?)"); - query.addBindValue(task->tile()->hash()); - query.addBindValue(task->tile()->format()); - query.addBindValue(task->tile()->img()); - query.addBindValue(task->tile()->img().size()); - query.addBindValue(task->tile()->type()); - query.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); - if(query.exec()) { - quint64 tileID = query.lastInsertId().toULongLong(); - quint64 setID = task->tile()->tileSet() == UINT64_MAX ? _getDefaultTileSet() : task->tile()->tileSet(); - QString s = QString("INSERT INTO SetTiles(tileID, setID) VALUES(%1, %2)").arg(tileID).arg(setID); - query.prepare(s); - if(!query.exec()) { - qWarning() << "Map Cache SQL error (add tile into SetTiles):" << query.lastError().text(); - } - qCDebug(QGCTileCacheWorkerLog) << "_saveTile() HASH:" << task->tile()->hash(); - } else { - //-- Tile was already there. - // QtLocation some times requests the same tile twice in a row. The first is saved, the second is already there. - } - } else { - qWarning() << "Map Cache SQL error (saveTile() open db):" << _db->lastError(); + quint64 tileID; + if (!TilesTableModel::insertTile(_db, tileID, *task->tile())) { + return; } + + const quint64 setID = (task->tile()->tileSet() == UINT64_MAX) ? _getDefaultTileSet() : task->tile()->tileSet(); + (void) SetTilesTableModel::insertSetTiles(_db, setID, tileID); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_getTile(QGCMapTask* mtask) +void QGCCacheWorker::_getTile(QGCMapTask *mtask) { - if(!_testTask(mtask)) { + QGCFetchTileTask *const task = static_cast(mtask); + + QGCCacheTile *tile = nullptr; + if (!TilesTableModel::getTile(_db, tile, task->hash())) { + task->setError(tr("Tile not in cache database")); return; } - bool found = false; - QGCFetchTileTask* task = static_cast(mtask); - QSqlQuery query(*_db); - QString s = QString("SELECT tile, format, type FROM Tiles WHERE hash = \"%1\"").arg(task->hash()); - if(query.exec(s)) { - if(query.next()) { - const QByteArray& arrray = query.value(0).toByteArray(); - const QString& format = query.value(1).toString(); - const QString& type = query.value(2).toString(); - qCDebug(QGCTileCacheWorkerLog) << "_getTile() (Found in DB) HASH:" << task->hash(); - QGCCacheTile* tile = new QGCCacheTile(task->hash(), arrray, format, type); - task->setTileFetched(tile); - found = true; - } - } - if(!found) { - qCDebug(QGCTileCacheWorkerLog) << "_getTile() (NOT in DB) HASH:" << task->hash(); - task->setError("Tile not in cache database"); - } + + task->setTileFetched(tile); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_getTileSets(QGCMapTask* mtask) +void QGCCacheWorker::_getTileSets(QGCMapTask *mtask) { - if(!_testTask(mtask)) { + QGCFetchTileSetTask *const task = static_cast(mtask); + + QList tileSets; + if (!TileSetsTableModel::getTileSets(_db, tileSets)) { + task->setError(tr("No tile set in database")); return; } - QGCFetchTileSetTask* task = static_cast(mtask); - QSqlQuery query(*_db); - QString s = QString("SELECT * FROM TileSets ORDER BY defaultSet DESC, name ASC"); - qCDebug(QGCTileCacheWorkerLog) << "_getTileSets(): " << s; - if(query.exec(s)) { - while(query.next()) { - QString name = query.value("name").toString(); - QGCCachedTileSet* set = new QGCCachedTileSet(name); - set->setId(query.value("setID").toULongLong()); - set->setMapTypeStr(query.value("typeStr").toString()); - set->setTopleftLat(query.value("topleftLat").toDouble()); - set->setTopleftLon(query.value("topleftLon").toDouble()); - set->setBottomRightLat(query.value("bottomRightLat").toDouble()); - set->setBottomRightLon(query.value("bottomRightLon").toDouble()); - set->setMinZoom(query.value("minZoom").toInt()); - set->setMaxZoom(query.value("maxZoom").toInt()); - set->setType(UrlFactory::getProviderTypeFromQtMapId(query.value("type").toInt())); - set->setTotalTileCount(query.value("numTiles").toUInt()); - set->setDefaultSet(query.value("defaultSet").toInt() != 0); - set->setCreationDate(QDateTime::fromSecsSinceEpoch(query.value("date").toUInt())); - _updateSetTotals(set); - //-- Object created here must be moved to app thread to be used there - set->moveToThread(QCoreApplication::instance()->thread()); - task->tileSetFetched(set); - } - } else { - task->setError("No tile set in database"); + + for (QGCCachedTileSet *tileSet: tileSets) { + _updateSetTotals(tileSet); + tileSet->moveToThread(QCoreApplication::instance()->thread()); + task->tileSetFetched(tileSet); } } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_updateSetTotals(QGCCachedTileSet* set) +void QGCCacheWorker::_updateSetTotals(QGCCachedTileSet *set) { - if(set->defaultSet()) { + if (set->defaultSet()) { _updateTotals(); set->setSavedTileCount(_totalCount); set->setSavedTileSize(_totalSize); @@ -360,73 +240,15 @@ QGCCacheWorker::_updateSetTotals(QGCCachedTileSet* set) set->setTotalTileSize(_defaultSize); return; } - QSqlQuery subquery(*_db); - QString sq = QString("SELECT COUNT(size), SUM(size) FROM Tiles A INNER JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = %1").arg(set->id()); - qCDebug(QGCTileCacheWorkerLog) << "_updateSetTotals(): " << sq; - if(subquery.exec(sq)) { - if(subquery.next()) { - set->setSavedTileCount(subquery.value(0).toUInt()); - set->setSavedTileSize(subquery.value(1).toULongLong()); - qCDebug(QGCTileCacheWorkerLog) << "Set" << set->id() << "Totals:" << set->savedTileCount() << " " << set->savedTileSize() << "Expected: " << set->totalTileCount() << " " << set->totalTilesSize(); - //-- Update (estimated) size - quint64 avg = UrlFactory::averageSizeForType(set->type()); - if(set->totalTileCount() <= set->savedTileCount()) { - //-- We're done so the saved size is the total size - set->setTotalTileSize(set->savedTileSize()); - } else { - //-- Otherwise we need to estimate it. - if(set->savedTileCount() > 10 && set->savedTileSize()) { - avg = set->savedTileSize() / set->savedTileCount(); - } - set->setTotalTileSize(avg * set->totalTileCount()); - } - //-- Now figure out the count for tiles unique to this set - quint32 ucount = 0; - quint64 usize = 0; - sq = QString("SELECT COUNT(size), SUM(size) FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A join SetTiles B on A.tileID = B.tileID WHERE B.setID = %1 GROUP by A.tileID HAVING COUNT(A.tileID) = 1)").arg(set->id()); - if(subquery.exec(sq)) { - if(subquery.next()) { - //-- This is only accurate when all tiles are downloaded - ucount = subquery.value(0).toUInt(); - usize = subquery.value(1).toULongLong(); - } - } - //-- If we haven't downloaded it all, estimate size of unique tiles - quint32 expectedUcount = set->totalTileCount() - set->savedTileCount(); - if(!ucount) { - usize = expectedUcount * avg; - } else { - expectedUcount = ucount; - } - set->setUniqueTileCount(expectedUcount); - set->setUniqueTileSize(usize); - } - } + + (void) TilesTableModel::updateSetTotals(_db, *set); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_updateTotals() +void QGCCacheWorker::_updateTotals() { - QSqlQuery query(*_db); - QString s; - s = QString("SELECT COUNT(size), SUM(size) FROM Tiles"); - qCDebug(QGCTileCacheWorkerLog) << "_updateTotals(): " << s; - if(query.exec(s)) { - if(query.next()) { - _totalCount = query.value(0).toUInt(); - _totalSize = query.value(1).toULongLong(); - } - } - s = QString("SELECT COUNT(size), SUM(size) FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A join SetTiles B on A.tileID = B.tileID WHERE B.setID = %1 GROUP by A.tileID HAVING COUNT(A.tileID) = 1)").arg(_getDefaultTileSet()); - qCDebug(QGCTileCacheWorkerLog) << "_updateTotals(): " << s; - if(query.exec(s)) { - if(query.next()) { - _defaultCount = query.value(0).toUInt(); - _defaultSize = query.value(1).toULongLong(); - } - } + (void) TilesTableModel::updateTotals(_db, _totalCount, _totalSize, _defaultCount, _defaultSize, _getDefaultTileSet()); emit updateTotals(_totalCount, _totalSize, _defaultCount, _defaultSize); + if (!_updateTimer.isValid()) { _updateTimer.start(); } else { @@ -434,330 +256,163 @@ QGCCacheWorker::_updateTotals() } } -//----------------------------------------------------------------------------- quint64 QGCCacheWorker::_findTile(const QString &hash) { - quint64 tileID = 0; - QSqlQuery query(*_db); - QString s = QString("SELECT tileID FROM Tiles WHERE hash = \"%1\"").arg(hash); - if(query.exec(s)) { - if(query.next()) { - tileID = query.value(0).toULongLong(); - } - } + quint64 tileID; + (void) TilesTableModel::getTileID(_db, tileID, hash); return tileID; } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_createTileSet(QGCMapTask *mtask) +void QGCCacheWorker::_createTileSet(QGCMapTask *mtask) { - if(_valid) { - //-- Create Tile Set - quint32 actual_count = 0; - QGCCreateTileSetTask* task = static_cast(mtask); - QSqlQuery query(*_db); - query.prepare("INSERT INTO TileSets(" - "name, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, date" - ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - query.addBindValue(task->tileSet()->name()); - query.addBindValue(task->tileSet()->mapTypeStr()); - query.addBindValue(task->tileSet()->topleftLat()); - query.addBindValue(task->tileSet()->topleftLon()); - query.addBindValue(task->tileSet()->bottomRightLat()); - query.addBindValue(task->tileSet()->bottomRightLon()); - query.addBindValue(task->tileSet()->minZoom()); - query.addBindValue(task->tileSet()->maxZoom()); - query.addBindValue(UrlFactory::getQtMapIdFromProviderType(task->tileSet()->type())); - query.addBindValue(task->tileSet()->totalTileCount()); - query.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); - if(!query.exec()) { - qWarning() << "Map Cache SQL error (add tileSet into TileSets):" << query.lastError().text(); - } else { - //-- Get just created (auto-incremented) setID - quint64 setID = query.lastInsertId().toULongLong(); - task->tileSet()->setId(setID); - //-- Prepare Download List - _db->transaction(); - for(int z = task->tileSet()->minZoom(); z <= task->tileSet()->maxZoom(); z++) { - QGCTileSet set = UrlFactory::getTileCount(z, - task->tileSet()->topleftLon(), task->tileSet()->topleftLat(), - task->tileSet()->bottomRightLon(), task->tileSet()->bottomRightLat(), task->tileSet()->type()); - QString type = task->tileSet()->type(); - for(int x = set.tileX0; x <= set.tileX1; x++) { - for(int y = set.tileY0; y <= set.tileY1; y++) { - //-- See if tile is already downloaded - QString hash = UrlFactory::getTileHash(type, x, y, z); - quint64 tileID = _findTile(hash); - if(!tileID) { - //-- Set to download - query.prepare("INSERT OR IGNORE INTO TilesDownload(setID, hash, type, x, y, z, state) VALUES(?, ?, ?, ?, ? ,? ,?)"); - query.addBindValue(setID); - query.addBindValue(hash); - query.addBindValue(UrlFactory::getQtMapIdFromProviderType(type)); - query.addBindValue(x); - query.addBindValue(y); - query.addBindValue(z); - query.addBindValue(0); - if(!query.exec()) { - qWarning() << "Map Cache SQL error (add tile into TilesDownload):" << query.lastError().text(); - mtask->setError("Error creating tile set download list"); - return; - } else - actual_count++; - } else { - //-- Tile already in the database. No need to dowload. - QString s = QString("INSERT OR IGNORE INTO SetTiles(tileID, setID) VALUES(%1, %2)").arg(tileID).arg(setID); - query.prepare(s); - if(!query.exec()) { - qWarning() << "Map Cache SQL error (add tile into SetTiles):" << query.lastError().text(); - } - qCDebug(QGCTileCacheWorkerLog) << "_createTileSet() Already Cached HASH:" << hash; - } - } - } - } - _db->commit(); - //-- Done - _updateSetTotals(task->tileSet()); - task->setTileSetSaved(); - return; - } + QGCCreateTileSetTask *const task = static_cast(mtask); + + QGCCachedTileSet *const tileSet = task->tileSet(); + + quint64 setID; + if (!TileSetsTableModel::insertTileSet(_db, *tileSet, setID)) { + mtask->setError(tr("Error saving tile set")); + return; } - mtask->setError("Error saving tile set"); -} -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_getTileDownloadList(QGCMapTask* mtask) -{ - if(!_testTask(mtask)) { + tileSet->setId(setID); + + if (!TilesDownloadTableModel::insertTilesDownload(_db, tileSet)) { return; } + + _updateSetTotals(task->tileSet()); + task->setTileSetSaved(); +} + +void QGCCacheWorker::_getTileDownloadList(QGCMapTask *mtask) +{ + QGCGetTileDownloadListTask *const task = static_cast(mtask); + QQueue tiles; - QGCGetTileDownloadListTask* task = static_cast(mtask); - QSqlQuery query(*_db); - QString s = QString("SELECT hash, type, x, y, z FROM TilesDownload WHERE setID = %1 AND state = 0 LIMIT %2").arg(task->setID()).arg(task->count()); - if(query.exec(s)) { - while(query.next()) { - QGCTile* tile = new QGCTile; - // tile->setTileSet(task->setID()); - tile->setHash(query.value("hash").toString()); - tile->setType(UrlFactory::getProviderTypeFromQtMapId(query.value("type").toInt())); - tile->setX(query.value("x").toInt()); - tile->setY(query.value("y").toInt()); - tile->setZ(query.value("z").toInt()); - tiles.enqueue(tile); - } - for(int i = 0; i < tiles.size(); i++) { - s = QString("UPDATE TilesDownload SET state = %1 WHERE setID = %2 and hash = \"%3\"").arg(static_cast(QGCTile::StateDownloading)).arg(task->setID()).arg(tiles[i]->hash()); - if(!query.exec(s)) { - qWarning() << "Map Cache SQL error (set TilesDownload state):" << query.lastError().text(); - } - } - } + (void) TilesDownloadTableModel::getTileDownloadList(_db, tiles, task->setID(), task->count()); task->setTileListFetched(tiles); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_updateTileDownloadState(QGCMapTask* mtask) +void QGCCacheWorker::_updateTileDownloadState(QGCMapTask *mtask) { - if(!_testTask(mtask)) { - return; - } - QGCUpdateTileDownloadStateTask* task = static_cast(mtask); - QSqlQuery query(*_db); - QString s; - if(task->state() == QGCTile::StateComplete) { - s = QString("DELETE FROM TilesDownload WHERE setID = %1 AND hash = \"%2\"").arg(task->setID()).arg(task->hash()); - } else { - if(task->hash() == "*") { - s = QString("UPDATE TilesDownload SET state = %1 WHERE setID = %2").arg(static_cast(task->state())).arg(task->setID()); - } else { - s = QString("UPDATE TilesDownload SET state = %1 WHERE setID = %2 AND hash = \"%3\"").arg(static_cast(task->state())).arg(task->setID()).arg(task->hash()); - } - } - if(!query.exec(s)) { - qWarning() << "QGCCacheWorker::_updateTileDownloadState() Error:" << query.lastError().text(); - } + QGCUpdateTileDownloadStateTask *const task = static_cast(mtask); + + (void) TilesDownloadTableModel::updateTilesDownloadSet(_db, task->state(), task->setID(), task->hash()); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_pruneCache(QGCMapTask* mtask) +void QGCCacheWorker::_pruneCache(QGCMapTask *mtask) { - if(!_testTask(mtask)) { - return; - } - QGCPruneCacheTask* task = static_cast(mtask); - QSqlQuery query(*_db); - QString s; - //-- Select tiles in default set only, sorted by oldest. - s = QString("SELECT tileID, size, hash FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A join SetTiles B on A.tileID = B.tileID WHERE B.setID = %1 GROUP by A.tileID HAVING COUNT(A.tileID) = 1) ORDER BY DATE ASC LIMIT 128").arg(_getDefaultTileSet()); - qint64 amount = (qint64)task->amount(); - QList tlist; - if(query.exec(s)) { - while(query.next() && amount >= 0) { - tlist << query.value(0).toULongLong(); - amount -= query.value(1).toULongLong(); - qCDebug(QGCTileCacheWorkerLog) << "_pruneCache() HASH:" << query.value(2).toString(); - } - while(tlist.count()) { - s = QString("DELETE FROM Tiles WHERE tileID = %1").arg(tlist[0]); - tlist.removeFirst(); - if(!query.exec(s)) - break; - } + QGCPruneCacheTask *const task = static_cast(mtask); + + if (TilesTableModel::prune(_db, _getDefaultTileSet(), task->amount())) { task->setPruned(); } } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_deleteTileSet(QGCMapTask* mtask) +void QGCCacheWorker::_deleteTileSet(QGCMapTask *mtask) { - if(!_testTask(mtask)) { - return; - } - QGCDeleteTileSetTask* task = static_cast(mtask); + QGCDeleteTileSetTask *const task = static_cast(mtask); + _deleteTileSet(task->setID()); task->setTileSetDeleted(); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_deleteTileSet(qulonglong id) +void QGCCacheWorker::_deleteTileSet(quint64 id) { - QSqlQuery query(*_db); - QString s; - //-- Only delete tiles unique to this set - s = QString("DELETE FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = %1 GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)").arg(id); - query.exec(s); - s = QString("DELETE FROM TilesDownload WHERE setID = %1").arg(id); - query.exec(s); - s = QString("DELETE FROM TileSets WHERE setID = %1").arg(id); - query.exec(s); - s = QString("DELETE FROM SetTiles WHERE setID = %1").arg(id); - query.exec(s); + (void) TilesTableModel::deleteTileSet(_db, id); + (void) TilesDownloadTableModel::deleteTileSet(_db, id); + (void) TileSetsTableModel::deleteTileSet(_db, id); + (void) SetTilesTableModel::deleteTileSet(_db, id); + _updateTotals(); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_renameTileSet(QGCMapTask* mtask) +void QGCCacheWorker::_renameTileSet(QGCMapTask *mtask) { - if(!_testTask(mtask)) { - return; - } - QGCRenameTileSetTask* task = static_cast(mtask); - QSqlQuery query(*_db); - QString s; - s = QString("UPDATE TileSets SET name = \"%1\" WHERE setID = %2").arg(task->newName()).arg(task->setID()); - if(!query.exec(s)) { - task->setError("Error renaming tile set"); + QGCRenameTileSetTask *const task = static_cast(mtask); + + if (!TileSetsTableModel::setName(_db, task->setID(), task->newName())) { + task->setError(tr("Error renaming tile set")); } } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_resetCacheDatabase(QGCMapTask* mtask) +void QGCCacheWorker::_resetCacheDatabase(QGCMapTask *mtask) { - if(!_testTask(mtask)) { - return; - } - QGCResetTask* task = static_cast(mtask); - QSqlQuery query(*_db); - QString s; - s = QString("DROP TABLE Tiles"); - query.exec(s); - s = QString("DROP TABLE TileSets"); - query.exec(s); - s = QString("DROP TABLE SetTiles"); - query.exec(s); - s = QString("DROP TABLE TilesDownload"); - query.exec(s); - _valid = _createDB(*_db); + QGCResetTask *const task = static_cast(mtask); + + (void) TilesTableModel::drop(_db); + (void) TileSetsTableModel::drop(_db); + (void) SetTilesTableModel::drop(_db); + (void) TilesDownloadTableModel::drop(_db); + + _valid = _createDB(_db); task->setResetCompleted(); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_importSets(QGCMapTask* mtask) +void QGCCacheWorker::_importSets(QGCMapTask *mtask) { - if(!_testTask(mtask)) { - return; - } - QGCImportTileTask* task = static_cast(mtask); - //-- If replacing, simply copy over it - if(task->replace()) { - //-- Close and delete old database + QGCImportTileTask *const task = static_cast(mtask); + if (task->replace()) { _disconnectDB(); + QFile file(_databasePath); - file.remove(); - //-- Copy given database - QFile::copy(task->path(), _databasePath); + (void) file.remove(); + (void) QFile::copy(task->path(), _databasePath); + task->setProgress(25); - _init(); - if(_valid) { + (void) _init(); + if (_valid) { task->setProgress(50); - _connectDB(); + (void) _connectDB(); } task->setProgress(100); } else { - //-- Open imported set - QSqlDatabase* dbImport = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kExportSession)); + std::shared_ptr dbImport = std::make_shared(QSqlDatabase::addDatabase("QSQLITE", kExportSession)); + dbImport->setDatabaseName(task->path()); dbImport->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE"); + if (dbImport->open()) { QSqlQuery query(*dbImport); - //-- Prepare progress report - quint64 tileCount = 0; + quint64 currentCount = 0; int lastProgress = -1; - QString s; - s = QString("SELECT COUNT(tileID) FROM Tiles"); - if(query.exec(s)) { - if(query.next()) { - //-- Total number of tiles in imported database - tileCount = query.value(0).toULongLong(); - } - } - if(tileCount) { - //-- Iterate Tile Sets - s = QString("SELECT * FROM TileSets ORDER BY defaultSet DESC, name ASC"); - if(query.exec(s)) { - while(query.next()) { - QString name = query.value("name").toString(); - quint64 setID = query.value("setID").toULongLong(); - QString mapType = query.value("typeStr").toString(); - double topleftLat = query.value("topleftLat").toDouble(); - double topleftLon = query.value("topleftLon").toDouble(); - double bottomRightLat = query.value("bottomRightLat").toDouble(); - double bottomRightLon = query.value("bottomRightLon").toDouble(); - int minZoom = query.value("minZoom").toInt(); - int maxZoom = query.value("maxZoom").toInt(); - int type = query.value("type").toInt(); - quint32 numTiles = query.value("numTiles").toUInt(); - int defaultSet = query.value("defaultSet").toInt(); - quint64 insertSetID = _getDefaultTileSet(); - //-- If not default set, create new one - if(!defaultSet) { - //-- Check if we have this tile set already - if(_findTileSetID(name, insertSetID)) { + + quint64 tileCount = 0; + (void) TilesTableModel::getTileCount(dbImport, tileCount); + + if (tileCount) { + if (query.exec("SELECT * FROM TileSets ORDER BY defaultSet DESC, name ASC")) { + while (query.next()) { + QString name = query.value("name").toString(); + const quint64 setID = query.value("setID").toULongLong(); + const QString mapType = query.value("typeStr").toString(); + const double topleftLat = query.value("topleftLat").toDouble(); + const double topleftLon = query.value("topleftLon").toDouble(); + const double bottomRightLat = query.value("bottomRightLat").toDouble(); + const double bottomRightLon = query.value("bottomRightLon").toDouble(); + const int minZoom = query.value("minZoom").toInt(); + const int maxZoom = query.value("maxZoom").toInt(); + const int type = query.value("type").toInt(); + const quint32 numTiles = query.value("numTiles").toUInt(); + const int defaultSet = query.value("defaultSet").toInt(); + quint64 insertSetID = _getDefaultTileSet(); + + if (!defaultSet) { + if (_findTileSetID(name, insertSetID)) { int testCount = 0; - //-- Set with this name already exists. Make name unique. while (true) { - auto testName = QString::asprintf("%s %02d", name.toLatin1().data(), ++testCount); - if(!_findTileSetID(testName, insertSetID) || testCount > 99) { + const QString testName = QString::asprintf("%s %02d", name.toLatin1().data(), ++testCount); + if (!_findTileSetID(testName, insertSetID) || (testCount > 99)) { name = testName; break; } } } - //-- Create new set + QSqlQuery cQuery(*_db); - cQuery.prepare("INSERT INTO TileSets(" + (void) cQuery.prepare("INSERT INTO TileSets(" "name, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, defaultSet, date" ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); cQuery.addBindValue(name); @@ -772,340 +427,225 @@ QGCCacheWorker::_importSets(QGCMapTask* mtask) cQuery.addBindValue(numTiles); cQuery.addBindValue(defaultSet); cQuery.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); - if(!cQuery.exec()) { - task->setError("Error adding imported tile set to database"); + if (!cQuery.exec()) { + task->setError(tr("Error adding imported tile set to database")); break; } else { - //-- Get just created (auto-incremented) setID insertSetID = cQuery.lastInsertId().toULongLong(); } } - //-- Find set tiles + QSqlQuery cQuery(*_db); QSqlQuery subQuery(*dbImport); - QString sb = QString("SELECT * FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = %1 GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)").arg(setID); - if(subQuery.exec(sb)) { + const QString sb = QStringLiteral("SELECT * FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = %1 GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)").arg(setID); + if (subQuery.exec(sb)) { quint64 tilesFound = 0; quint64 tilesSaved = 0; - _db->transaction(); - while(subQuery.next()) { + (void) _db->transaction(); + while (subQuery.next()) { tilesFound++; - QString hash = subQuery.value("hash").toString(); - QString format = subQuery.value("format").toString(); - QByteArray img = subQuery.value("tile").toByteArray(); - int type = subQuery.value("type").toInt(); - //-- Save tile - cQuery.prepare("INSERT INTO Tiles(hash, format, tile, size, type, date) VALUES(?, ?, ?, ?, ?, ?)"); - cQuery.addBindValue(hash); - cQuery.addBindValue(format); - cQuery.addBindValue(img); - cQuery.addBindValue(img.size()); - cQuery.addBindValue(type); - cQuery.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); - if(cQuery.exec()) { + const QString hash = subQuery.value("hash").toString(); + const QString format = subQuery.value("format").toString(); + const QByteArray img = subQuery.value("tile").toByteArray(); + const QString type = subQuery.value("type").toString(); + // TODO: const int type = subQuery.value("type").toInt(); + + quint64 importTileID = 0; + if (TilesTableModel::insertTile(_db, importTileID, hash, format, img, type)) { tilesSaved++; - quint64 importTileID = cQuery.lastInsertId().toULongLong(); - QString s = QString("INSERT INTO SetTiles(tileID, setID) VALUES(%1, %2)").arg(importTileID).arg(insertSetID); - cQuery.prepare(s); - cQuery.exec(); + (void) SetTilesTableModel::insertSetTiles(_db, insertSetID, importTileID); currentCount++; - if(tileCount) { - int progress = (int)((double)currentCount / (double)tileCount * 100.0); - //-- Avoid calling this if (int) progress hasn't changed. - if(lastProgress != progress) { + if (tileCount != 0) { + const int progress = static_cast((static_cast(currentCount) / static_cast(tileCount)) * 100.0); + if (lastProgress != progress) { lastProgress = progress; task->setProgress(progress); } } } } - _db->commit(); - if(tilesSaved) { - //-- Update tile count (if any added) - s = QString("SELECT COUNT(size) FROM Tiles A INNER JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = %1").arg(insertSetID); - if(cQuery.exec(s)) { - if(cQuery.next()) { - quint64 count = cQuery.value(0).toULongLong(); - s = QString("UPDATE TileSets SET numTiles = %1 WHERE setID = %2").arg(count).arg(insertSetID); - cQuery.exec(s); - } + (void) _db->commit(); + + if (tilesSaved != 0) { + quint64 count = 0; + if (TilesTableModel::getSetTotal(_db, count, insertSetID)) { + (void) TileSetsTableModel::setNumTiles(_db, insertSetID, count); } } + qint64 uniqueTiles = tilesFound - tilesSaved; - if((quint64)uniqueTiles < tileCount) { + if (static_cast(uniqueTiles) < tileCount) { tileCount -= uniqueTiles; } else { tileCount = 0; } - //-- If there was nothing new in this set, remove it. - if(!tilesSaved && !defaultSet) { + + if ((tilesSaved == 0) && (defaultSet == 0)) { qCDebug(QGCTileCacheWorkerLog) << "No unique tiles in" << name << "Removing it."; _deleteTileSet(insertSetID); } } } } else { - task->setError("No tile set in database"); + task->setError(tr("No tile set in database")); } } - delete dbImport; + + dbImport.reset(); QSqlDatabase::removeDatabase(kExportSession); - if(!tileCount) { - task->setError("No unique tiles in imported database"); + + if (tileCount == 0) { + task->setError(tr("No unique tiles in imported database")); } } else { - task->setError("Error opening import database"); + task->setError(tr("Error opening import database")); } } + task->setImportCompleted(); } -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_exportSets(QGCMapTask* mtask) +void QGCCacheWorker::_exportSets(QGCMapTask *mtask) { - if(!_testTask(mtask)) { - return; - } - QGCExportTileTask* task = static_cast(mtask); - //-- Delete target if it exists + QGCExportTileTask *const task = static_cast(mtask); + QFile file(task->path()); - file.remove(); - //-- Create exported database - QScopedPointer dbExport(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kExportSession))); + (void) file.remove(); + + std::shared_ptr dbExport = std::make_shared(QSqlDatabase::addDatabase("QSQLITE", kExportSession)); + // TODO: QSqlDatabase::cloneDatabase? dbExport->setDatabaseName(task->path()); dbExport->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE"); + if (dbExport->open()) { - if(_createDB(*dbExport, false)) { - //-- Prepare progress report + if (_createDB(dbExport, false)) { quint64 tileCount = 0; - quint64 currentCount = 0; - for(int i = 0; i < task->sets().count(); i++) { - QGCCachedTileSet* set = task->sets()[i]; - //-- Default set has no unique tiles - if(set->defaultSet()) { + for (const QGCCachedTileSet *set: task->sets()) { + if (set->defaultSet()) { tileCount += set->totalTileCount(); } else { tileCount += set->uniqueTileCount(); } } - if(!tileCount) { + + if (!tileCount) { tileCount = 1; } - //-- Iterate sets to save - for(int i = 0; i < task->sets().count(); i++) { - QGCCachedTileSet* set = task->sets()[i]; - //-- Create Tile Exported Set - QSqlQuery exportQuery(*dbExport); - exportQuery.prepare("INSERT INTO TileSets(" - "name, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, defaultSet, date" - ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - exportQuery.addBindValue(set->name()); - exportQuery.addBindValue(set->mapTypeStr()); - exportQuery.addBindValue(set->topleftLat()); - exportQuery.addBindValue(set->topleftLon()); - exportQuery.addBindValue(set->bottomRightLat()); - exportQuery.addBindValue(set->bottomRightLon()); - exportQuery.addBindValue(set->minZoom()); - exportQuery.addBindValue(set->maxZoom()); - exportQuery.addBindValue(UrlFactory::getQtMapIdFromProviderType(set->type())); - exportQuery.addBindValue(set->totalTileCount()); - exportQuery.addBindValue(set->defaultSet()); - exportQuery.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); - if(!exportQuery.exec()) { - task->setError("Error adding tile set to exported database"); + + for (const QGCCachedTileSet *set: task->sets()) { + quint64 exportSetID = 0; + if (!TileSetsTableModel::insertTileSet(_db, *set, exportSetID)) { + task->setError(tr("Error adding tile set to exported database")); break; - } else { - //-- Get just created (auto-incremented) setID - quint64 exportSetID = exportQuery.lastInsertId().toULongLong(); - //-- Find set tiles - QString s = QString("SELECT * FROM SetTiles WHERE setID = %1").arg(set->id()); - QSqlQuery query(*_db); - if(query.exec(s)) { - dbExport->transaction(); - while(query.next()) { - quint64 tileID = query.value("tileID").toULongLong(); - //-- Get tile - QString s = QString("SELECT * FROM Tiles WHERE tileID = \"%1\"").arg(tileID); - QSqlQuery subQuery(*_db); - if(subQuery.exec(s)) { - if(subQuery.next()) { - QString hash = subQuery.value("hash").toString(); - QString format = subQuery.value("format").toString(); - QByteArray img = subQuery.value("tile").toByteArray(); - int type = subQuery.value("type").toInt(); - //-- Save tile - exportQuery.prepare("INSERT INTO Tiles(hash, format, tile, size, type, date) VALUES(?, ?, ?, ?, ?, ?)"); - exportQuery.addBindValue(hash); - exportQuery.addBindValue(format); - exportQuery.addBindValue(img); - exportQuery.addBindValue(img.size()); - exportQuery.addBindValue(type); - exportQuery.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); - if(exportQuery.exec()) { - quint64 exportTileID = exportQuery.lastInsertId().toULongLong(); - QString s = QString("INSERT INTO SetTiles(tileID, setID) VALUES(%1, %2)").arg(exportTileID).arg(exportSetID); - exportQuery.prepare(s); - exportQuery.exec(); - currentCount++; - task->setProgress((int)((double)currentCount / (double)tileCount * 100.0)); - } - } - } - } + } + + QString s = QStringLiteral("SELECT * FROM SetTiles WHERE setID = %1").arg(set->id()); + QSqlQuery query(*_db); + if (!query.exec(s)) { + continue; + } + + quint64 currentCount = 0; + (void) dbExport->transaction(); + while (query.next()) { + const quint64 tileID = query.value("tileID").toULongLong(); + s = QStringLiteral("SELECT * FROM Tiles WHERE tileID = \"%1\"").arg(tileID); + QSqlQuery subQuery(*_db); + if (!subQuery.exec(s) || !subQuery.next()) { + continue; + } + + const QString hash = subQuery.value("hash").toString(); + const QString format = subQuery.value("format").toString(); + const QByteArray img = subQuery.value("tile").toByteArray(); + const QString type = subQuery.value("type").toString(); + // TODO: const int type = subQuery.value("type").toInt(); + quint64 exportTileID = 0; + if (!TilesTableModel::insertTile(dbExport, exportTileID, hash, format, img, type)) { + continue; + } + + if (!SetTilesTableModel::insertSetTiles(dbExport, exportSetID, exportTileID)) { + continue; } - dbExport->commit(); + + currentCount++; + const double progress = (static_cast(currentCount) / static_cast(tileCount)) * 100.0; + task->setProgress(static_cast(progress)); } + (void) dbExport->commit(); } } else { - task->setError("Error creating export database"); + task->setError(tr("Error creating export database")); } } else { - qCritical() << "Map Cache SQL error (create export database):" << dbExport->lastError(); - task->setError("Error opening export database"); + qCCritical(QGCTileCacheWorkerLog) << "Map Cache SQL error (create export database):" << dbExport->lastError(); + task->setError(tr("Error opening export database")); } + dbExport.reset(); QSqlDatabase::removeDatabase(kExportSession); task->setExportCompleted(); } -//----------------------------------------------------------------------------- -bool QGCCacheWorker::_testTask(QGCMapTask* mtask) +bool QGCCacheWorker::_init() { - if(!_valid) { - mtask->setError("No Cache Database"); - return false; + bool result = false; + + qCDebug(QGCTileCacheWorkerLog) << "Mapping cache directory:" << _databasePath; + if (_databasePath.isEmpty()) { + qCCritical(QGCTileCacheWorkerLog) << "Could not find suitable cache directory."; + } else if (!_connectDB()) { + qCCritical(QGCTileCacheWorkerLog) << "Failed to initialize database connection"; + } else if (!_createDB(_db)) { + qCCritical(QGCTileCacheWorkerLog) << "Failed to create database"; + _valid = false; + } else { + result = true; } - return true; + + if (!result) { + _disconnectDB(); + } + + _failed = !result; + + return result; } -//----------------------------------------------------------------------------- -bool -QGCCacheWorker::_init() +bool QGCCacheWorker::_createDB(std::shared_ptr db, bool createDefault) { - _failed = false; - if(!_databasePath.isEmpty()) { - qCDebug(QGCTileCacheWorkerLog) << "Mapping cache directory:" << _databasePath; - //-- Initialize Database - if (_connectDB()) { - _valid = _createDB(*_db); - if(!_valid) { - _failed = true; + bool result = false; + + if (TilesTableModel::create(db) && SetTilesTableModel::create(db) && TilesDownloadTableModel::create(db)) { + if (createDefault) { + if (TileSetsTableModel::createDefaultTileSet(db)) { + result = true; } } else { - qCritical() << "Map Cache SQL error (init() open db):" << _db->lastError(); - _failed = true; + result = true; } - _disconnectDB(); - } else { - qCritical() << "Could not find suitable cache directory."; - _failed = true; } - return !_failed; + + if (!result) { + QFile file(_databasePath); + (void) file.remove(); + } + + return result; } -//----------------------------------------------------------------------------- -bool -QGCCacheWorker::_connectDB() +bool QGCCacheWorker::_connectDB() { _db.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession))); _db->setDatabaseName(_databasePath); _db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE"); + _valid = _db->open(); return _valid; } -//----------------------------------------------------------------------------- -bool -QGCCacheWorker::_createDB(QSqlDatabase& db, bool createDefault) -{ - bool res = false; - QSqlQuery query(db); - if(!query.exec( - "CREATE TABLE IF NOT EXISTS Tiles (" - "tileID INTEGER PRIMARY KEY NOT NULL, " - "hash TEXT NOT NULL UNIQUE, " - "format TEXT NOT NULL, " - "tile BLOB NULL, " - "size INTEGER, " - "type INTEGER, " - "date INTEGER DEFAULT 0)")) - { - qWarning() << "Map Cache SQL error (create Tiles db):" << query.lastError().text(); - } else { - query.exec("CREATE INDEX IF NOT EXISTS hash ON Tiles ( hash, size, type ) "); - - if(!query.exec( - "CREATE TABLE IF NOT EXISTS TileSets (" - "setID INTEGER PRIMARY KEY NOT NULL, " - "name TEXT NOT NULL UNIQUE, " - "typeStr TEXT, " - "topleftLat REAL DEFAULT 0.0, " - "topleftLon REAL DEFAULT 0.0, " - "bottomRightLat REAL DEFAULT 0.0, " - "bottomRightLon REAL DEFAULT 0.0, " - "minZoom INTEGER DEFAULT 3, " - "maxZoom INTEGER DEFAULT 3, " - "type INTEGER DEFAULT -1, " - "numTiles INTEGER DEFAULT 0, " - "defaultSet INTEGER DEFAULT 0, " - "date INTEGER DEFAULT 0)")) - { - qWarning() << "Map Cache SQL error (create TileSets db):" << query.lastError().text(); - } else { - if(!query.exec( - "CREATE TABLE IF NOT EXISTS SetTiles (" - "setID INTEGER, " - "tileID INTEGER)")) - { - qWarning() << "Map Cache SQL error (create SetTiles db):" << query.lastError().text(); - } else { - if(!query.exec( - "CREATE TABLE IF NOT EXISTS TilesDownload (" - "setID INTEGER, " - "hash TEXT NOT NULL UNIQUE, " - "type INTEGER, " - "x INTEGER, " - "y INTEGER, " - "z INTEGER, " - "state INTEGER DEFAULT 0)")) - { - qWarning() << "Map Cache SQL error (create TilesDownload db):" << query.lastError().text(); - } else { - //-- Database it ready for use - res = true; - } - } - } - } - //-- Create default tile set - if(res && createDefault) { - QString s = QString("SELECT name FROM TileSets WHERE name = \"%1\"").arg("Default Tile Set"); - if(query.exec(s)) { - if(!query.next()) { - query.prepare("INSERT INTO TileSets(name, defaultSet, date) VALUES(?, ?, ?)"); - query.addBindValue("Default Tile Set"); - query.addBindValue(1); - query.addBindValue(QDateTime::currentDateTime().toSecsSinceEpoch()); - if(!query.exec()) { - qWarning() << "Map Cache SQL error (Creating default tile set):" << db.lastError(); - res = false; - } - } - } else { - qWarning() << "Map Cache SQL error (Looking for default tile set):" << db.lastError(); - } - } - if(!res) { - QFile file(_databasePath); - file.remove(); - } - return res; -} - -//----------------------------------------------------------------------------- -void -QGCCacheWorker::_disconnectDB() +void QGCCacheWorker::_disconnectDB() { if (_db) { _db.reset(); diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.h b/src/QtLocationPlugin/QGCTileCacheWorker.h index 4db473a9630..1c5007a18bb 100644 --- a/src/QtLocationPlugin/QGCTileCacheWorker.h +++ b/src/QtLocationPlugin/QGCTileCacheWorker.h @@ -66,28 +66,25 @@ public slots: void _resetCacheDatabase(QGCMapTask *task); void _importSets(QGCMapTask *task); void _exportSets(QGCMapTask *task); - bool _testTask(QGCMapTask *task); bool _connectDB(); void _disconnectDB(); - bool _createDB(QSqlDatabase &db, bool createDefault = true); + bool _createDB(std::shared_ptr db, bool createDefault = true); bool _findTileSetID(const QString &name, quint64 &setID); bool _init(); quint64 _findTile(const QString &hash); quint64 _getDefaultTileSet(); - void _deleteBingNoTileTiles(); void _deleteTileSet(quint64 id); void _updateSetTotals(QGCCachedTileSet *set); void _updateTotals(); - std::shared_ptr _db = nullptr; + std::shared_ptr _db; QMutex _taskQueueMutex; QQueue _taskQueue; QWaitCondition _waitc; QString _databasePath; quint32 _defaultCount = 0; quint32 _totalCount = 0; - quint64 _defaultSet = UINT64_MAX; quint64 _defaultSize = 0; quint64 _totalSize = 0; QElapsedTimer _updateTimer; @@ -95,7 +92,6 @@ public slots: std::atomic_bool _failed = false; std::atomic_bool _valid = false; - static QByteArray _bingNoTileImage; static constexpr const char *kSession = "QGeoTileWorkerSession"; static constexpr const char *kExportSession = "QGeoTileExportSession"; static constexpr int kShortTimeout = 2;