You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
408 lines
13 KiB
408 lines
13 KiB
/**************************************************************************** |
|
* |
|
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> |
|
* |
|
* QGroundControl is licensed according to the terms in the file |
|
* COPYING.md in the root of the source code directory. |
|
* |
|
****************************************************************************/ |
|
|
|
|
|
/** |
|
* @file |
|
* @brief Map Tile Cache |
|
* |
|
* @author Gus Grubba <gus@auterion.com> |
|
* |
|
*/ |
|
#include "QGCApplication.h" |
|
#include "AppSettings.h" |
|
#include "SettingsManager.h" |
|
|
|
#include <math.h> |
|
#include <QSettings> |
|
#include <QStandardPaths> |
|
#include <QDir> |
|
#include <stdio.h> |
|
|
|
#include "QGCMapEngine.h" |
|
#include "QGCMapTileSet.h" |
|
|
|
Q_DECLARE_METATYPE(QGCMapTask::TaskType) |
|
Q_DECLARE_METATYPE(QGCTile) |
|
Q_DECLARE_METATYPE(QList<QGCTile*>) |
|
|
|
static const char* kDbFileName = "qgcMapCache.db"; |
|
static QLocale kLocale; |
|
|
|
#define CACHE_PATH_VERSION "300" |
|
|
|
struct stQGeoTileCacheQGCMapTypes { |
|
const char* name; |
|
QString type; |
|
}; |
|
|
|
static const char* kMaxDiskCacheKey = "MaxDiskCache"; |
|
static const char* kMaxMemCacheKey = "MaxMemoryCache"; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton |
|
static QGCMapEngine* kMapEngine = nullptr; |
|
QGCMapEngine* |
|
getQGCMapEngine() |
|
{ |
|
if(!kMapEngine) |
|
kMapEngine = new QGCMapEngine(); |
|
return kMapEngine; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
destroyMapEngine() |
|
{ |
|
delete kMapEngine; |
|
kMapEngine = nullptr; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QGCMapEngine::QGCMapEngine() |
|
: _urlFactory(new UrlFactory()) |
|
#ifdef WE_ARE_KOSHER |
|
//-- TODO: Get proper version |
|
#if defined Q_OS_MAC |
|
, _userAgent("QGroundControl (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/2.9.0") |
|
#elif defined Q_OS_WIN32 |
|
, _userAgent("QGroundControl (Windows; Windows NT 6.0) (KHTML, like Gecko) Version/2.9.0") |
|
#else |
|
, _userAgent("QGroundControl (X11; Ubuntu; Linux x86_64) (KHTML, like Gecko) Version/2.9.0") |
|
#endif |
|
#else |
|
#if defined Q_OS_MAC |
|
, _userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefox/25.0") |
|
#elif defined Q_OS_WIN32 |
|
, _userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20130401 Firefox/31.0") |
|
#else |
|
, _userAgent("Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0") |
|
#endif |
|
#endif |
|
, _maxDiskCache(0) |
|
, _maxMemCache(0) |
|
, _prunning(false) |
|
, _cacheWasReset(false) |
|
, _isInternetActive(false) |
|
{ |
|
qRegisterMetaType<QGCMapTask::TaskType>(); |
|
qRegisterMetaType<QGCTile>(); |
|
qRegisterMetaType<QList<QGCTile*>>(); |
|
connect(&_worker, &QGCCacheWorker::updateTotals, this, &QGCMapEngine::_updateTotals); |
|
connect(&_worker, &QGCCacheWorker::internetStatus, this, &QGCMapEngine::_internetStatus); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QGCMapEngine::~QGCMapEngine() |
|
{ |
|
_worker.quit(); |
|
_worker.wait(); |
|
delete _urlFactory; |
|
_urlFactory = nullptr; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::_checkWipeDirectory(const QString& dirPath) |
|
{ |
|
QDir dir(dirPath); |
|
if (dir.exists(dirPath)) { |
|
_cacheWasReset = true; |
|
_wipeDirectory(dirPath); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::_wipeOldCaches() |
|
{ |
|
QString oldCacheDir; |
|
#ifdef __mobile__ |
|
oldCacheDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/QGCMapCache55"); |
|
#else |
|
oldCacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/QGCMapCache55"); |
|
#endif |
|
_checkWipeDirectory(oldCacheDir); |
|
#ifdef __mobile__ |
|
oldCacheDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/QGCMapCache100"); |
|
#else |
|
oldCacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/QGCMapCache100"); |
|
#endif |
|
_checkWipeDirectory(oldCacheDir); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::init() |
|
{ |
|
//-- Delete old style caches (if present) |
|
_wipeOldCaches(); |
|
//-- Figure out cache path |
|
#ifdef __mobile__ |
|
QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/QGCMapCache" CACHE_PATH_VERSION); |
|
#else |
|
QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/QGCMapCache" CACHE_PATH_VERSION); |
|
#endif |
|
if(!QDir::root().mkpath(cacheDir)) { |
|
qWarning() << "Could not create mapping disk cache directory: " << cacheDir; |
|
cacheDir = QDir::homePath() + QStringLiteral("/.qgcmapscache/"); |
|
if(!QDir::root().mkpath(cacheDir)) { |
|
qWarning() << "Could not create mapping disk cache directory: " << cacheDir; |
|
cacheDir.clear(); |
|
} |
|
} |
|
_cachePath = cacheDir; |
|
if(!_cachePath.isEmpty()) { |
|
_cacheFile = kDbFileName; |
|
_worker.setDatabaseFile(_cachePath + "/" + _cacheFile); |
|
qDebug() << "Map Cache in:" << _cachePath << "/" << _cacheFile; |
|
} else { |
|
qCritical() << "Could not find suitable map cache directory."; |
|
} |
|
QGCMapTask* task = new QGCMapTask(QGCMapTask::taskInit); |
|
_worker.enqueueTask(task); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool |
|
QGCMapEngine::_wipeDirectory(const QString& dirPath) |
|
{ |
|
bool result = true; |
|
QDir dir(dirPath); |
|
if (dir.exists(dirPath)) { |
|
Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { |
|
if (info.isDir()) { |
|
result = _wipeDirectory(info.absoluteFilePath()); |
|
} else { |
|
result = QFile::remove(info.absoluteFilePath()); |
|
} |
|
if (!result) { |
|
return result; |
|
} |
|
} |
|
result = dir.rmdir(dirPath); |
|
} |
|
return result; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::addTask(QGCMapTask* task) |
|
{ |
|
_worker.enqueueTask(task); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::cacheTile(QString type, int x, int y, int z, const QByteArray& image, const QString &format, qulonglong set) |
|
{ |
|
QString hash = getTileHash(type, x, y, z); |
|
cacheTile(type, hash, image, format, set); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::cacheTile(QString type, const QString& hash, const QByteArray& image, const QString& format, qulonglong set) |
|
{ |
|
AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings(); |
|
//-- If we are allowed to persist data, save tile to cache |
|
if(!appSettings->disableAllPersistence()->rawValue().toBool()) { |
|
QGCSaveTileTask* task = new QGCSaveTileTask(new QGCCacheTile(hash, image, format, type, set)); |
|
_worker.enqueueTask(task); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QString |
|
QGCMapEngine::getTileHash(QString type, int x, int y, int z) |
|
{ |
|
return QString::asprintf("%010d%08d%08d%03d", getQGCMapEngine()->urlFactory()->getIdFromType(type), x, y, z); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QString |
|
QGCMapEngine::hashToType(const QString& hash) |
|
{ |
|
QString type = hash.mid(0,10); |
|
return urlFactory()->getTypeFromId(type.toInt()); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QGCFetchTileTask* |
|
QGCMapEngine::createFetchTileTask(QString type, int x, int y, int z) |
|
{ |
|
QString hash = getTileHash(type, x, y, z); |
|
QGCFetchTileTask* task = new QGCFetchTileTask(hash); |
|
return task; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QGCTileSet |
|
QGCMapEngine::getTileCount(int zoom, double topleftLon, double topleftLat, double bottomRightLon, double bottomRightLat, QString mapType) |
|
{ |
|
if(zoom < 1) zoom = 1; |
|
if(zoom > MAX_MAP_ZOOM) zoom = MAX_MAP_ZOOM; |
|
|
|
return getQGCMapEngine()->urlFactory()->getTileCount(zoom, topleftLon, topleftLat, bottomRightLon, bottomRightLat, mapType); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
QStringList |
|
QGCMapEngine::getMapNameList() |
|
{ |
|
return QStringList(getQGCMapEngine()->urlFactory()->getProviderTable().keys()); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
quint32 |
|
QGCMapEngine::getMaxDiskCache() |
|
{ |
|
if(!_maxDiskCache) { |
|
QSettings settings; |
|
_maxDiskCache = settings.value(kMaxDiskCacheKey, 1024).toUInt(); |
|
} |
|
return _maxDiskCache; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::setMaxDiskCache(quint32 size) |
|
{ |
|
QSettings settings; |
|
settings.setValue(kMaxDiskCacheKey, size); |
|
_maxDiskCache = size; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
quint32 |
|
QGCMapEngine::getMaxMemCache() |
|
{ |
|
if(!_maxMemCache) { |
|
QSettings settings; |
|
#ifdef __mobile__ |
|
_maxMemCache = settings.value(kMaxMemCacheKey, 16).toUInt(); |
|
#else |
|
_maxMemCache = settings.value(kMaxMemCacheKey, 128).toUInt(); |
|
#endif |
|
} |
|
//-- Size in MB |
|
if(_maxMemCache > 1024) |
|
_maxMemCache = 1024; |
|
return _maxMemCache; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::setMaxMemCache(quint32 size) |
|
{ |
|
//-- Size in MB |
|
if(size > 1024) |
|
size = 1024; |
|
QSettings settings; |
|
settings.setValue(kMaxMemCacheKey, size); |
|
_maxMemCache = size; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QString |
|
QGCMapEngine::bigSizeToString(quint64 size) |
|
{ |
|
if(size < 1024) |
|
return kLocale.toString(size); |
|
else if(size < 1024 * 1024) |
|
return kLocale.toString(static_cast<double>(size) / 1024.0, 'f', 1) + "kB"; |
|
else if(size < 1024 * 1024 * 1024) |
|
return kLocale.toString(static_cast<double>(size) / (1024.0 * 1024.0), 'f', 1) + "MB"; |
|
else if(size < 1024.0 * 1024.0 * 1024.0 * 1024.0) |
|
return kLocale.toString(static_cast<double>(size) / (1024.0 * 1024.0 * 1024.0), 'f', 1) + "GB"; |
|
else |
|
return kLocale.toString(static_cast<double>(size) / (1024.0 * 1024.0 * 1024.0 * 1024), 'f', 1) + "TB"; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QString |
|
QGCMapEngine::storageFreeSizeToString(quint64 size_MB) |
|
{ |
|
if(size_MB < 1024) |
|
return kLocale.toString(static_cast<double>(size_MB) , 'f', 0) + " MB"; |
|
else if(size_MB < 1024.0 * 1024.0) |
|
return kLocale.toString(static_cast<double>(size_MB) / (1024.0), 'f', 2) + " GB"; |
|
else |
|
return kLocale.toString(static_cast<double>(size_MB) / (1024.0 * 1024), 'f', 2) + " TB"; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QString |
|
QGCMapEngine::numberToString(quint64 number) |
|
{ |
|
return kLocale.toString(number); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::_updateTotals(quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize) |
|
{ |
|
emit updateTotals(totaltiles, totalsize, defaulttiles, defaultsize); |
|
quint64 maxSize = static_cast<quint64>(getMaxDiskCache()) * 1024L * 1024L; |
|
if(!_prunning && defaultsize > maxSize) { |
|
//-- Prune Disk Cache |
|
_prunning = true; |
|
QGCPruneCacheTask* task = new QGCPruneCacheTask(defaultsize - maxSize); |
|
connect(task, &QGCPruneCacheTask::pruned, this, &QGCMapEngine::_pruned); |
|
getQGCMapEngine()->addTask(task); |
|
} |
|
} |
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::_pruned() |
|
{ |
|
_prunning = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
int |
|
QGCMapEngine::concurrentDownloads(QString type) |
|
{ |
|
Q_UNUSED(type); |
|
// TODO : We may want different values depending on |
|
// the provider here, let it like this as all provider are set to 12 |
|
// at the moment |
|
return 12; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QGCCreateTileSetTask::~QGCCreateTileSetTask() |
|
{ |
|
//-- If not sent out, delete it |
|
if(!_saved && _tileSet) |
|
delete _tileSet; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::testInternet() |
|
{ |
|
if(qgcApp()->toolbox()->settingsManager()->appSettings()->checkInternet()->rawValue().toBool()) |
|
getQGCMapEngine()->addTask(new QGCTestInternetTask()); |
|
else |
|
_internetStatus(true); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
QGCMapEngine::_internetStatus(bool active) |
|
{ |
|
if(_isInternetActive != active) { |
|
_isInternetActive = active; |
|
emit internetUpdated(); |
|
} |
|
} |
|
|
|
// Resolution math: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Resolution_and_Scale
|
|
|