Commit 0878dfa2 authored by Gus Grubba's avatar Gus Grubba Committed by GitHub

Merge pull request #4817 from dogmaphobic/exportTileSet

Map Tile Set Import/Export
parents fbc34cc5 71813e49
......@@ -119,7 +119,9 @@ public:
taskUpdateTileDownloadState,
taskDeleteTileSet,
taskPruneCache,
taskReset
taskReset,
taskExport,
taskImport
};
QGCMapTask(TaskType type)
......@@ -365,5 +367,80 @@ signals:
void resetCompleted();
};
//-----------------------------------------------------------------------------
class QGCExportTileTask : public QGCMapTask
{
Q_OBJECT
public:
QGCExportTileTask(QVector<QGCCachedTileSet*> sets, QString path)
: QGCMapTask(QGCMapTask::taskExport)
, _sets(sets)
, _path(path)
{}
~QGCExportTileTask()
{
}
QVector<QGCCachedTileSet*> sets() { return _sets; }
QString path() { return _path; }
void setExportCompleted()
{
emit actionCompleted();
}
void setProgress(int percentage)
{
emit actionProgress(percentage);
}
private:
QVector<QGCCachedTileSet*> _sets;
QString _path;
signals:
void actionCompleted ();
void actionProgress (int percentage);
};
//-----------------------------------------------------------------------------
class QGCImportTileTask : public QGCMapTask
{
Q_OBJECT
public:
QGCImportTileTask(QString path, bool replace)
: QGCMapTask(QGCMapTask::taskImport)
, _path(path)
, _replace(replace)
{}
~QGCImportTileTask()
{
}
QString path () { return _path; }
bool replace () { return _replace; }
void setImportCompleted()
{
emit actionCompleted();
}
void setProgress(int percentage)
{
emit actionProgress(percentage);
}
private:
QString _path;
bool _replace;
signals:
void actionCompleted ();
void actionProgress (int percentage);
};
#endif // QGC_MAP_ENGINE_DATA_H
......@@ -52,6 +52,7 @@ QGCCachedTileSet::QGCCachedTileSet(const QString& name)
, _noMoreTiles(false)
, _batchRequested(false)
, _manager(NULL)
, _selected(false)
{
}
......@@ -349,3 +350,14 @@ QGCCachedTileSet::setManager(QGCMapEngineManager* mgr)
{
_manager = mgr;
}
//-----------------------------------------------------------------------------
void
QGCCachedTileSet::setSelected(bool sel)
{
_selected = sel;
emit selectedChanged();
if(_manager) {
emit _manager->selectedCountChanged();
}
}
......@@ -72,6 +72,8 @@ public:
Q_PROPERTY(quint32 errorCount READ errorCount NOTIFY errorCountChanged)
Q_PROPERTY(QString errorCountStr READ errorCountStr NOTIFY errorCountChanged)
Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged)
Q_INVOKABLE void createDownloadTask ();
Q_INVOKABLE void resumeDownloadTask ();
Q_INVOKABLE void cancelDownloadTask ();
......@@ -109,7 +111,9 @@ public:
bool downloading () { return _downloading; }
quint32 errorCount () { return _errorCount; }
QString errorCountStr ();
bool selected () { return _selected; }
void setSelected (bool sel);
void setName (QString name) { _name = name; }
void setMapTypeStr (QString typeStr) { _mapTypeStr = typeStr; }
void setTopleftLat (double lat) { _topleftLat = lat; }
......@@ -142,6 +146,7 @@ signals:
void savedTileSizeChanged ();
void completeChanged ();
void errorCountChanged ();
void selectedChanged ();
private slots:
void _tileListFetched (QList<QGCTile*> tiles);
......@@ -181,6 +186,7 @@ private:
bool _noMoreTiles;
bool _batchRequested;
QGCMapEngineManager* _manager;
bool _selected;
};
#endif // QGC_MAP_TILE_SET_H
......
......@@ -30,7 +30,8 @@
#include "time.h"
const char* kDefaultSet = "Default Tile Set";
const QString kSession = QLatin1String("QGeoTileWorkerSession");
const QString kSession = QLatin1String("QGeoTileWorkerSession");
const QString kExportSession = QLatin1String("QGeoTileExportSession");
QGC_LOGGING_CATEGORY(QGCTileCacheLog, "QGCTileCacheLog")
......@@ -153,6 +154,12 @@ QGCCacheWorker::run()
case QGCMapTask::taskReset:
_resetCacheDatabase(task);
break;
case QGCMapTask::taskExport:
_exportSets(task);
break;
case QGCMapTask::taskImport:
_importSets(task);
break;
case QGCMapTask::taskTestInternet:
_testInternet();
break;
......@@ -262,8 +269,7 @@ QGCCacheWorker::_saveTile(QGCMapTask *mtask)
void
QGCCacheWorker::_getTile(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
if(!_testTask(mtask)) {
return;
}
bool found = false;
......@@ -291,8 +297,7 @@ QGCCacheWorker::_getTile(QGCMapTask* mtask)
void
QGCCacheWorker::_getTileSets(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
if(!_testTask(mtask)) {
return;
}
QGCFetchTileSetTask* task = static_cast<QGCFetchTileSetTask*>(mtask);
......@@ -506,8 +511,7 @@ QGCCacheWorker::_createTileSet(QGCMapTask *mtask)
void
QGCCacheWorker::_getTileDownloadList(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
if(!_testTask(mtask)) {
return;
}
QList<QGCTile*> tiles;
......@@ -538,8 +542,7 @@ QGCCacheWorker::_getTileDownloadList(QGCMapTask* mtask)
void
QGCCacheWorker::_updateTileDownloadState(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
if(!_testTask(mtask)) {
return;
}
QGCUpdateTileDownloadStateTask* task = static_cast<QGCUpdateTileDownloadStateTask*>(mtask);
......@@ -563,8 +566,7 @@ QGCCacheWorker::_updateTileDownloadState(QGCMapTask* mtask)
void
QGCCacheWorker::_pruneCache(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
if(!_testTask(mtask)) {
return;
}
QGCPruneCacheTask* task = static_cast<QGCPruneCacheTask*>(mtask);
......@@ -594,8 +596,7 @@ QGCCacheWorker::_pruneCache(QGCMapTask* mtask)
void
QGCCacheWorker::_deleteTileSet(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
if(!_testTask(mtask)) {
return;
}
QGCDeleteTileSetTask* task = static_cast<QGCDeleteTileSetTask*>(mtask);
......@@ -613,12 +614,12 @@ QGCCacheWorker::_deleteTileSet(QGCMapTask* mtask)
_updateTotals();
task->setTileSetDeleted();
}
//-----------------------------------------------------------------------------
void
QGCCacheWorker::_resetCacheDatabase(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
if(!_testTask(mtask)) {
return;
}
QGCResetTask* task = static_cast<QGCResetTask*>(mtask);
......@@ -632,10 +633,288 @@ QGCCacheWorker::_resetCacheDatabase(QGCMapTask* mtask)
query.exec(s);
s = QString("DROP TABLE TilesDownload");
query.exec(s);
_createDB();
_valid = _createDB(_db);
task->setResetCompleted();
}
//-----------------------------------------------------------------------------
void
QGCCacheWorker::_importSets(QGCMapTask* mtask)
{
if(!_testTask(mtask)) {
return;
}
QGCImportTileTask* task = static_cast<QGCImportTileTask*>(mtask);
//-- If replacing, simply copy over it
if(task->replace()) {
//-- Close and delete old database
if(_db) {
delete _db;
_db = NULL;
QSqlDatabase::removeDatabase(kSession);
}
QFile file(_databasePath);
file.remove();
//-- Copy given database
QFile::copy(task->path(), _databasePath);
task->setProgress(25);
_init();
if(_valid) {
task->setProgress(50);
_db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession));
_db->setDatabaseName(_databasePath);
_db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
_valid = _db->open();
}
task->setProgress(100);
} else {
//-- Open imported set
QSqlDatabase* dbImport = new QSqlDatabase(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;
QString s;
s = QString("SELECT COUNT(tileID) FROM Tiles");
if(query.exec(s)) {
if(query.next()) {
tileCount = query.value(0).toULongLong();
}
}
if(!tileCount) {
qWarning() << "No tiles found in imported database";
tileCount = 1; //-- Let it run through
}
//-- 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
int testCount = 0;
while (true) {
QString testName;
testName.sprintf("%s %03d", name.toLatin1().data(), ++testCount);
if(!_findTileSetID(testName, insertSetID) || testCount > 99) {
if(testCount > 1) {
name = testName;
}
break;
}
}
//-- Create new set
QSqlQuery cQuery(*_db);
cQuery.prepare("INSERT INTO TileSets("
"name, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, defaultSet, date"
") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
cQuery.addBindValue(name);
cQuery.addBindValue(mapType);
cQuery.addBindValue(topleftLat);
cQuery.addBindValue(topleftLon);
cQuery.addBindValue(bottomRightLat);
cQuery.addBindValue(bottomRightLon);
cQuery.addBindValue(minZoom);
cQuery.addBindValue(maxZoom);
cQuery.addBindValue(type);
cQuery.addBindValue(numTiles);
cQuery.addBindValue(defaultSet);
cQuery.addBindValue(QDateTime::currentDateTime().toTime_t());
if(!cQuery.exec()) {
task->setError("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)) {
_db->transaction();
while(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
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().toTime_t());
if(cQuery.exec()) {
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();
currentCount++;
task->setProgress((int)((double)currentCount / (double)tileCount * 100.0));
}
}
_db->commit();
//-- Update tile count
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);
}
}
}
}
} else {
task->setError("No tile set in database");
}
delete dbImport;
QSqlDatabase::removeDatabase(kExportSession);
} else {
task->setError("Error opening import database");
}
}
task->setImportCompleted();
}
//-----------------------------------------------------------------------------
void
QGCCacheWorker::_exportSets(QGCMapTask* mtask)
{
if(!_testTask(mtask)) {
return;
}
QGCExportTileTask* task = static_cast<QGCExportTileTask*>(mtask);
//-- Delete target if it exists
QFile file(task->path());
file.remove();
//-- Create exported database
QSqlDatabase *dbExport = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kExportSession));
dbExport->setDatabaseName(task->path());
dbExport->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
if (dbExport->open()) {
if(_createDB(dbExport, false)) {
//-- Prepare progress report
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()) {
tileCount += set->totalTileCount();
} else {
tileCount += set->uniqueTileCount();
}
}
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(set->type());
exportQuery.addBindValue(set->totalTileCount());
exportQuery.addBindValue(set->defaultSet());
exportQuery.addBindValue(QDateTime::currentDateTime().toTime_t());
if(!exportQuery.exec()) {
task->setError("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().toTime_t());
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));
}
}
}
}
}
dbExport->commit();
}
}
} else {
task->setError("Error creating export database");
}
} else {
qCritical() << "Map Cache SQL error (create export database):" << dbExport->lastError();
task->setError("Error opening export database");
}
delete dbExport;
QSqlDatabase::removeDatabase(kExportSession);
task->setExportCompleted();
}
//-----------------------------------------------------------------------------
bool QGCCacheWorker::_testTask(QGCMapTask* mtask)
{
if(!_valid) {
mtask->setError("No Cache Database");
return false;
}
return true;
}
//-----------------------------------------------------------------------------
bool
QGCCacheWorker::_init()
......@@ -648,7 +927,10 @@ QGCCacheWorker::_init()
_db->setDatabaseName(_databasePath);
_db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
if (_db->open()) {
_createDB();
_valid = _createDB(_db);
if(!_valid) {
_failed = true;
}
} else {
qCritical() << "Map Cache SQL error (init() open db):" << _db->lastError();
_failed = true;
......@@ -665,10 +947,11 @@ QGCCacheWorker::_init()
}
//-----------------------------------------------------------------------------
void
QGCCacheWorker::_createDB()
bool
QGCCacheWorker::_createDB(QSqlDatabase* db, bool createDefault)
{
QSqlQuery query(*_db);
bool res = false;
QSqlQuery query(*db);
if(!query.exec(
"CREATE TABLE IF NOT EXISTS Tiles ("
"tileID INTEGER PRIMARY KEY NOT NULL, "
......@@ -719,13 +1002,13 @@ QGCCacheWorker::_createDB()
qWarning() << "Map Cache SQL error (create TilesDownload db):" << query.lastError().text();
} else {
//-- Database it ready for use
_valid = true;
res = true;
}
}
}
}
//-- Create default tile set
if(_valid) {
if(res && createDefault) {
QString s = QString("SELECT name FROM TileSets WHERE name = \"%1\"").arg(kDefaultSet);
if(query.exec(s)) {
if(!query.next()) {
......@@ -734,19 +1017,19 @@ QGCCacheWorker::_createDB()
query.addBindValue(1);
query.addBindValue(QDateTime::currentDateTime().toTime_t());
if(!query.exec()) {
qWarning() << "Map Cache SQL error (Creating default tile set):" << _db->lastError();
_valid = false;
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();
qWarning() << "Map Cache SQL error (Looking for default tile set):" << db->lastError();
}
}
if(!_valid) {
if(!res) {
QFile file(_databasePath);
file.remove();
}
_failed = !_valid;
return res;
}
//-----------------------------------------------------------------------------
......
......@@ -59,13 +59,16 @@ private:
void _deleteTileSet (QGCMapTask* mtask);
void _resetCacheDatabase (QGCMapTask* mtask);
void _pruneCache (QGCMapTask* mtask);
void _exportSets (QGCMapTask* mtask);
void _importSets (QGCMapTask* mtask);
bool _testTask (QGCMapTask* mtask);
void _testInternet ();
quint64 _findTile (const QString hash);
bool _findTileSetID (const QString name, quint64& setID);
void _updateSetTotals (QGCCachedTileSet* set);
bool _init ();
void _createDB ();
bool _createDB (QSqlDatabase *db, bool createDefault = true);
quint64 _getDefaultTileSet ();
void _updateTotals ();
......
......@@ -15,11 +15,12 @@ import QtQuick.Layouts 1.2
import QtLocation 5.3
import QtPositioning 5.3
import QGroundControl 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
import QGroundControl 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
import QGroundControl.QGCMapEngineManager 1.0
QGCView {
id: offlineMapView
......@@ -37,7 +38,9 @@ QGCView {
property string savedMapType: ""
property bool _showPreview: true
property bool _defaultSet: offlineMapView && offlineMapView._currentSelection && offlineMapView._currentSelection.defaultSet
property real _margins: ScreenTools.defaultFontPixelWidth / 2
property real _margins: ScreenTools.defaultFontPixelWidth * 0.5
property real _buttonSize: ScreenTools.defaultFontPixelWidth * 12
property real _bigButtonSize: ScreenTools.defaultFontPixelWidth * 16
property bool _saveRealEstate: ScreenTools.isTinyScreen || ScreenTools.isShortScreen
property real _adjustableFontPointSize: _saveRealEstate ? ScreenTools.smallFontPointSize : ScreenTools.defaultFontPointSize
......@@ -100,15 +103,27 @@ QGCView {
_map.visible = true
_tileSetList.visible = false
infoView.visible = false
_exporTiles.visible = false
addNewSetView.visible = true
}
function showList() {
_exporTiles.visible = false
isMapInteractive = false
_map.visible = false
_tileSetList.visible = true
infoView.visible = false
addNewSetView.visible = false
QGroundControl.mapEngineManager.resetAction();
}
function showExport() {
isMapInteractive = false
_map.visible = false
_tileSetList.visible = false
infoView.visible = false
addNewSetView.visible = false
_exporTiles.visible = true
}
function showInfo() {
......@@ -796,7 +811,7 @@ QGCView {
clip: true
anchors.margins: ScreenTools.defaultFontPixelWidth
anchors.top: parent.top
anchors.bottom: _optionsButton.top
anchors.bottom: _listButtonRow.top
anchors.left: parent.left
anchors.right: parent.right
contentHeight: _cacheList.height
......@@ -806,7 +821,6 @@ QGCView {
width: Math.min(_tileSetList.width, (ScreenTools.defaultFontPixelWidth * 50).toFixed(0))
spacing: ScreenTools.defaultFontPixelHeight * 0.5
anchors.horizontalCenter: parent.horizontalCenter
OfflineMapButton {
id: firstButton
text: qsTr("Add new set")
......@@ -834,15 +848,365 @@ QGCView {
}
}
}
Row {
id: _listButtonRow
visible: _tileSetList.visible
spacing: _margins
anchors.bottom: parent.bottom
anchors.margins: ScreenTools.defaultFontPixelWidth
anchors.horizontalCenter: parent.horizontalCenter
QGCButton {
text: qsTr("Import")
width: _buttonSize
visible: !ScreenTools.isMobile
onClicked: rootLoader.sourceComponent = importDialog
}
QGCButton {
text: qsTr("Export")
width: _buttonSize
visible: !ScreenTools.isMobile
enabled: QGroundControl.mapEngineManager.tileSets.count > 1
onClicked: showExport()
}
QGCButton {
text: qsTr("Options")
width: _buttonSize
onClicked: showDialog(optionsDialogComponent, qsTr("Offline Maps Options"), qgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
}
}
QGCButton {
id: _optionsButton
text: qsTr("Options")
visible: _tileSetList.visible
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: ScreenTools.defaultFontPixelWidth
onClicked: showDialog(optionsDialogComponent, qsTr("Offline Maps Options"), qgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel)
//-- Export Tile Sets
QGCFlickable {
id: _exporTiles
clip: true
visible: false
anchors.margins: ScreenTools.defaultFontPixelWidth
anchors.top: parent.top
anchors.bottom: _exportButtonRow.top
anchors.left: parent.left
anchors.right: parent.right
contentHeight: _exportList.height
Column {
id: _exportList
width: Math.min(_exporTiles.width, (ScreenTools.defaultFontPixelWidth * 50).toFixed(0))
spacing: ScreenTools.defaultFontPixelHeight * 0.5
anchors.horizontalCenter: parent.horizontalCenter
QGCLabel {
text: qsTr("Select Tile Sets to Export")
font.pointSize: ScreenTools.mediumFontPointSize
}
Item { width: 1; height: ScreenTools.defaultFontPixelHeight; }
Repeater {
model: QGroundControl.mapEngineManager.tileSets
delegate: QGCCheckBox {
text: object.name
checked: object.selected
onClicked: {
object.selected = checked
}
}
}
}
}
Row {
id: _exportButtonRow
visible: _exporTiles.visible
spacing: _margins
anchors.bottom: parent.bottom
anchors.margins: ScreenTools.defaultFontPixelWidth
anchors.horizontalCenter: parent.horizontalCenter
QGCButton {
text: qsTr("Select All")
width: _bigButtonSize
onClicked: QGroundControl.mapEngineManager.selectAll()
}
QGCButton {
text: qsTr("Select None")
width: _bigButtonSize
onClicked: QGroundControl.mapEngineManager.selectNone()
}
QGCButton {
text: qsTr("Export to Disk")
width: _bigButtonSize
enabled: QGroundControl.mapEngineManager.selectedCount > 0
onClicked: {
showList();
if(QGroundControl.mapEngineManager.exportSets()) {
rootLoader.sourceComponent = exportToDiskProgress
}
}
}
QGCButton {
text: qsTr("Export to Device")
width: _bigButtonSize
enabled: QGroundControl.mapEngineManager.selectedCount > 0
onClicked: {
rootLoader.sourceComponent = exportToDevice
}
}
QGCButton {
text: qsTr("Cancel")
width: _bigButtonSize
onClicked: showList()
}
}
} // QGCViewPanel
Component {
id: exportToDiskProgress
Rectangle {
width: mainWindow.width
height: mainWindow.height
color: "black"
anchors.centerIn: parent
Rectangle {
width: parent.width * 0.5
height: exportCol.height * 1.25
radius: ScreenTools.defaultFontPixelWidth
color: qgcPal.windowShadeDark
border.color: qgcPal.text
anchors.centerIn: parent
Column {
id: exportCol
spacing: ScreenTools.defaultFontPixelHeight
width: parent.width
anchors.centerIn: parent
QGCLabel {
text: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionExporting ? qsTr("Tile Set Export Progress") : qsTr("Tile Set Export Completed")
font.family: ScreenTools.demiboldFontFamily
font.pointSize: ScreenTools.mediumFontPointSize
anchors.horizontalCenter: parent.horizontalCenter
}
ProgressBar {
id: progressBar
width: parent.width * 0.45
maximumValue: 100
value: QGroundControl.mapEngineManager.actionProgress
anchors.horizontalCenter: parent.horizontalCenter
}
BusyIndicator {
visible: QGroundControl.mapEngineManager.exporting
running: QGroundControl.mapEngineManager.exporting
width: exportCloseButton.height
height: exportCloseButton.height
anchors.horizontalCenter: parent.horizontalCenter
}
QGCButton {
id: exportCloseButton
text: qsTr("Close")
width: _buttonSize
visible: !QGroundControl.mapEngineManager.exporting
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
rootLoader.sourceComponent = null
}
}
}
}
}
}
Component {
id: importDialog
Rectangle {
width: mainWindow.width
height: mainWindow.height
color: "black"
anchors.centerIn: parent
Rectangle {
width: parent.width * 0.45
height: importCol.height * 1.5
radius: ScreenTools.defaultFontPixelWidth
color: qgcPal.windowShadeDark
border.color: qgcPal.text
anchors.centerIn: parent
Column {
id: importCol
spacing: ScreenTools.defaultFontPixelHeight
width: parent.width
anchors.centerIn: parent
QGCLabel {
text: {
if(QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionNone) {
return qsTr("Map Tile Set Import");
} else if(QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionImporting) {
return qsTr("Map Tile Set Import Progress");
} else {
return qsTr("Map Tile Set Import Completed");
}
}
font.family: ScreenTools.demiboldFontFamily
font.pointSize: ScreenTools.mediumFontPointSize
anchors.horizontalCenter: parent.horizontalCenter
}
ProgressBar {
id: progressBar
width: parent.width * 0.45
maximumValue: 100
visible: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionImporting
value: QGroundControl.mapEngineManager.actionProgress
anchors.horizontalCenter: parent.horizontalCenter
}
BusyIndicator {
visible: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionImporting
running: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionImporting
width: ScreenTools.defaultFontPixelWidth * 2
height: width
anchors.horizontalCenter: parent.horizontalCenter
}
ExclusiveGroup { id: radioGroup }
Column {
spacing: ScreenTools.defaultFontPixelHeight
width: ScreenTools.defaultFontPixelWidth * 24
anchors.horizontalCenter: parent.horizontalCenter
QGCRadioButton {
exclusiveGroup: radioGroup
text: qsTr("Append to existing set")
checked: !QGroundControl.mapEngineManager.importReplace
onClicked: QGroundControl.mapEngineManager.importReplace = !checked
visible: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionNone
}
QGCRadioButton {
exclusiveGroup: radioGroup
text: qsTr("Replace existing set")
checked: QGroundControl.mapEngineManager.importReplace
onClicked: QGroundControl.mapEngineManager.importReplace = checked
visible: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionNone
}
}
QGCButton {
text: qsTr("Close")
width: _bigButtonSize * 1.25
visible: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionDone
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
showList();
rootLoader.sourceComponent = null
}
}
Row {
spacing: _margins
visible: QGroundControl.mapEngineManager.importAction === QGCMapEngineManager.ActionNone
anchors.horizontalCenter: parent.horizontalCenter
QGCButton {
text: qsTr("Import From Disk")
width: _bigButtonSize * 1.25
onClicked: {
if(!QGroundControl.mapEngineManager.importSets()) {
showList();
rootLoader.sourceComponent = null
}
}
}
QGCButton {
text: qsTr("Import From Device")
width: _bigButtonSize * 1.25
onClicked: {
rootLoader.sourceComponent = importFromDevice
}
}
QGCButton {
text: qsTr("Cancel")
width: _bigButtonSize * 1.25
onClicked: {
showList();
rootLoader.sourceComponent = null
}
}
}
}
}
}
}
Component {
id: importFromDevice
Rectangle {
width: mainWindow.width
height: mainWindow.height
color: "black"
anchors.centerIn: parent
Rectangle {
width: parent.width * 0.45
height: importCol.height * 1.5
radius: ScreenTools.defaultFontPixelWidth
color: qgcPal.windowShadeDark
border.color: qgcPal.text
anchors.centerIn: parent
Column {
id: importCol
spacing: ScreenTools.defaultFontPixelHeight
width: parent.width
anchors.centerIn: parent
QGCLabel {
text: qsTr("Map Tile Set Import From Device");
font.family: ScreenTools.demiboldFontFamily
font.pointSize: ScreenTools.mediumFontPointSize
anchors.horizontalCenter: parent.horizontalCenter
}
QGCLabel {
text: qsTr("NOT YET IMPLEMENTED");
font.family: ScreenTools.demiboldFontFamily
font.pointSize: ScreenTools.mediumFontPointSize
anchors.horizontalCenter: parent.horizontalCenter
}
QGCButton {
text: qsTr("Close")
width: _bigButtonSize * 1.25
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
showList();
rootLoader.sourceComponent = null
}
}
}
}
}
}
Component {
id: exportToDevice
Rectangle {
width: mainWindow.width
height: mainWindow.height
color: "black"
anchors.centerIn: parent
Rectangle {
width: parent.width * 0.45
height: importCol.height * 1.5
radius: ScreenTools.defaultFontPixelWidth
color: qgcPal.windowShadeDark
border.color: qgcPal.text
anchors.centerIn: parent
Column {
id: importCol
spacing: ScreenTools.defaultFontPixelHeight
width: parent.width
anchors.centerIn: parent
QGCLabel {
text: qsTr("Map Tile Set Export To Device");
font.family: ScreenTools.demiboldFontFamily
font.pointSize: ScreenTools.mediumFontPointSize
anchors.horizontalCenter: parent.horizontalCenter
}
QGCLabel {
text: qsTr("NOT YET IMPLEMENTED");
font.family: ScreenTools.demiboldFontFamily
font.pointSize: ScreenTools.mediumFontPointSize
anchors.horizontalCenter: parent.horizontalCenter
}
QGCButton {
text: qsTr("Close")
width: _bigButtonSize * 1.25
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
showList();
rootLoader.sourceComponent = null
}
}
}
}
}
}
} // QGCView
......@@ -11,6 +11,11 @@
/// @file
/// @author Gus Grubba <mavlink@grubba.com>
#if !defined(__mobile__)
#include "QGCFileDialog.h"
#include "MainWindow.h"
#endif
#include "QGCMapEngineManager.h"
#include "QGCApplication.h"
#include "QGCMapTileSet.h"
......@@ -36,6 +41,9 @@ QGCMapEngineManager::QGCMapEngineManager(QGCApplication* app)
, _setID(UINT64_MAX)
, _freeDiskSpace(0)
, _diskSpace(0)
, _actionProgress(0)
, _importAction(ActionNone)
, _importReplace(false)
{
}
......@@ -308,6 +316,9 @@ QGCMapEngineManager::taskError(QGCMapTask::TaskType type, QString error)
case QGCMapTask::taskReset:
task = "Reset Tile Sets";
break;
case QGCMapTask::taskExport:
task = "Export Tile Sets";
break;
default:
task = "Database Error";
break;
......@@ -351,6 +362,142 @@ QGCMapEngineManager::findName(const QString& name)
return false;
}
//-----------------------------------------------------------------------------
void
QGCMapEngineManager::selectAll() {
for(int i = 0; i < _tileSets.count(); i++ ) {
QGCCachedTileSet* set = qobject_cast<QGCCachedTileSet*>(_tileSets.get(i));
Q_ASSERT(set);
set->setSelected(true);
}
}
//-----------------------------------------------------------------------------
void
QGCMapEngineManager::selectNone() {
for(int i = 0; i < _tileSets.count(); i++ ) {
QGCCachedTileSet* set = qobject_cast<QGCCachedTileSet*>(_tileSets.get(i));
Q_ASSERT(set);
set->setSelected(false);
}
}
//-----------------------------------------------------------------------------
int
QGCMapEngineManager::selectedCount() {
int count = 0;
for(int i = 0; i < _tileSets.count(); i++ ) {
QGCCachedTileSet* set = qobject_cast<QGCCachedTileSet*>(_tileSets.get(i));
Q_ASSERT(set);
if(set->selected()) {
count++;
}
}
return count;
}
//-----------------------------------------------------------------------------
bool
QGCMapEngineManager::importSets(QString path) {
_importAction = ActionNone;
emit importActionChanged();
QString dir = path;
if(dir.isEmpty()) {
#if defined(__mobile__)
//-- TODO: This has to be something fixed
dir = QDir(QDir::homePath()).filePath(QString("export_%1.db").arg(QDateTime::currentDateTime().toTime_t()));
#else
dir = QGCFileDialog::getOpenFileName(
MainWindow::instance(),
"Export Tile Set",
QDir::homePath(),
"Tile Sets (*.qgctiledb)");
#endif
}
if(!dir.isEmpty()) {
_importAction = ActionImporting;
emit importActionChanged();
QGCImportTileTask* task = new QGCImportTileTask(dir, _importReplace);
connect(task, &QGCImportTileTask::actionCompleted, this, &QGCMapEngineManager::_actionCompleted);
connect(task, &QGCImportTileTask::actionProgress, this, &QGCMapEngineManager::_actionProgressHandler);
connect(task, &QGCMapTask::error, this, &QGCMapEngineManager::taskError);
getQGCMapEngine()->addTask(task);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool
QGCMapEngineManager::exportSets(QString path) {
_importAction = ActionNone;
emit importActionChanged();
QString dir = path;
if(dir.isEmpty()) {
#if defined(__mobile__)
dir = QDir(QDir::homePath()).filePath(QString("export_%1.db").arg(QDateTime::currentDateTime().toTime_t()));
#else
dir = QGCFileDialog::getSaveFileName(
MainWindow::instance(),
"Export Tile Set",
QDir::homePath(),
"Tile Sets (*.qgctiledb)",
"qgctiledb",
true);
#endif
}
if(!dir.isEmpty()) {
QVector<QGCCachedTileSet*> sets;
for(int i = 0; i < _tileSets.count(); i++ ) {
QGCCachedTileSet* set = qobject_cast<QGCCachedTileSet*>(_tileSets.get(i));
Q_ASSERT(set);
if(set->selected()) {
sets.append(set);
}
}
if(sets.count()) {
_importAction = ActionExporting;
emit importActionChanged();
QGCExportTileTask* task = new QGCExportTileTask(sets, dir);
connect(task, &QGCExportTileTask::actionCompleted, this, &QGCMapEngineManager::_actionCompleted);
connect(task, &QGCExportTileTask::actionProgress, this, &QGCMapEngineManager::_actionProgressHandler);
connect(task, &QGCMapTask::error, this, &QGCMapEngineManager::taskError);
getQGCMapEngine()->addTask(task);
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
void
QGCMapEngineManager::_actionProgressHandler(int percentage)
{
_actionProgress = percentage;
emit actionProgressChanged();
}
//-----------------------------------------------------------------------------
void
QGCMapEngineManager::_actionCompleted()
{
ImportAction oldState = _importAction;
_importAction = ActionDone;
emit importActionChanged();
//-- If we just imported, reload it all
if(oldState == ActionImporting) {
loadTileSets();
}
}
//-----------------------------------------------------------------------------
void
QGCMapEngineManager::resetAction()
{
_importAction = ActionNone;
emit importActionChanged();
}
//-----------------------------------------------------------------------------
QString
QGCMapEngineManager::getUniqueName()
......
......@@ -29,6 +29,14 @@ public:
QGCMapEngineManager(QGCApplication* app);
~QGCMapEngineManager();
enum ImportAction {
ActionNone,
ActionImporting,
ActionExporting,
ActionDone,
};
Q_ENUMS(ImportAction)
Q_PROPERTY(int tileX0 READ tileX0 NOTIFY tileX0Changed)
Q_PROPERTY(int tileX1 READ tileX1 NOTIFY tileX1Changed)
Q_PROPERTY(int tileY0 READ tileY0 NOTIFY tileY0Changed)
......@@ -46,6 +54,12 @@ public:
//-- Disk Space in MB
Q_PROPERTY(quint32 freeDiskSpace READ freeDiskSpace NOTIFY freeDiskSpaceChanged)
Q_PROPERTY(quint32 diskSpace READ diskSpace CONSTANT)
//-- Tile set export
Q_PROPERTY(int selectedCount READ selectedCount NOTIFY selectedCountChanged)
Q_PROPERTY(int actionProgress READ actionProgress NOTIFY actionProgressChanged)
Q_PROPERTY(ImportAction importAction READ importAction NOTIFY importActionChanged)
Q_PROPERTY(bool importReplace READ importReplace WRITE setImportReplace NOTIFY importReplaceChanged)
Q_INVOKABLE void loadTileSets ();
Q_INVOKABLE void updateForCurrentView (double lon0, double lat0, double lon1, double lat1, int minZoom, int maxZoom, const QString& mapName);
......@@ -55,6 +69,11 @@ public:
Q_INVOKABLE void deleteTileSet (QGCCachedTileSet* tileSet);
Q_INVOKABLE QString getUniqueName ();
Q_INVOKABLE bool findName (const QString& name);
Q_INVOKABLE void selectAll ();
Q_INVOKABLE void selectNone ();
Q_INVOKABLE bool exportSets (QString path = QString());
Q_INVOKABLE bool importSets (QString path = QString());
Q_INVOKABLE void resetAction ();
int tileX0 () { return _totalSet.tileX0; }
int tileX1 () { return _totalSet.tileX1; }
......@@ -72,10 +91,15 @@ public:
QString errorMessage () { return _errorMessage; }
quint64 freeDiskSpace () { return _freeDiskSpace; }
quint64 diskSpace () { return _diskSpace; }
int selectedCount ();
int actionProgress () { return _actionProgress; }
ImportAction importAction () { return _importAction; }
bool importReplace () { return _importReplace; }
void setMapboxToken (QString token);
void setMaxMemCache (quint32 size);
void setMaxDiskCache (quint32 size);
void setImportReplace (bool replace) { _importReplace = replace; emit importReplaceChanged(); }
void setErrorMessage (const QString& error) { _errorMessage = error; emit errorMessageChanged(); }
......@@ -95,6 +119,10 @@ signals:
void maxDiskCacheChanged ();
void errorMessageChanged ();
void freeDiskSpaceChanged ();
void selectedCountChanged ();
void actionProgressChanged ();
void importActionChanged ();
void importReplaceChanged ();
public slots:
void taskError (QGCMapTask::TaskType type, QString error);
......@@ -105,6 +133,8 @@ private slots:
void _tileSetDeleted (quint64 setID);
void _updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize);
void _resetCompleted ();
void _actionCompleted ();
void _actionProgressHandler (int percentage);
private:
void _updateDiskFreeSpace ();
......@@ -122,6 +152,9 @@ private:
quint32 _diskSpace;
QmlObjectListModel _tileSets;
QString _errorMessage;
int _actionProgress;
ImportAction _importAction;
bool _importReplace;
};
#endif
......@@ -232,14 +232,14 @@ Rectangle {
}
Repeater {
model: _corePlugin.settingsPages
visible: _corePlugin.options.combineSettingsAndSetup
model: _corePlugin ? _corePlugin.settingsPages : []
visible: _corePlugin && _corePlugin.options.combineSettingsAndSetup
SubMenuButton {
imageResource: modelData.icon
setupIndicator: false
exclusiveGroup: setupButtonGroup
text: modelData.title
visible: _corePlugin.options.combineSettingsAndSetup
visible: _corePlugin && _corePlugin.options.combineSettingsAndSetup
onClicked: panelLoader.setSource(modelData.url)
Layout.fillWidth: true
}
......@@ -312,7 +312,7 @@ Rectangle {
SubMenuButton {
setupIndicator: false
exclusiveGroup: setupButtonGroup
visible: QGroundControl.multiVehicleManager.parameterReadyVehicleAvailable && _corePlugin.showAdvancedUI
visible: QGroundControl.multiVehicleManager && QGroundControl.multiVehicleManager.parameterReadyVehicleAvailable && _corePlugin.showAdvancedUI
text: "Parameters"
Layout.fillWidth: true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment