diff --git a/qgcresources.qrc b/qgcresources.qrc index 66cea287df2c204088100b638f38a0b17b097140..cf696aaf9268eedbd5a589bd308b8b2b2606e88d 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -139,6 +139,7 @@ resources/buttonRight.svg resources/JoystickBezel.png resources/JoystickBezelLight.png + resources/notile.png resources/Pause.svg resources/Play.svg resources/PowerButton.svg diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index a6d86ac831c049164329cd9bb75536dad57115cf..3ca6477bba4d190621f964b405c9ba04cb80d0d7 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -203,6 +203,8 @@ INCLUDEPATH += \ src/ui/uas \ src/VehicleSetup \ src/ViewWidgets \ + src/QtLocationPlugin \ + src/QtLocationPlugin/QMLControl \ FORMS += \ src/ui/MainWindow.ui \ @@ -284,7 +286,8 @@ HEADERS += \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ src/AutoPilotPlugins/APM/APMAirframeLoader.h \ src/QmlControls/QGCImageProvider.h \ - src/AutoPilotPlugins/APM/APMRemoteParamsDownloader.h + src/AutoPilotPlugins/APM/APMRemoteParamsDownloader.h \ + src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ DebugBuild { HEADERS += \ @@ -405,7 +408,8 @@ SOURCES += \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ src/AutoPilotPlugins/APM/APMAirframeLoader.cc \ src/QmlControls/QGCImageProvider.cc \ - src/AutoPilotPlugins/APM/APMRemoteParamsDownloader.cc + src/AutoPilotPlugins/APM/APMRemoteParamsDownloader.cc \ + src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ DebugBuild { SOURCES += \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index e9247708a569974497d6ace58a1d5e0896c36b93..fb1ca4772296cdeaaa876c231a6eebcd7c6eb886 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -27,6 +27,7 @@ src/ui/preferences/MavlinkSettings.qml src/ui/preferences/MockLink.qml src/ui/preferences/MockLinkSettings.qml + src/QtLocationPlugin/QMLControl/OfflineMap.qml src/ui/preferences/SerialSettings.qml src/ui/preferences/TcpSettings.qml src/ui/preferences/UdpSettings.qml @@ -139,6 +140,7 @@ src/VehicleSetup/SetupView.qml src/test.qml src/VehicleSetup/VehicleSummary.qml + src/QmlControls/OfflineMapButton.qml src/MissionManager/MavCmdInfoCommon.json diff --git a/resources/notile.png b/resources/notile.png new file mode 100644 index 0000000000000000000000000000000000000000..cbd241ee61906d0e5ccba100d3410c629a668fc5 Binary files /dev/null and b/resources/notile.png differ diff --git a/src/FlightMap/FlightMapSettings.cc b/src/FlightMap/FlightMapSettings.cc index c963721d2510390ef2c5466a8475a1292a1e1a60..63c34e59c160b697ee8549ecd1e16a2fe8197b27 100644 --- a/src/FlightMap/FlightMapSettings.cc +++ b/src/FlightMap/FlightMapSettings.cc @@ -43,7 +43,7 @@ void FlightMapSettings::setToolbox(QGCToolbox *toolbox) qmlRegisterUncreatableType ("QGroundControl", 1, 0, "FlightMapSetting", "Reference only"); - _supportedMapProviders << "Bing" << "Google" << "OpenStreetMap"; + _supportedMapProviders << "Bing" << "Google"; // << "OpenStreetMap"; _loadSettings(); } @@ -93,8 +93,10 @@ void FlightMapSettings::_setMapTypesForCurrentProvider(void) _mapTypes << "Street Map" << "Satellite Map" << "Hybrid Map"; } else if (_mapProvider == "Google") { _mapTypes << "Street Map" << "Satellite Map" << "Terrain Map"; + /* } else if (_mapProvider == "OpenStreetMap") { _mapTypes << "Street Map"; + */ } emit mapTypesChanged(_mapTypes); diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index c99f2e1b2edda333454e803fd2ee9f9a11b6f7ab..fa07e45390253bce5f607dda35b682b189e58b84 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -34,6 +34,7 @@ #include "MultiVehicleManager.h" #include "QGCImageProvider.h" #include "UASMessageHandler.h" +#include "QGCMapEngineManager.h" QGCToolbox::QGCToolbox(QGCApplication* app) : _audioOutput(NULL) @@ -48,6 +49,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) , _mavlinkProtocol(NULL) , _missionCommands(NULL) , _multiVehicleManager(NULL) + , _mapEngineManager(NULL) , _uasMessageHandler(NULL) { _audioOutput = new GAudioOutput(app); @@ -62,6 +64,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) _mavlinkProtocol = new MAVLinkProtocol(app); _missionCommands = new MissionCommands(app); _multiVehicleManager = new MultiVehicleManager(app); + _mapEngineManager = new QGCMapEngineManager(app); _uasMessageHandler = new UASMessageHandler(app); _audioOutput->setToolbox(this); @@ -76,6 +79,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) _mavlinkProtocol->setToolbox(this); _missionCommands->setToolbox(this); _multiVehicleManager->setToolbox(this); + _mapEngineManager->setToolbox(this); _uasMessageHandler->setToolbox(this); } @@ -91,6 +95,7 @@ QGCToolbox::~QGCToolbox() delete _linkManager; delete _mavlinkProtocol; delete _missionCommands; + delete _mapEngineManager; delete _multiVehicleManager; delete _uasMessageHandler; } diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index d420067303ef12e093c79e63c9165fdf6166b246..348a0b7b0c87c8fd828093662891d53d961f20c7 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -37,6 +37,7 @@ class LinkManager; class MAVLinkProtocol; class MissionCommands; class MultiVehicleManager; +class QGCMapEngineManager; class QGCApplication; class QGCImageProvider; class UASMessageHandler; @@ -58,6 +59,7 @@ public: MAVLinkProtocol* mavlinkProtocol(void) { return _mavlinkProtocol; } MissionCommands* missionCommands(void) { return _missionCommands; } MultiVehicleManager* multiVehicleManager(void) { return _multiVehicleManager; } + QGCMapEngineManager* mapEngineManager(void) { return _mapEngineManager; } QGCImageProvider* imageProvider() { return _imageProvider; } UASMessageHandler* uasMessageHandler(void) { return _uasMessageHandler; } @@ -74,6 +76,7 @@ private: MAVLinkProtocol* _mavlinkProtocol; MissionCommands* _missionCommands; MultiVehicleManager* _multiVehicleManager; + QGCMapEngineManager* _mapEngineManager; UASMessageHandler* _uasMessageHandler; }; diff --git a/src/QmlControls/OfflineMapButton.qml b/src/QmlControls/OfflineMapButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..ca819323bd78bef74c3ea925fd58bf265778d065 --- /dev/null +++ b/src/QmlControls/OfflineMapButton.qml @@ -0,0 +1,103 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.2 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Rectangle +{ + id: __mapButton + + property var __qgcPal: QGCPalette { colorGroupEnabled: enabled } + property bool __showHighlight: (__pressed | __hovered | checked) && !__forceHoverOff + + property bool __forceHoverOff: false + property int __lastGlobalMouseX: 0 + property int __lastGlobalMouseY: 0 + property bool __pressed: false + property bool __hovered: false + + property bool checked: false + property bool complete: false + property alias text: nameLabel.text + property alias size: sizeLabel.text + + signal clicked() + + color: __showHighlight ? __qgcPal.buttonHighlight : __qgcPal.button + anchors.margins: ScreenTools.defaultFontPixelWidth + Row { + anchors.centerIn: parent + QGCLabel { + id: nameLabel + width: __mapButton.width * 0.4 + color: __showHighlight ? __qgcPal.buttonHighlightText : __qgcPal.buttonText + anchors.verticalCenter: parent.verticalCenter + } + QGCLabel { + id: sizeLabel + width: __mapButton.width * 0.4 + horizontalAlignment: Text.AlignRight + anchors.verticalCenter: parent.verticalCenter + color: __showHighlight ? __qgcPal.buttonHighlightText : __qgcPal.buttonText + } + Item { + width: ScreenTools.defaultFontPixelWidth * 2 + height: 1 + } + Rectangle { + width: sizeLabel.height * 0.5 + height: sizeLabel.height * 0.5 + radius: width / 2 + color: complete ? "#31f55b" : "#fc5656" + opacity: sizeLabel.text.length > 0 ? 1 : 0 + anchors.verticalCenter: parent.verticalCenter + } + Item { + width: ScreenTools.defaultFontPixelWidth * 2 + height: 1 + } + QGCColoredImage { + width: sizeLabel.height * 0.8 + height: sizeLabel.height * 0.8 + source: "/res/buttonRight.svg" + mipmap: true + fillMode: Image.PreserveAspectFit + color: __showHighlight ? __qgcPal.buttonHighlightText : __qgcPal.buttonText + anchors.verticalCenter: parent.verticalCenter + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onMouseXChanged: { + __lastGlobalMouseX = ScreenTools.mouseX() + __lastGlobalMouseY = ScreenTools.mouseY() + } + onMouseYChanged: { + __lastGlobalMouseX = ScreenTools.mouseX() + __lastGlobalMouseY = ScreenTools.mouseY() + } + onEntered: { __hovered = true; __forceHoverOff = false; hoverTimer.start() } + onExited: { __hovered = false; __forceHoverOff = false; hoverTimer.stop() } + onPressed: { __pressed = true; } + onReleased: { __pressed = false; } + onClicked: { + __mapButton.clicked() + } + } + + Timer { + id: hoverTimer + interval: 250 + repeat: true + onTriggered: { + if (__lastGlobalMouseX !== ScreenTools.mouseX() || __lastGlobalMouseY !== ScreenTools.mouseY()) { + __forceHoverOff = true + } else { + __forceHoverOff = false + } + } + } +} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 8992d4123ac97a539f70f6b1a69a3c238660e28c..05526e6b6c2932f1e6380fad5c22aeb11ecc25e4 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -35,3 +35,4 @@ SubMenuButton 1.0 SubMenuButton.qml VehicleRotationCal 1.0 VehicleRotationCal.qml VehicleSummaryRow 1.0 VehicleSummaryRow.qml ViewWidget 1.0 ViewWidget.qml +OfflineMapButton 1.0 OfflineMapButton.qml diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index f932e5c4417d439558a83c8e01e1993589bf0492..4f91a12be9d6d7a561b2fe493a6985730323c261 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -39,6 +39,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) , _linkManager(NULL) , _missionCommands(NULL) , _multiVehicleManager(NULL) + , _mapEngineManager(NULL) , _virtualTabletJoystick(false) , _offlineEditingFirmwareTypeFact(QString(), "OfflineEditingFirmwareType", FactMetaData::valueTypeUint32, (uint32_t)MAV_AUTOPILOT_ARDUPILOTMEGA) , _offlineEditingFirmwareTypeMetaData(FactMetaData::valueTypeUint32) @@ -65,12 +66,12 @@ QGroundControlQmlGlobal::~QGroundControlQmlGlobal() void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); - - _flightMapSettings = toolbox->flightMapSettings(); - _homePositionManager = toolbox->homePositionManager(); - _linkManager = toolbox->linkManager(); - _missionCommands = toolbox->missionCommands(); - _multiVehicleManager = toolbox->multiVehicleManager(); + _flightMapSettings = toolbox->flightMapSettings(); + _homePositionManager = toolbox->homePositionManager(); + _linkManager = toolbox->linkManager(); + _missionCommands = toolbox->missionCommands(); + _multiVehicleManager = toolbox->multiVehicleManager(); + _mapEngineManager = toolbox->mapEngineManager(); } diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 82c7e46274c8821a0f38d4d97ba61b0a787cb59d..4db3e072ba78f9089427773fe35468e2aa558f85 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -55,6 +55,7 @@ public: Q_PROPERTY(LinkManager* linkManager READ linkManager CONSTANT) Q_PROPERTY(MissionCommands* missionCommands READ missionCommands CONSTANT) Q_PROPERTY(MultiVehicleManager* multiVehicleManager READ multiVehicleManager CONSTANT) + Q_PROPERTY(QGCMapEngineManager* mapEngineManager READ mapEngineManager CONSTANT) Q_PROPERTY(qreal zOrderTopMost READ zOrderTopMost CONSTANT) ///< z order for top most items, toolbar, main window sub view Q_PROPERTY(qreal zOrderWidgets READ zOrderWidgets CONSTANT) ///< z order value to widgets, for example: zoom controls, hud widgetss @@ -95,9 +96,10 @@ public: FlightMapSettings* flightMapSettings () { return _flightMapSettings; } HomePositionManager* homePositionManager () { return _homePositionManager; } - LinkManager* linkManager () { return _linkManager; } - MissionCommands* missionCommands () { return _missionCommands; } + LinkManager* linkManager () { return _linkManager; } + MissionCommands* missionCommands () { return _missionCommands; } MultiVehicleManager* multiVehicleManager () { return _multiVehicleManager; } + QGCMapEngineManager* mapEngineManager () { return _mapEngineManager; } qreal zOrderTopMost () { return 1000; } qreal zOrderWidgets () { return 100; } @@ -149,6 +151,7 @@ private: LinkManager* _linkManager; MissionCommands* _missionCommands; MultiVehicleManager* _multiVehicleManager; + QGCMapEngineManager* _mapEngineManager; bool _virtualTabletJoystick; diff --git a/src/QtLocationPlugin/OpenPilotMaps.cc b/src/QtLocationPlugin/OpenPilotMaps.cc deleted file mode 100644 index 8816d85269f7195b0acd8a62921dcac6fff9e546..0000000000000000000000000000000000000000 --- a/src/QtLocationPlugin/OpenPilotMaps.cc +++ /dev/null @@ -1,511 +0,0 @@ -/*===================================================================== - -QGroundControl Open Source Ground Control Station - -(c) 2009, 2015 QGROUNDCONTROL PROJECT - -This file is part of the QGROUNDCONTROL project - - QGROUNDCONTROL is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - QGROUNDCONTROL is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with QGROUNDCONTROL. If not, see . - -======================================================================*/ - -/** - * @file - * @brief QGC Open Pilot Mapping Tools - * @author Gus Grubba - * Original work: The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. - */ - -#include -#include -#include -#include - -#include "OpenPilotMaps.h" - -namespace OpenPilot { - -const QString ProviderStrings::kLevelsForSigPacSpainMap[] = - { "0", "1", "2", "3", "4", - "MTNSIGPAC", - "MTN2000", "MTN2000", "MTN2000", "MTN2000", "MTN2000", - "MTN200", "MTN200", "MTN200", - "MTN25", "MTN25", - "ORTOFOTOS", "ORTOFOTOS", "ORTOFOTOS", "ORTOFOTOS" }; - -ProviderStrings::ProviderStrings() -{ - // Google version strings - VersionGoogleMap = "m@313"; - VersionGoogleSatellite = "s@177"; - VersionGoogleLabels = "h@313"; - VersionGoogleTerrain = "t@132,r@313"; - SecGoogleWord = "Galileo"; - - // Google (China) version strings - VersionGoogleMapChina = "m@132"; - VersionGoogleSatelliteChina = "s@71"; - VersionGoogleLabelsChina = "h@132"; - VersionGoogleTerrainChina = "t@125,r@132"; - - // Google (Korea) version strings - VersionGoogleMapKorea = "kr1.12"; - VersionGoogleSatelliteKorea = "66"; - VersionGoogleLabelsKorea = "kr1t.12"; - - /// - /// Google Maps API generated using http://greatmaps.codeplex.com/ - /// from http://code.google.com/intl/en-us/apis/maps/signup.html - /// - GoogleMapsAPIKey = "ABQIAAAAWaQgWiEBF3lW97ifKnAczhRAzBk5Igf8Z5n2W3hNnMT0j2TikxTLtVIGU7hCLLHMAuAMt-BO5UrEWA"; - - // Yahoo version strings - VersionYahooMap = "4.3"; - VersionYahooSatellite = "1.9"; - VersionYahooLabels = "4.3"; - - // BingMaps - VersionBingMaps = "563"; - - // YandexMap - VersionYandexMap = "2.16.0"; - - /// - /// Bing Maps Customer Identification, more info here - /// http://msdn.microsoft.com/en-us/library/bb924353.aspx - /// - BingMapsClientToken = ""; -} - -UrlFactory::UrlFactory(QNetworkAccessManager *network) - : _timeout(5 * 1000) - , _googleVersionRetrieved(false) - , _network(network) - , _googleReply(NULL) -{ -} - -UrlFactory::~UrlFactory() -{ - if(_googleReply) - _googleReply->deleteLater(); -} - -QString UrlFactory::_tileXYToQuadKey(const int& tileX, const int& tileY, const int& levelOfDetail) const -{ - QString quadKey; - for (int i = levelOfDetail; i > 0; i--) { - char digit = '0'; - int mask = 1 << (i - 1); - if ((tileX & mask) != 0) { - digit++; - } - if ((tileY & mask) != 0) { - digit++; - digit++; - } - quadKey.append(digit); - } - return quadKey; -} - -int UrlFactory::_getServerNum(const QPoint &pos, const int &max) const -{ - return (pos.x() + 2 * pos.y()) % max; -} - -void UrlFactory::_networkReplyError(QNetworkReply::NetworkError error) -{ - qWarning() << "Could not connect to google maps. Error:" << error; - if(_googleReply) - { - _googleReply->deleteLater(); - _googleReply = NULL; - } -} - -void UrlFactory::_replyDestroyed() -{ - _googleReply = NULL; -} - -void UrlFactory::_googleVersionCompleted() -{ - if (!_googleReply || (_googleReply->error() != QNetworkReply::NoError)) { - qDebug() << "Error collecting Google maps version info"; - return; - } - QString html = QString(_googleReply->readAll()); - QRegExp reg("\"*http://mts0.google.com/vt/lyrs=m@(\\d*)", Qt::CaseInsensitive); - if (reg.indexIn(html) != -1) { - QStringList gc = reg.capturedTexts(); - VersionGoogleMap = QString("m@%1").arg(gc[1]); - VersionGoogleMapChina = VersionGoogleMap; - VersionGoogleMapKorea = VersionGoogleMap; - } - reg = QRegExp("\"*http://mts0.google.com/vt/lyrs=h@(\\d*)", Qt::CaseInsensitive); - if (reg.indexIn(html) != -1) { - QStringList gc = reg.capturedTexts(); - VersionGoogleLabels = QString("h@%1").arg(gc[1]); - VersionGoogleLabelsChina = VersionGoogleLabels; - VersionGoogleLabelsKorea = VersionGoogleLabels; - } - reg = QRegExp("\"*http://khms0.google.com/kh/v=(\\d*)", Qt::CaseInsensitive); - if (reg.indexIn(html) != -1) { - QStringList gc = reg.capturedTexts(); - VersionGoogleSatellite = "s@" + gc[1]; - VersionGoogleSatelliteKorea = VersionGoogleSatellite; - VersionGoogleSatelliteChina = VersionGoogleSatellite; - } - reg = QRegExp("\"*http://mts0.google.com/vt/lyrs=t@(\\d*),r@(\\d*)", Qt::CaseInsensitive); - if (reg.indexIn(html) != -1) { - QStringList gc = reg.capturedTexts(); - VersionGoogleTerrain = QString("t@%1,r@%2").arg(gc[1]).arg(gc[2]); - VersionGoogleTerrainChina = VersionGoogleTerrain; - VersionGoogleTerrainChina = VersionGoogleTerrain; - } - _googleReply->deleteLater(); - _googleReply = NULL; -} - -void UrlFactory::_tryCorrectGoogleVersions() -{ - QMutexLocker locker(&_googleVersionMutex); - if (_googleVersionRetrieved) { - return; - } - _googleVersionRetrieved = true; - if(_network) - { - QNetworkRequest qheader; - QNetworkProxy proxy = _network->proxy(); - QNetworkProxy tProxy; - tProxy.setType(QNetworkProxy::NoProxy); - _network->setProxy(tProxy); - QString url = "http://maps.google.com/maps?output=classic"; - qheader.setUrl(QUrl(url)); -#if defined Q_OS_MAC - QByteArray userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0"; -#elif defined Q_OS_WIN32 - QByteArray userAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7"; -#else - QByteArray userAgent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20130331 Firefox/21.0"; -#endif - qheader.setRawHeader("User-Agent", userAgent); - _googleReply = _network->get(qheader); - connect(_googleReply, &QNetworkReply::finished, this, &UrlFactory::_googleVersionCompleted); - connect(_googleReply, &QNetworkReply::destroyed, this, &UrlFactory::_replyDestroyed); - - connect(_googleReply, static_cast(&QNetworkReply::error), - this, &UrlFactory::_networkReplyError); - _network->setProxy(proxy); - } -} - -QString UrlFactory::makeImageUrl(const MapType &type, const QPoint& pos, const int &zoom, const QString &language) -{ - switch (type) { - case GoogleMap: - { - // http://mt1.google.com/vt/lyrs=m - QString server = "mt"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10&scale=2").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleMap).arg(language).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleSatellite: - { - // http://mt1.google.com/vt/lyrs=s - QString server = "mt"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10&scale=2").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleSatellite).arg(language).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleLabels: - { - QString server = "mts"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleLabels).arg(language).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleTerrain: - { - QString server = "mts"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - return QString("http://%1%2.google.com/%3/v=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10&scale=2").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleTerrain).arg(language).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleMapChina: - { - QString server = "mt"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - // http://mt0.google.cn/vt/v=w2.101&hl=zh-CN&gl=cn&x=12&y=6&z=4&s=Ga - return QString("http://%1%2.google.cn/%3/lyrs=%4&hl=%5&gl=cn&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleMapChina).arg("zh-CN").arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleSatelliteChina: - { - QString server = "mt"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - // http://khm0.google.cn/kh/v=46&x=12&y=6&z=4&s=Ga - return QString("http://%1%2.google.cn/%3/lyrs=%4&gl=cn&x=%5%6&y=%7&z=%8&s=%9").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleSatelliteChina).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleLabelsChina: - { - QString server = "mt"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - // http://mt0.google.cn/vt/v=w2t.110&hl=zh-CN&gl=cn&x=12&y=6&z=4&s=Ga - return QString("http://%1%2.google.cn/%3/imgtp=png32&lyrs=%4&hl=%5&gl=cn&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleLabelsChina).arg("zh-CN").arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleTerrainChina: - { - QString server = "mt"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - // http://mt0.google.cn/vt/v=w2p.110&hl=zh-CN&gl=cn&x=12&y=6&z=4&s=Ga - return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&gl=cn&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleTerrainChina).arg("zh-CN").arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleMapKorea: - { - QString server = "mts"; - QString request = "vt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - // http://mts0.google.com/vt/lyrs=m@224000000&hl=ko&gl=KR&src=app&x=107&y=50&z=7&s=Gal - // http://mts0.google.com/mt/v=kr1.11&hl=ko&x=109&y=49&z=7&s= - QString ret = QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleMapKorea).arg(language).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - return ret; - } - break; - case GoogleSatelliteKorea: - { - QString server = "khms"; - QString request = "kh"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - // http://khm1.google.co.kr/kh/v=54&x=109&y=49&z=7&s= - return QString("http://%1%2.google.co.kr/%3/v=%4&x=%5%6&y=%7&z=%8&s=%9").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleSatelliteKorea).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case GoogleLabelsKorea: - { - QString server = "mts"; - QString request = "mt"; - QString sec1 = ""; // after &x=... - QString sec2 = ""; // after &zoom=... - _getSecGoogleWords(pos, sec1, sec2); - _tryCorrectGoogleVersions(); - // http://mts1.gmaptiles.co.kr/mt/v=kr1t.11&hl=lt&x=109&y=50&z=7&s=G - return QString("http://%1%2.gmaptiles.co.kr/%3/v=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(pos, 4)).arg(request).arg(VersionGoogleLabelsKorea).arg(language).arg(pos.x()).arg(sec1).arg(pos.y()).arg(zoom).arg(sec2); - } - break; - case YahooMap: - { - return QString("http://maps%1.yimg.com/hx/tl?v=%2&.intl=%3&x=%4&y=%5&z=%6&r=1").arg(((_getServerNum(pos, 2)) + 1)).arg(VersionYahooMap).arg(language).arg(pos.x()).arg((((1 << zoom) >> 1) - 1 - pos.y())).arg((zoom + 1)); - } - case YahooSatellite: - { - return QString("http://maps%1.yimg.com/ae/ximg?v=%2&t=a&s=256&.intl=%3&x=%4&y=%5&z=%6&r=1").arg("3").arg(VersionYahooSatellite).arg(language).arg(pos.x()).arg(((1 << zoom) >> 1) - 1 - pos.y()).arg(zoom + 1); - } - break; - case YahooLabels: - { - return QString("http://maps%1.yimg.com/hx/tl?v=%2&t=h&.intl=%3&x=%4&y=%5&z=%6&r=1").arg("1").arg(VersionYahooLabels).arg(language).arg(pos.x()).arg(((1 << zoom) >> 1) - 1 - pos.y()).arg(zoom + 1); - } - break; - case OpenStreetMap: - { - char letter = "abc"[_getServerNum(pos, 3)]; - return QString("http://%1.tile.openstreetmap.org/%2/%3/%4.png").arg(letter).arg(zoom).arg(pos.x()).arg(pos.y()); - } - break; - case OpenStreetOsm: - { - char letter = "abc"[_getServerNum(pos, 3)]; - return QString("http://%1.tah.openstreetmap.org/Tiles/tile/%2/%3/%4.png").arg(letter).arg(zoom).arg(pos.x()).arg(pos.y()); - } - break; - case OpenStreetMapSurfer: - { - // http://tiles1.mapsurfer.net/tms_r.ashx?x=37378&y=20826&z=16 - return QString("http://tiles1.mapsurfer.net/tms_r.ashx?x=%1&y=%2&z=%3").arg(pos.x()).arg(pos.y()).arg(zoom); - } - break; - case OpenStreetMapSurferTerrain: - { - // http://tiles2.mapsurfer.net/tms_t.ashx?x=9346&y=5209&z=14 - return QString("http://tiles2.mapsurfer.net/tms_t.ashx?x=%1&y=%2&z=%3").arg(pos.x()).arg(pos.y()).arg(zoom); - } - break; - case BingMap: - { - QString key = _tileXYToQuadKey(pos.x(), pos.y(), zoom); - return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/r%2.png?g=%3&mkt=%4%5").arg(_getServerNum(pos, 4)).arg(key).arg(VersionBingMaps).arg(language).arg(!(BingMapsClientToken.isNull() | BingMapsClientToken.isEmpty()) ? "&token=" + BingMapsClientToken : QString("")); - } - break; - case BingSatellite: - { - QString key = _tileXYToQuadKey(pos.x(), pos.y(), zoom); - return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/a%2.jpeg?g=%3&mkt=%4%5").arg(_getServerNum(pos, 4)).arg(key).arg(VersionBingMaps).arg(language).arg(!(BingMapsClientToken.isNull() | BingMapsClientToken.isEmpty()) ? "&token=" + BingMapsClientToken : QString("")); - } - break; - case BingHybrid: - { - QString key = _tileXYToQuadKey(pos.x(), pos.y(), zoom); - return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/h%2.jpeg?g=%3&mkt=%4%5").arg(_getServerNum(pos, 4)).arg(key).arg(VersionBingMaps).arg(language).arg(!(BingMapsClientToken.isNull() | BingMapsClientToken.isEmpty()) ? "&token=" + BingMapsClientToken : QString("")); - } - case ArcGIS_Map: - { - // http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer/tile/0/0/0.jpg - return QString("http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer/tile/%1/%2/%3").arg(zoom).arg(pos.y()).arg(pos.x()); - } - break; - case ArcGIS_Satellite: - { - // http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer/tile/1/0/1.jpg - return QString("http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer/tile/%1/%2/%3").arg(zoom).arg(pos.y()).arg(pos.x()); - } - break; - case ArcGIS_ShadedRelief: - { - // http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_ShadedRelief_World_2D/MapServer/tile/1/0/1.jpg - return QString("http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_ShadedRelief_World_2D/MapServer/tile/%1/%2/%3").arg(zoom).arg(pos.y()).arg(pos.x()); - } - break; - case ArcGIS_Terrain: - { - // http://server.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer/tile/4/3/15 - return QString("http://server.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer/tile/%1/%2/%3").arg(zoom).arg(pos.y()).arg(pos.x()); - } - break; - case ArcGIS_MapsLT_OrtoFoto: - { - // http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L02/R0000001b/C00000028.jpg - // http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto/MapServer/tile/0/9/13 - // return string.Format("http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.jpg", zoom, pos.y(), pos.x()); - // http://dc1.maps.lt/cache/mapslt_ortofoto_512/map/_alllayers/L03/R0000001c/C00000029.jpg - // return string.Format("http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto/MapServer/tile/{0}/{1}/{2}", zoom, pos.y(), pos.x()); - // http://dc1.maps.lt/cache/mapslt_ortofoto_512/map/_alllayers/L03/R0000001d/C0000002a.jpg - // TODO verificar - return QString("http://dc1.maps.lt/cache/mapslt_ortofoto/map/_alllayers/L%1/R%2/C%3.jpg").arg(zoom, 2, 10, (QChar)'0').arg(pos.y(), 8, 16, (QChar)'0').arg(pos.x(), 8, 16, (QChar)'0'); - } - break; - case ArcGIS_MapsLT_Map: - { - // http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L02/R0000001b/C00000028.jpg - // http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto/MapServer/tile/0/9/13 - // return string.Format("http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.jpg", zoom, pos.y(), pos.x()); - // http://arcgis.maps.lt/ArcGIS/rest/services/mapslt/MapServer/tile/7/1162/1684.png - // http://dc1.maps.lt/cache/mapslt_512/map/_alllayers/L03/R0000001b/C00000029.png - // TODO verificar - // http://dc1.maps.lt/cache/mapslt/map/_alllayers/L02/R0000001c/C00000029.png - return QString("http://dc1.maps.lt/cache/mapslt/map/_alllayers/L%1/R%2/C%3.png").arg(zoom, 2, 10, (QChar)'0').arg(pos.y(), 8, 16, (QChar)'0').arg(pos.x(), 8, 16, (QChar)'0'); - } - break; - case ArcGIS_MapsLT_Map_Labels: - { - // http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto_overlay/MapServer/tile/0/9/13 - // return string.Format("http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto_overlay/MapServer/tile/{0}/{1}/{2}", zoom, pos.y(), pos.x()); - // http://dc1.maps.lt/cache/mapslt_ortofoto_overlay_512/map/_alllayers/L03/R0000001d/C00000029.png - // TODO verificar - return QString("http://dc1.maps.lt/cache/mapslt_ortofoto_overlay/map/_alllayers/L%1/R%2/C%3.png").arg(zoom, 2, 10, (QChar)'0').arg(pos.y(), 8, 16, (QChar)'0').arg(pos.x(), 8, 16, (QChar)'0'); - } - break; - case PergoTurkeyMap: - { - // http://{domain}/{layerName}/{zoomLevel}/{first3LetterOfTileX}/{second3LetterOfTileX}/{third3LetterOfTileX}/{first3LetterOfTileY}/{second3LetterOfTileY}/{third3LetterOfTileXY}.png - // http://map3.pergo.com.tr/tile/00/000/000/001/000/000/000.png - // That means: Zoom Level: 0 TileX: 1 TileY: 0 - // http://domain/tile/14/000/019/371/000/011/825.png - // That means: Zoom Level: 14 TileX: 19371 TileY:11825 - // string x = pos.x().ToString("000000000").Insert(3, "/").Insert(7, "/"); // - 000/000/001 - // string y = pos.y().ToString("000000000").Insert(3, "/").Insert(7, "/"); // - 000/000/000 - QString x = QString("%1").arg(QString::number(pos.x()), 9, (QChar)'0'); - x.insert(3, "/").insert(7, "/"); - QString y = QString("%1").arg(QString::number(pos.y()), 9, (QChar)'0'); - y.insert(3, "/").insert(7, "/"); - // "http://map03.pergo.com.tr/tile/2/000/000/003/000/000/002.png" - return QString("http://map%1.pergo.com.tr/tile/%2/%3/%4.png").arg(_getServerNum(pos, 4)).arg(zoom, 2, 10, (QChar)'0').arg(x).arg(y); - } - break; - case SigPacSpainMap: - { - return QString("http://sigpac.mapa.es/kmlserver/raster/%1@3785/%2.%3.%4.img").arg(kLevelsForSigPacSpainMap[zoom]).arg(zoom).arg(pos.x()).arg((2 << (zoom - 1)) - pos.y() - 1); - } - break; - case YandexMapRu: - { - QString server = "vec"; - // http://vec01.maps.yandex.ru/tiles?l=map&v=2.10.2&x=1494&y=650&z=11 - return QString("http://%1").arg(server) + QString("0%2.maps.yandex.ru/tiles?l=map&v=%3&x=%4&y=%5&z=%6").arg(_getServerNum(pos, 4) + 1).arg(VersionYandexMap).arg(pos.x()).arg(pos.y()).arg(zoom); - } - break; - default: - qWarning("Unknown map id %d\n", type); - break; - } - return QString::null; -} - -void UrlFactory::_getSecGoogleWords(const QPoint &pos, QString &sec1, QString &sec2) -{ - sec1 = ""; // after &x=... - sec2 = ""; // after &zoom=... - int seclen = ((pos.x() * 3) + pos.y()) % 8; - sec2 = SecGoogleWord.left(seclen); - if (pos.y() >= 10000 && pos.y() < 100000) { - sec1 = "&s="; - } -} - -} diff --git a/src/QtLocationPlugin/OpenPilotMaps.h b/src/QtLocationPlugin/OpenPilotMaps.h deleted file mode 100644 index a8fc609ded8214c5c5fc3b0acda5bb61fab851bf..0000000000000000000000000000000000000000 --- a/src/QtLocationPlugin/OpenPilotMaps.h +++ /dev/null @@ -1,164 +0,0 @@ -/*===================================================================== - -QGroundControl Open Source Ground Control Station - -(c) 2009, 2015 QGROUNDCONTROL PROJECT - -This file is part of the QGROUNDCONTROL project - - QGROUNDCONTROL is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - QGROUNDCONTROL is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with QGROUNDCONTROL. If not, see . - -======================================================================*/ - -/** - * @file - * @brief QGC Open Pilot Mapping Tools - * @author Gus Grubba - * Original work: The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. - */ - -#ifndef OPENPILOTTOOLS_H -#define OPENPILOTTOOLS_H - -#include -#include -#include -#include -#include -#include - -#define MAX_MAP_ZOOM (20.0) - -namespace OpenPilot { - -enum MapType -{ - GoogleMap = 1, - GoogleSatellite = 4, - GoogleLabels = 8, - GoogleTerrain = 16, - GoogleHybrid = 20, - - GoogleMapChina = 22, - GoogleSatelliteChina = 24, - GoogleLabelsChina = 26, - GoogleTerrainChina = 28, - GoogleHybridChina = 29, - - OpenStreetMap = 32, - OpenStreetOsm = 33, - OpenStreetMapSurfer = 34, - OpenStreetMapSurferTerrain=35, - - YahooMap = 64, - YahooSatellite = 128, - YahooLabels = 256, - YahooHybrid = 333, - - BingMap = 444, - BingSatellite = 555, - BingHybrid = 666, - - ArcGIS_Map = 777, - ArcGIS_Satellite = 788, - ArcGIS_ShadedRelief = 799, - ArcGIS_Terrain = 811, - - ArcGIS_MapsLT_Map = 1000, - ArcGIS_MapsLT_OrtoFoto = 1001, - ArcGIS_MapsLT_Map_Labels= 1002, - ArcGIS_MapsLT_Map_Hybrid= 1003, - - PergoTurkeyMap = 2001, - SigPacSpainMap = 3001, - - GoogleMapKorea = 4001, - GoogleSatelliteKorea = 4002, - GoogleLabelsKorea = 4003, - GoogleHybridKorea = 4005, - - YandexMapRu = 5000 -}; - -class ProviderStrings : public QObject { - Q_OBJECT -public: - ProviderStrings(); - static const QString kLevelsForSigPacSpainMap[]; - QString GoogleMapsAPIKey; - // Google version strings - QString VersionGoogleMap; - QString VersionGoogleSatellite; - QString VersionGoogleLabels; - QString VersionGoogleTerrain; - QString SecGoogleWord; - // Google (China) version strings - QString VersionGoogleMapChina; - QString VersionGoogleSatelliteChina; - QString VersionGoogleLabelsChina; - QString VersionGoogleTerrainChina; - // Google (Korea) version strings - QString VersionGoogleMapKorea; - QString VersionGoogleSatelliteKorea; - QString VersionGoogleLabelsKorea; - /// - /// Google Maps API generated using http://greatmaps.codeplex.com/ - /// from http://code.google.com/intl/en-us/apis/maps/signup.html - /// - // Yahoo version strings - QString VersionYahooMap; - QString VersionYahooSatellite; - QString VersionYahooLabels; - // BingMaps - QString VersionBingMaps; - // YandexMap - QString VersionYandexMap; - /// - /// Bing Maps Customer Identification, more info here - /// http://msdn.microsoft.com/en-us/library/bb924353.aspx - /// - QString BingMapsClientToken; -}; - -class UrlFactory : public ProviderStrings { - Q_OBJECT -public: - - UrlFactory(QNetworkAccessManager* network); - ~UrlFactory(); - - QString makeImageUrl (const MapType &type, const QPoint &pos, const int &zoom, const QString &language); - -private slots: - void _networkReplyError (QNetworkReply::NetworkError error); - void _googleVersionCompleted (); - void _replyDestroyed (); - -private: - void _getSecGoogleWords (const QPoint &pos, QString &sec1, QString &sec2); - int _getServerNum (const QPoint& pos, const int &max) const; - void _tryCorrectGoogleVersions (); - QString _tileXYToQuadKey (const int &tileX, const int &tileY, const int &levelOfDetail) const; - - int _timeout; - bool _googleVersionRetrieved; - QNetworkAccessManager* _network; - QNetworkReply* _googleReply; - QMutex _googleVersionMutex; - QByteArray _userAgent; -}; - -} - -#endif // FOO_H diff --git a/src/QtLocationPlugin/QGCLocationPlugin.pri b/src/QtLocationPlugin/QGCLocationPlugin.pri index d0673c45f3b0bacc10559a8ef5f7cb94cf3dfbe3..9e44cae4f498d6c4bc61194b54d5d5bdb90df2b8 100644 --- a/src/QtLocationPlugin/QGCLocationPlugin.pri +++ b/src/QtLocationPlugin/QGCLocationPlugin.pri @@ -17,22 +17,29 @@ contains(QT_VERSION, 5.5.1) { } HEADERS += \ - $$PWD/qgeoserviceproviderpluginqgc.h \ - $$PWD/qgeotiledmappingmanagerengineqgc.h \ - $$PWD/qgeotilefetcherqgc.h \ - $$PWD/qgeomapreplyqgc.h \ - $$PWD/qgeocodingmanagerengineqgc.h \ - $$PWD/qgeocodereplyqgc.h \ - $$PWD/OpenPilotMaps.h + $$PWD/QGCMapEngine.h \ + $$PWD/QGCMapEngineData.h \ + $$PWD/QGCMapTileSet.h \ + $$PWD/QGCMapUrlEngine.h \ + $$PWD/QGCTileCacheWorker.h \ + $$PWD/QGeoCodeReplyQGC.h \ + $$PWD/QGeoCodingManagerEngineQGC.h \ + $$PWD/QGeoMapReplyQGC.h \ + $$PWD/QGeoServiceProviderPluginQGC.h \ + $$PWD/QGeoTileFetcherQGC.h \ + $$PWD/QGeoTiledMappingManagerEngineQGC.h \ SOURCES += \ - $$PWD/qgeoserviceproviderpluginqgc.cpp \ - $$PWD/qgeotiledmappingmanagerengineqgc.cpp \ - $$PWD/qgeotilefetcherqgc.cpp \ - $$PWD/qgeomapreplyqgc.cpp \ - $$PWD/qgeocodingmanagerengineqgc.cpp \ - $$PWD/qgeocodereplyqgc.cpp \ - $$PWD/OpenPilotMaps.cc + $$PWD/QGCMapEngine.cpp \ + $$PWD/QGCMapTileSet.cpp \ + $$PWD/QGCMapUrlEngine.cpp \ + $$PWD/QGCTileCacheWorker.cpp \ + $$PWD/QGeoCodeReplyQGC.cpp \ + $$PWD/QGeoCodingManagerEngineQGC.cpp \ + $$PWD/QGeoMapReplyQGC.cpp \ + $$PWD/QGeoServiceProviderPluginQGC.cpp \ + $$PWD/QGeoTileFetcherQGC.cpp \ + $$PWD/QGeoTiledMappingManagerEngineQGC.cpp \ OTHER_FILES += \ $$PWD/qgc_maps_plugin.json diff --git a/src/QtLocationPlugin/QGCMapEngine.cpp b/src/QtLocationPlugin/QGCMapEngine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3ba9c7a144dde92e0c5ccfbfe8465e58f23cc98 --- /dev/null +++ b/src/QtLocationPlugin/QGCMapEngine.cpp @@ -0,0 +1,432 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Map Tile Cache + * + * @author Gus Grubba + * + */ + +#include +#include +#include +#include + +#include "QGCMapEngine.h" +#include "QGCMapTileSet.h" + +Q_DECLARE_METATYPE(QGCMapTask::TaskType) +Q_DECLARE_METATYPE(QGCTile) +Q_DECLARE_METATYPE(QList) + +static const char* kDbFileName = "qgcMapCache.db"; +static QLocale kLocale; + +struct stQGeoTileCacheQGCMapTypes { + const char* name; + UrlFactory::MapType type; +}; + +//-- IMPORTANT +// Changes here must reflect those in QGeoTiledMappingManagerEngineQGC.cpp + +stQGeoTileCacheQGCMapTypes kMapTypes[] = { + {"Google Street Map", UrlFactory::GoogleMap}, + {"Google Satellite Map", UrlFactory::GoogleSatellite}, + {"Google Terrain Map", UrlFactory::GoogleTerrain}, + {"Bing Street Map", UrlFactory::BingMap}, + {"Bing Satellite Map", UrlFactory::BingSatellite}, + {"Bing Hybrid Map", UrlFactory::BingHybrid}, + {"MapQuest Street Map", UrlFactory::MapQuestMap}, + {"MapQuest Satellite Map", UrlFactory::MapQuestSat} + /* + {"Open Street Map", UrlFactory::OpenStreetMap} + */ +}; + +#define NUM_MAPS (sizeof(kMapTypes) / sizeof(stQGeoTileCacheQGCMapTypes)) + +stQGeoTileCacheQGCMapTypes kMapBoxTypes[] = { + {"MapBox Street Map", UrlFactory::MapBoxStreets}, + {"MapBox Satellite Map", UrlFactory::MapBoxSatellite}, + {"MapBox High Contrast Map",UrlFactory::MapBoxHighContrast}, + {"MapBox Light Map", UrlFactory::MapBoxLight}, + {"MapBox Dark Map", UrlFactory::MapBoxDark}, + {"MapBox Hybrid Map", UrlFactory::MapBoxHybrid}, + {"MapBox Wheat Paste Map", UrlFactory::MapBoxWheatPaste}, + {"MapBox Streets Basic Map",UrlFactory::MapBoxStreetsBasic}, + {"MapBox Comic Map", UrlFactory::MapBoxComic}, + {"MapBox Outdoors Map", UrlFactory::MapBoxOutdoors}, + {"MapBox Run, Byke and Hike Map", UrlFactory::MapBoxRunBikeHike}, + {"MapBox Pencil Map", UrlFactory::MapBoxPencil}, + {"MapBox Pirates Map", UrlFactory::MapBoxPirates}, + {"MapBox Emerald Map", UrlFactory::MapBoxEmerald} +}; + +#define NUM_MAPBOXMAPS (sizeof(kMapBoxTypes) / sizeof(stQGeoTileCacheQGCMapTypes)) + +static const char* kMapBoxTokenKey = "MapBoxToken"; +static const char* kMaxDiskCacheKey = "MaxDiskCache"; +static const char* kMaxMemCacheKey = "MaxMemoryCache"; + +//----------------------------------------------------------------------------- +// Singleton +static QGCMapEngine* kMapEngine = NULL; +QGCMapEngine* +getQGCMapEngine() +{ + if(!kMapEngine) + kMapEngine = new QGCMapEngine(); + return kMapEngine; +} + +//----------------------------------------------------------------------------- +void +destroyMapEngine() +{ + if(kMapEngine) { + delete kMapEngine; + kMapEngine = NULL; + } +} + +//----------------------------------------------------------------------------- +QGCMapEngine::QGCMapEngine() + : _urlFactory(new UrlFactory::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) +{ + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType>(); + connect(&_worker, &QGCCacheWorker::updateTotals, this, &QGCMapEngine::_updateTotals); +} + +//----------------------------------------------------------------------------- +QGCMapEngine::~QGCMapEngine() +{ + _worker.quit(); + _worker.wait(); + if(_urlFactory) + delete _urlFactory; +} + +//----------------------------------------------------------------------------- +void +QGCMapEngine::init() +{ +#ifdef __mobile__ + QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/QGCMapCache55"); +#else + QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/QGCMapCache55"); +#endif + if(!QDir::root().mkpath(cacheDir)) { + qWarning() << "Could not create mapping disk cache directory: " << cacheDir; + cacheDir = QDir::homePath() + QLatin1String("/.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); + } else { + qCritical() << "Could not find suitable map cache directory."; + } + QGCMapTask* task = new QGCMapTask(QGCMapTask::taskInit); + _worker.enqueueTask(task); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngine::addTask(QGCMapTask* task) +{ + _worker.enqueueTask(task); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngine::cacheTile(UrlFactory::MapType 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(UrlFactory::MapType type, const QString& hash, const QByteArray& image, const QString& format, qulonglong set) +{ + QGCSaveTileTask* task = new QGCSaveTileTask(new QGCCacheTile(hash, image, format, type, set)); + _worker.enqueueTask(task); +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngine::getTileHash(UrlFactory::MapType type, int x, int y, int z) +{ + char hashSource[64]; + snprintf(hashSource, sizeof(hashSource), "%04d%08d%08d%03d", (int)type, x, y, z); + return QString(hashSource); +} + +//----------------------------------------------------------------------------- +UrlFactory::MapType +QGCMapEngine::hashToType(const QString& hash) +{ + QString type = hash.mid(0,4); + return (UrlFactory::MapType)type.toInt(); +} + +//----------------------------------------------------------------------------- +QGCFetchTileTask* +QGCMapEngine::createFetchTileTask(UrlFactory::MapType 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, UrlFactory::MapType mapType) +{ + if(zoom < 1) zoom = 1; + if(zoom > 18) zoom = 18; + QGCTileSet set; + set.tileX0 = long2tileX(topleftLon, zoom); + set.tileY0 = lat2tileY(topleftLat, zoom); + set.tileX1 = long2tileX(bottomRightLon, zoom); + set.tileY1 = lat2tileY(bottomRightLat, zoom); + set.tileCount = (quint64)((quint64)set.tileX1 - (quint64)set.tileX0 + 1) * (quint64)((quint64)set.tileY1 - (quint64)set.tileY0 + 1); + set.tileSize = UrlFactory::averageSizeForType(mapType) * set.tileCount; + return set; +} + +//----------------------------------------------------------------------------- +int +QGCMapEngine::long2tileX(double lon, int z) +{ + return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); +} + +//----------------------------------------------------------------------------- +int +QGCMapEngine::lat2tileY(double lat, int z) +{ + return (int)(floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); +} + +//----------------------------------------------------------------------------- +UrlFactory::MapType +QGCMapEngine::getTypeFromName(const QString& name) +{ + size_t i; + for(i = 0; i < NUM_MAPS; i++) { + if(name.compare(kMapTypes[i].name, Qt::CaseInsensitive) == 0) + return kMapTypes[i].type; + } + for(i = 0; i < NUM_MAPBOXMAPS; i++) { + if(name.compare(kMapBoxTypes[i].name, Qt::CaseInsensitive) == 0) + return kMapBoxTypes[i].type; + } + return UrlFactory::Invalid; +} + +//----------------------------------------------------------------------------- +QStringList +QGCMapEngine::getMapNameList() +{ + QStringList mapList; + for(size_t i = 0; i < NUM_MAPS; i++) { + mapList << kMapTypes[i].name; + } + if(!getMapBoxToken().isEmpty()) { + for(size_t i = 0; i < NUM_MAPBOXMAPS; i++) { + mapList << kMapBoxTypes[i].name; + } + } + return mapList; +} + +//----------------------------------------------------------------------------- +void +QGCMapEngine::setMapBoxToken(const QString& token) +{ + QSettings settings; + settings.setValue(kMapBoxTokenKey, token); + _mapBoxToken = token; +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngine::getMapBoxToken() +{ + if(_mapBoxToken.isEmpty()) { + QSettings settings; + _mapBoxToken = settings.value(kMapBoxTokenKey).toString(); + } + return _mapBoxToken; +} + +//----------------------------------------------------------------------------- +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 + } + return _maxMemCache; +} + +//----------------------------------------------------------------------------- +void +QGCMapEngine::setMaxMemCache(quint32 size) +{ + 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((double)size / 1024.0, 'f', 1) + "kB"; + else if(size < 1024 * 1024 * 1024) + return kLocale.toString((double)size / (1024.0 * 1024.0), 'f', 1) + "MB"; + else if(size < 1024.0 * 1024.0 * 1024.0 * 1024.0) + return kLocale.toString((double)size / (1024.0 * 1024.0 * 1024.0), 'f', 1) + "GB"; + else + return kLocale.toString((double)size / (1024.0 * 1024.0 * 1024.0 * 1024), 'f', 1) + "TB"; +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngine::numberToString(quint32 number) +{ + return kLocale.toString(number); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngine::_updateTotals(quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize) +{ + emit updateTotals(totaltiles, totalsize, defaulttiles, defaultsize); + quint64 maxSize = getMaxDiskCache() * 1024 * 1024; + 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(UrlFactory::MapType type) +{ + switch(type) { + case UrlFactory::GoogleMap: + case UrlFactory::GoogleSatellite: + case UrlFactory::GoogleTerrain: + case UrlFactory::BingMap: + case UrlFactory::BingSatellite: + case UrlFactory::BingHybrid: + return 12; + case UrlFactory::MapQuestMap: + case UrlFactory::MapQuestSat: + return 8; + default: + break; + } + return 6; +} + +//----------------------------------------------------------------------------- +QGCCreateTileSetTask::~QGCCreateTileSetTask() +{ + //-- If not sent out, delete it + if(!_saved && _tileSet) + delete _tileSet; +} + +// Resolution math: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Resolution_and_Scale + diff --git a/src/QtLocationPlugin/QGCMapEngine.h b/src/QtLocationPlugin/QGCMapEngine.h new file mode 100644 index 0000000000000000000000000000000000000000..f9654c6c2cf8e0f90da454de6b432d990770d240 --- /dev/null +++ b/src/QtLocationPlugin/QGCMapEngine.h @@ -0,0 +1,137 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Map Tile Cache + * + * @author Gus Grubba + * + */ + +#ifndef QGC_MAP_ENGINE_H +#define QGC_MAP_ENGINE_H + +#include + +#include "QGCMapUrlEngine.h" +#include "QGCMapEngineData.h" +#include "QGCTileCacheWorker.h" + +//----------------------------------------------------------------------------- +class QGCTileSet +{ +public: + QGCTileSet() + { + clear(); + } + QGCTileSet& operator += (QGCTileSet& other) + { + tileX0 += other.tileX0; + tileX1 += other.tileX1; + tileY0 += other.tileY0; + tileY1 += other.tileY1; + tileCount += other.tileCount; + tileSize += other.tileSize; + return *this; + } + void clear() + { + tileX0 = 0; + tileX1 = 0; + tileY0 = 0; + tileY1 = 0; + tileCount = 0; + tileSize = 0; + } + + int tileX0; + int tileX1; + int tileY0; + int tileY1; + quint64 tileCount; + quint64 tileSize; +}; + +//----------------------------------------------------------------------------- +class QGCMapEngine : public QObject +{ + Q_OBJECT +public: + QGCMapEngine (); + ~QGCMapEngine (); + + void init (); + void addTask (QGCMapTask *task); + void cacheTile (UrlFactory::MapType type, int x, int y, int z, const QByteArray& image, const QString& format, qulonglong set = UINT64_MAX); + void cacheTile (UrlFactory::MapType type, const QString& hash, const QByteArray& image, const QString& format, qulonglong set = UINT64_MAX); + QGCFetchTileTask* createFetchTileTask (UrlFactory::MapType type, int x, int y, int z); + QStringList getMapNameList (); + const QString userAgent () { return _userAgent; } + void setUserAgent (const QString& ua) { _userAgent = ua; } + UrlFactory::MapType hashToType (const QString& hash); + QString getMapBoxToken (); + void setMapBoxToken (const QString& token); + quint32 getMaxDiskCache (); + void setMaxDiskCache (quint32 size); + quint32 getMaxMemCache (); + void setMaxMemCache (quint32 size); + const QString getCachePath () { return _cachePath; } + const QString getCacheFilename () { return _cacheFile; } + + UrlFactory* urlFactory () { return _urlFactory; } + + //-- Tile Math + static QGCTileSet getTileCount (int zoom, double topleftLon, double topleftLat, double bottomRightLon, double bottomRightLat, UrlFactory::MapType mapType); + static int long2tileX (double lon, int z); + static int lat2tileY (double lat, int z); + static QString getTileHash (UrlFactory::MapType type, int x, int y, int z); + static UrlFactory::MapType getTypeFromName (const QString &name); + static QString bigSizeToString (quint64 size); + static QString numberToString (quint32 number); + static int concurrentDownloads (UrlFactory::MapType type); + +private slots: + void _updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); + void _pruned (); + +signals: + void updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); + +private: + QGCCacheWorker _worker; + QString _cachePath; + QString _cacheFile; + QString _mapBoxToken; + UrlFactory* _urlFactory; + QString _userAgent; + quint32 _maxDiskCache; + quint32 _maxMemCache; + bool _prunning; +}; + +extern QGCMapEngine* getQGCMapEngine(); +extern void destroyMapEngine(); + +#endif // QGC_MAP_ENGINE_H diff --git a/src/QtLocationPlugin/QGCMapEngineData.h b/src/QtLocationPlugin/QGCMapEngineData.h new file mode 100644 index 0000000000000000000000000000000000000000..a07d5700547e23c55fa921f98853a3582b0eb8ec --- /dev/null +++ b/src/QtLocationPlugin/QGCMapEngineData.h @@ -0,0 +1,382 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Map Tile Cache Data + * + * @author Gus Grubba + * + */ + +#ifndef QGC_MAP_ENGINE_DATA_H +#define QGC_MAP_ENGINE_DATA_H + +#include +#include +#include +#include + +#include "QGCMapUrlEngine.h" + +class QGCCachedTileSet; + +//----------------------------------------------------------------------------- +class QGCTile : public QObject +{ + Q_OBJECT +public: + QGCTile() + : _x(0) + , _y(0) + , _z(0) + , _set(UINT64_MAX) + , _type(UrlFactory::Invalid) + { + } + + QGCTile(const QGCTile& other) + : _x(other.x()) + , _y(other.y()) + , _z(other.z()) + , _set(other.set()) + , _hash(other.hash()) + , _type(other.type()) + { + } + + enum TyleState { + StatePending = 0, + StateDownloading, + StateError, + StateComplete + }; + + int x () const { return _x; } + int y () const { return _y; } + int z () const { return _z; } + qulonglong set () const { return _set; } + const QString hash () const { return _hash; } + UrlFactory::MapType type () const { return _type; } + + void setX (int x) { _x = x; } + void setY (int y) { _y = y; } + void setZ (int z) { _z = z; } + void setTileSet (qulonglong set) { _set = set; } + void setHash (const QString& hash) { _hash = hash; } + void setType (UrlFactory::MapType type) { _type = type; } + +private: + int _x; + int _y; + int _z; + qulonglong _set; + QString _hash; + UrlFactory::MapType _type; +}; + +//----------------------------------------------------------------------------- +class QGCCacheTile : public QObject +{ + Q_OBJECT +public: + QGCCacheTile (const QString hash, const QByteArray img, const QString format, UrlFactory::MapType type, qulonglong set = UINT64_MAX) + : _set(set) + , _hash(hash) + , _img(img) + , _format(format) + , _type(type) + { + } + QGCCacheTile (const QString hash, qulonglong set) + : _set(set) + , _hash(hash) + { + } + qulonglong set () { return _set; } + QString hash () { return _hash; } + QByteArray img () { return _img; } + QString format () { return _format;} + UrlFactory::MapType type () { return _type; } +private: + qulonglong _set; + QString _hash; + QByteArray _img; + QString _format; + UrlFactory::MapType _type; +}; + +//----------------------------------------------------------------------------- +class QGCMapTask : public QObject +{ + Q_OBJECT +public: + + enum TaskType { + taskInit, + taskCacheTile, + taskFetchTile, + taskFetchTileSets, + taskCreateTileSet, + taskGetTileDownloadList, + taskUpdateTileDownloadState, + taskDeleteTileSet, + taskPruneCache, + taskReset + }; + + QGCMapTask(TaskType type) + : _type(type) + {} + virtual ~QGCMapTask() + {} + + virtual TaskType type () { return _type; } + + void setError(QString errorString) + { + emit error(_type, errorString); + } + +signals: + void error (QGCMapTask::TaskType type, QString errorString); + +private: + TaskType _type; +}; + +//----------------------------------------------------------------------------- +class QGCFetchTileSetTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCFetchTileSetTask() + : QGCMapTask(QGCMapTask::taskFetchTileSets) + {} + + void setTileSetFetched(QGCCachedTileSet* tileSet) + { + emit tileSetFetched(tileSet); + } + +signals: + void tileSetFetched (QGCCachedTileSet* tileSet); +}; + +//----------------------------------------------------------------------------- +class QGCCreateTileSetTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCCreateTileSetTask(QGCCachedTileSet* tileSet) + : QGCMapTask(QGCMapTask::taskCreateTileSet) + , _tileSet(tileSet) + , _saved(false) + {} + + ~QGCCreateTileSetTask(); + + QGCCachedTileSet* tileSet () { return _tileSet; } + + void setTileSetSaved() + { + //-- Flag as saved. Signalee wll maintain it. + _saved = true; + emit tileSetSaved(_tileSet); + } + +signals: + void tileSetSaved (QGCCachedTileSet* tileSet); + +private: + QGCCachedTileSet* _tileSet; + bool _saved; +}; + +//----------------------------------------------------------------------------- +class QGCFetchTileTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCFetchTileTask(const QString hash) + : QGCMapTask(QGCMapTask::taskFetchTile) + , _hash(hash) + {} + + ~QGCFetchTileTask() + { + } + + void setTileFetched(QGCCacheTile* tile) + { + emit tileFetched(tile); + } + + QString hash() { return _hash; } + +signals: + void tileFetched (QGCCacheTile* tile); + +private: + QString _hash; +}; + +//----------------------------------------------------------------------------- +class QGCSaveTileTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCSaveTileTask(QGCCacheTile* tile) + : QGCMapTask(QGCMapTask::taskCacheTile) + , _tile(tile) + {} + + ~QGCSaveTileTask() + { + if(_tile) + delete _tile; + } + + QGCCacheTile* tile() { return _tile; } + +private: + QGCCacheTile* _tile; +}; + +//----------------------------------------------------------------------------- +class QGCGetTileDownloadListTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCGetTileDownloadListTask(qulonglong setID, int count) + : QGCMapTask(QGCMapTask::taskGetTileDownloadList) + , _setID(setID) + , _count(count) + {} + + qulonglong setID() { return _setID; } + int count() { return _count; } + + void setTileListFetched(QList tiles) + { + emit tileListFetched(tiles); + } + +signals: + void tileListFetched (QList tiles); + +private: + qulonglong _setID; + int _count; +}; + +//----------------------------------------------------------------------------- +class QGCUpdateTileDownloadStateTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCUpdateTileDownloadStateTask(qulonglong setID, QGCTile::TyleState state, const QString& hash) + : QGCMapTask(QGCMapTask::taskUpdateTileDownloadState) + , _setID(setID) + , _state(state) + , _hash(hash) + {} + + QString hash () { return _hash; } + qulonglong setID () { return _setID; } + QGCTile::TyleState state () { return _state; } + +private: + qulonglong _setID; + QGCTile::TyleState _state; + QString _hash; +}; + +//----------------------------------------------------------------------------- +class QGCDeleteTileSetTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCDeleteTileSetTask(qulonglong setID) + : QGCMapTask(QGCMapTask::taskDeleteTileSet) + , _setID(setID) + {} + + qulonglong setID() { return _setID; } + + void setTileSetDeleted() + { + emit tileSetDeleted(_setID); + } + +signals: + void tileSetDeleted(qulonglong setID); + +private: + qulonglong _setID; +}; + +//----------------------------------------------------------------------------- +class QGCPruneCacheTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCPruneCacheTask(quint64 amount) + : QGCMapTask(QGCMapTask::taskPruneCache) + , _amount(amount) + {} + + quint64 amount() { return _amount; } + + void setPruned() + { + emit pruned(); + } + +signals: + void pruned(); + +private: + quint64 _amount; +}; + +//----------------------------------------------------------------------------- +class QGCResetTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCResetTask() + : QGCMapTask(QGCMapTask::taskReset) + {} + + void setResetCompleted() + { + emit resetCompleted(); + } + +signals: + void resetCompleted(); +}; + + +#endif // QGC_MAP_ENGINE_DATA_H diff --git a/src/QtLocationPlugin/QGCMapTileSet.cpp b/src/QtLocationPlugin/QGCMapTileSet.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ff29bfdf02b19e9586394a2e085283be3d0480a --- /dev/null +++ b/src/QtLocationPlugin/QGCMapTileSet.cpp @@ -0,0 +1,325 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Map Tile Set + * + * @author Gus Grubba + * + */ + +#include +#include + +#include "QGCMapEngine.h" +#include "QGCMapTileSet.h" +#include "QGCMapEngineManager.h" + +QGC_LOGGING_CATEGORY(QGCCachedTileSetLog, "QGCCachedTileSetLog") + +#define TILE_BATCH_SIZE 256 + +//----------------------------------------------------------------------------- +QGCCachedTileSet::QGCCachedTileSet(const QString& name, const QString& description) + : _name(name) + , _description(description) + , _topleftLat(0.0) + , _topleftLon(0.0) + , _bottomRightLat(0.0) + , _bottomRightLon(0.0) + , _numTiles(0) + , _tilesSize(0) + , _savedTiles(0) + , _savedSize(0) + , _minZoom(3) + , _maxZoom(3) + , _defaultSet(false) + , _deleting(false) + , _downloading(false) + , _id(0) + , _type(UrlFactory::Invalid) + , _networkManager(NULL) + , _errorCount(0) + , _noMoreTiles(false) + , _batchRequested(false) + , _manager(NULL) +{ + +} + +//----------------------------------------------------------------------------- +QGCCachedTileSet::~QGCCachedTileSet() +{ + if(_networkManager) { + delete _networkManager; + } +} + +//----------------------------------------------------------------------------- +QString +QGCCachedTileSet::errorCountStr() +{ + return QGCMapEngine::numberToString(_errorCount); +} + +//----------------------------------------------------------------------------- +QString +QGCCachedTileSet::numTilesStr() +{ + return QGCMapEngine::numberToString(_numTiles); +} + +//----------------------------------------------------------------------------- +QString +QGCCachedTileSet::tilesSizeStr() +{ + return QGCMapEngine::bigSizeToString(_tilesSize); +} + +//----------------------------------------------------------------------------- +QString +QGCCachedTileSet::savedTilesStr() +{ + return QGCMapEngine::numberToString(_savedTiles); +} + +//----------------------------------------------------------------------------- +QString +QGCCachedTileSet::savedSizeStr() +{ + return QGCMapEngine::bigSizeToString(_savedSize); +} + +//----------------------------------------------------------------------------- +QString +QGCCachedTileSet::downloadStatus() +{ + //-- Default size has no estimage. If complete, show only total size as well. + if(_defaultSet || _numTiles == _savedTiles) { + return savedSizeStr(); + } else { + return savedSizeStr() + " / " + tilesSizeStr(); + } +} + +//----------------------------------------------------------------------------- +void +QGCCachedTileSet::createDownloadTask() +{ + if(!_downloading) { + _errorCount = 0; + _downloading = true; + _noMoreTiles = false; + emit downloadingChanged(); + emit errorCountChanged(); + } + QGCGetTileDownloadListTask* task = new QGCGetTileDownloadListTask(_id, TILE_BATCH_SIZE); + connect(task, &QGCGetTileDownloadListTask::tileListFetched, this, &QGCCachedTileSet::_tileListFetched); + if(_manager) + connect(task, &QGCMapTask::error, _manager, &QGCMapEngineManager::taskError); + getQGCMapEngine()->addTask(task); + emit numTilesChanged(); + emit tilesSizeChanged(); + _batchRequested = true; +} + +//----------------------------------------------------------------------------- +void +QGCCachedTileSet::resumeDownloadTask() +{ + //-- Reset and download error flag (for all tiles) + QGCUpdateTileDownloadStateTask* task = new QGCUpdateTileDownloadStateTask(_id, QGCTile::StatePending, "*"); + getQGCMapEngine()->addTask(task); + //-- Start download + createDownloadTask(); +} + +//----------------------------------------------------------------------------- +void +QGCCachedTileSet::cancelDownloadTask() +{ + if(_downloading) { + _downloading = false; + emit downloadingChanged(); + } +} + +//----------------------------------------------------------------------------- +void +QGCCachedTileSet::_tileListFetched(QList tiles) +{ + _batchRequested = false; + //-- Done? + if(tiles.size() < TILE_BATCH_SIZE) { + _noMoreTiles = true; + } + if(!tiles.size()) { + return; + } + //-- If this is the first time, create Network Manager + if (!_networkManager) { + _networkManager = new QNetworkAccessManager(this); + } + //-- Add tiles to the list + _tilesToDownload += tiles; + //-- Kick downloads + _prepareDownload(); +} + +//----------------------------------------------------------------------------- +void QGCCachedTileSet::_prepareDownload() +{ + if(!_tilesToDownload.count()) { + //-- Are we done? + if(_noMoreTiles) { + if(!_errorCount) { + _numTiles = _savedTiles; + _tilesSize = _savedSize; + } + emit numTilesChanged(); + emit tilesSizeChanged(); + emit savedSizeChanged(); + emit savedTilesChanged(); + _downloading = false; + emit downloadingChanged(); + emit completeChanged(); + } else { + if(!_batchRequested) + createDownloadTask(); + } + return; + } + //-- Prepare queue (QNetworkAccessManager has a limit for concurrent downloads) + for(int i = _replies.count(); i < QGCMapEngine::concurrentDownloads(_type); i++) { + if(_tilesToDownload.count()) { + QGCTile* tile = _tilesToDownload.first(); + _tilesToDownload.removeFirst(); + QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL(tile->type(), tile->x(), tile->y(), tile->z(), _networkManager); + request.setAttribute(QNetworkRequest::User, tile->hash()); + QNetworkReply* reply = _networkManager->get(request); + reply->setParent(0); + connect(reply, &QNetworkReply::finished, this, &QGCCachedTileSet::_networkReplyFinished); + connect(reply, static_cast(&QNetworkReply::error), this, &QGCCachedTileSet::_networkReplyError); + _replies.insert(tile->hash(), reply); + tile->deleteLater(); + //-- Refill queue if running low + if(!_batchRequested && !_noMoreTiles && _tilesToDownload.count() < (QGCMapEngine::concurrentDownloads(_type) * 10)) { + //-- Request new batch of tiles + createDownloadTask(); + } + } + } +} + +//----------------------------------------------------------------------------- +void +QGCCachedTileSet::_networkReplyFinished() +{ + //-- Figure out which reply this is + QNetworkReply* reply = qobject_cast(QObject::sender()); + if(!reply) { + qWarning() << "QGCMapEngineManager::networkReplyFinished() NULL Reply"; + return; + } + //-- Get tile hash + const QString hash = reply->request().attribute(QNetworkRequest::User).toString(); + if(!hash.isEmpty()) { + if(_replies.contains(hash)) { + _replies.remove(hash); + } else { + qWarning() << "QGCMapEngineManager::networkReplyFinished() Reply not in list: " << hash; + } + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "QGCMapEngineManager::networkReplyFinished() Error:" << reply->errorString(); + return; + } + qCDebug(QGCCachedTileSetLog) << "Tile fetched" << hash; + QByteArray image = reply->readAll(); + UrlFactory::MapType type = getQGCMapEngine()->hashToType(hash); + QString format = getQGCMapEngine()->urlFactory()->getImageFormat(type, image); + if(!format.isEmpty()) { + //-- Cache tile + getQGCMapEngine()->cacheTile(type, hash, image, format, _id); + QGCUpdateTileDownloadStateTask* task = new QGCUpdateTileDownloadStateTask(_id, QGCTile::StateComplete, hash); + getQGCMapEngine()->addTask(task); + //-- Updated cached (downloaded) data + _savedSize += image.size(); + _savedTiles++; + emit savedSizeChanged(); + emit savedTilesChanged(); + //-- Update estimate + if(_savedTiles % 10 == 0) { + quint32 avg = _savedSize / _savedTiles; + _tilesSize = avg * _numTiles; + emit tilesSizeChanged(); + } + } + //-- Setup a new download + _prepareDownload(); + } else { + qWarning() << "QGCMapEngineManager::networkReplyFinished() Empty Hash"; + } + reply->deleteLater(); +} + +//----------------------------------------------------------------------------- +void +QGCCachedTileSet::_networkReplyError(QNetworkReply::NetworkError error) +{ + //-- Figure out which reply this is + QNetworkReply* reply = qobject_cast(QObject::sender()); + if (!reply) { + return; + } + //-- Upodate error count + _errorCount++; + emit errorCountChanged(); + //-- Get tile hash + QString hash = reply->request().attribute(QNetworkRequest::User).toString(); + qCDebug(QGCCachedTileSetLog) << "Error fetching tile" << reply->errorString(); + if(!hash.isEmpty()) { + if(_replies.contains(hash)) { + _replies.remove(hash); + } else { + qWarning() << "QGCMapEngineManager::networkReplyError() Reply not in list: " << hash; + } + if (error != QNetworkReply::OperationCanceledError) { + qWarning() << "QGCMapEngineManager::networkReplyError() Error:" << reply->errorString(); + } + QGCUpdateTileDownloadStateTask* task = new QGCUpdateTileDownloadStateTask(_id, QGCTile::StateError, hash); + getQGCMapEngine()->addTask(task); + } else { + qWarning() << "QGCMapEngineManager::networkReplyError() Empty Hash"; + } + //-- Setup a new download + _prepareDownload(); + reply->deleteLater(); +} + +//----------------------------------------------------------------------------- +void +QGCCachedTileSet::setManager(QGCMapEngineManager* mgr) +{ + _manager = mgr; +} diff --git a/src/QtLocationPlugin/QGCMapTileSet.h b/src/QtLocationPlugin/QGCMapTileSet.h new file mode 100644 index 0000000000000000000000000000000000000000..60d1ae7a775b7a483bd75b777e888c9441083fa1 --- /dev/null +++ b/src/QtLocationPlugin/QGCMapTileSet.h @@ -0,0 +1,193 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Map Tile Set + * + * @author Gus Grubba + * + */ + +#ifndef QGC_MAP_TILE_SET_H +#define QGC_MAP_TILE_SET_H + +#include +#include +#include +#include +#include + +#include "QGCLoggingCategory.h" +#include "QGCMapEngineData.h" +#include "QGCMapUrlEngine.h" + +Q_DECLARE_LOGGING_CATEGORY(QGCCachedTileSetLog) + +class QGCTile; +class QGCMapEngineManager; + +//----------------------------------------------------------------------------- +class QGCCachedTileSet : public QObject +{ + Q_OBJECT +public: + QGCCachedTileSet (const QString& name, const QString& description); + ~QGCCachedTileSet (); + + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString description READ description CONSTANT) + Q_PROPERTY(QString mapTypeStr READ mapTypeStr CONSTANT) + Q_PROPERTY(double topleftLon READ topleftLon CONSTANT) + Q_PROPERTY(double topleftLat READ topleftLat CONSTANT) + Q_PROPERTY(double bottomRightLon READ bottomRightLon CONSTANT) + Q_PROPERTY(double bottomRightLat READ bottomRightLat CONSTANT) + Q_PROPERTY(int minZoom READ minZoom CONSTANT) + Q_PROPERTY(int maxZoom READ maxZoom CONSTANT) + Q_PROPERTY(quint32 numTiles READ numTiles NOTIFY numTilesChanged) + Q_PROPERTY(QString numTilesStr READ numTilesStr NOTIFY numTilesChanged) + Q_PROPERTY(quint64 tilesSize READ tilesSize NOTIFY tilesSizeChanged) + Q_PROPERTY(QString tilesSizeStr READ tilesSizeStr NOTIFY tilesSizeChanged) + Q_PROPERTY(quint32 savedTiles READ savedTiles NOTIFY savedTilesChanged) + Q_PROPERTY(QString savedTilesStr READ savedTilesStr NOTIFY savedTilesChanged) + Q_PROPERTY(quint64 savedSize READ savedSize NOTIFY savedSizeChanged) + Q_PROPERTY(QString savedSizeStr READ savedSizeStr NOTIFY savedSizeChanged) + Q_PROPERTY(QString downloadStatus READ downloadStatus NOTIFY savedSizeChanged) + Q_PROPERTY(QDateTime creationDate READ creationDate CONSTANT) + Q_PROPERTY(bool complete READ complete NOTIFY completeChanged) + Q_PROPERTY(bool defaultSet READ defaultSet CONSTANT) + Q_PROPERTY(quint64 setID READ setID CONSTANT) + Q_PROPERTY(bool deleting READ deleting NOTIFY deletingChanged) + Q_PROPERTY(bool downloading READ downloading NOTIFY downloadingChanged) + Q_PROPERTY(quint32 errorCount READ errorCount NOTIFY errorCountChanged) + Q_PROPERTY(QString errorCountStr READ errorCountStr NOTIFY errorCountChanged) + Q_PROPERTY(QImage thumbNail READ thumbNail CONSTANT) + + Q_INVOKABLE void createDownloadTask (); + Q_INVOKABLE void resumeDownloadTask (); + Q_INVOKABLE void cancelDownloadTask (); + + void setManager (QGCMapEngineManager* mgr); + + QString name () { return _name; } + QString description () { return _description; } + QString mapTypeStr () { return _mapTypeStr; } + double topleftLat () { return _topleftLat; } + double topleftLon () { return _topleftLon; } + double bottomRightLat () { return _bottomRightLat; } + double bottomRightLon () { return _bottomRightLon; } + quint32 numTiles () { return (quint32)_numTiles; } + QString numTilesStr (); + quint64 tilesSize () { return (quint64)_tilesSize; } + QString tilesSizeStr (); + quint32 savedTiles () { return (quint32)_savedTiles; } + QString savedTilesStr (); + quint64 savedSize () { return (quint64)_savedSize; } + QString savedSizeStr (); + QString downloadStatus (); + int minZoom () { return _minZoom; } + int maxZoom () { return _maxZoom; } + QDateTime creationDate () { return _creationDate; } + quint64 id () { return _id; } + UrlFactory::MapType type () { return _type; } + bool complete () { return _defaultSet || (_numTiles == _savedTiles); } + bool defaultSet () { return _defaultSet; } + quint64 setID () { return _id; } + bool deleting () { return _deleting; } + bool downloading () { return _downloading; } + quint32 errorCount () { return _errorCount; } + QString errorCountStr (); + QImage thumbNail () { return _thumbNail; } + + void setName (QString name) { _name = name; } + void setDescription (QString desc) { _description = desc; } + void setMapTypeStr (QString typeStr) { _mapTypeStr = typeStr; } + void setTopleftLat (double lat) { _topleftLat = lat; } + void setTopleftLon (double lon) { _topleftLon = lon; } + void setBottomRightLat (double lat) { _bottomRightLat = lat; } + void setBottomRightLon (double lon) { _bottomRightLon = lon; } + void setNumTiles (quint32 num) { _numTiles = num; } + void setTilesSize (quint64 size) { _tilesSize = size; } + void setSavedTiles (quint32 num) { _savedTiles = num; emit savedTilesChanged(); } + void setSavedSize (quint64 size) { _savedSize = size; emit savedSizeChanged(); } + void setMinZoom (int zoom) { _minZoom = zoom; } + void setMaxZoom (int zoom) { _maxZoom = zoom; } + void setCreationDate (QDateTime date) { _creationDate = date; } + void setId (quint64 id) { _id = id; } + void setType (UrlFactory::MapType type) { _type = type; } + void setDefaultSet (bool def) { _defaultSet = def; } + void setDeleting (bool del) { _deleting = del; emit deletingChanged(); } + void setDownloading (bool down) { _downloading = down; } + void setThumbNail (const QImage& thumb) { _thumbNail = thumb; } + +signals: + void deletingChanged (); + void downloadingChanged (); + void numTilesChanged (); + void tilesSizeChanged (); + void savedTilesChanged (); + void savedSizeChanged (); + void completeChanged (); + void errorCountChanged (); + +private slots: + void _tileListFetched (QList tiles); + void _networkReplyFinished (); + void _networkReplyError (QNetworkReply::NetworkError error); + +private: + void _prepareDownload (); + +private: + QString _name; + QString _description; + QString _mapTypeStr; + double _topleftLat; + double _topleftLon; + double _bottomRightLat; + double _bottomRightLon; + quint32 _numTiles; + quint64 _tilesSize; + quint32 _savedTiles; + quint64 _savedSize; + int _minZoom; + int _maxZoom; + bool _defaultSet; + bool _deleting; + bool _downloading; + QDateTime _creationDate; + quint64 _id; + UrlFactory::MapType _type; + QNetworkAccessManager* _networkManager; + QHash _replies; + quint32 _errorCount; + //-- Tile download + QList _tilesToDownload; + bool _noMoreTiles; + bool _batchRequested; + QGCMapEngineManager* _manager; + QImage _thumbNail; +}; + +#endif // QGC_MAP_TILE_SET_H + diff --git a/src/QtLocationPlugin/QGCMapUrlEngine.cpp b/src/QtLocationPlugin/QGCMapUrlEngine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0473d26fd887666e9106dc4e1907178b6fc642fa --- /dev/null +++ b/src/QtLocationPlugin/QGCMapUrlEngine.cpp @@ -0,0 +1,513 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2015 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @author Gus Grubba + * Original work: The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. + */ + +#include +#include +#include +#include +#include +#include + +#include "QGCMapEngine.h" + +//----------------------------------------------------------------------------- +UrlFactory::UrlFactory() + : _timeout(5 * 1000) + , _googleVersionRetrieved(false) + , _googleReply(NULL) +{ + QStringList langs = QLocale::system().uiLanguages(); + if (langs.length() > 0) { + _language = langs[0]; + } + // Google version strings + _versionGoogleMap = "m@336"; + _versionGoogleSatellite = "194"; + _versionGoogleLabels = "h@336"; + _versionGoogleTerrain = "t@132,r@336"; + _secGoogleWord = "Galileo"; + // BingMaps + _versionBingMaps = "563"; +} + +//----------------------------------------------------------------------------- +UrlFactory::~UrlFactory() +{ + if(_googleReply) + _googleReply->deleteLater(); +} + + +//----------------------------------------------------------------------------- +QString +UrlFactory::getImageFormat(MapType type, const QByteArray& image) +{ + QString format; + if(image.size() > 2) + { + if((char)image[0] == (char)0xff && (char)image[1] == (char)0xd8) + format = "jpg"; + else if((char)image[0] == (char)0x89 && (char)image[1] == (char)0x50) + format = "png"; + else { + switch (type) { + case GoogleMap: + case GoogleLabels: + case GoogleTerrain: + case GoogleHybrid: + case BingMap: + case OpenStreetMap: + format = "png"; + break; + case MapQuestMap: + case MapQuestSat: + case MapBoxStreets: + case MapBoxLight: + case MapBoxDark: + case MapBoxSatellite: + case MapBoxHybrid: + case MapBoxWheatPaste: + case MapBoxStreetsBasic: + case MapBoxComic: + case MapBoxOutdoors: + case MapBoxRunBikeHike: + case MapBoxPencil: + case MapBoxPirates: + case MapBoxEmerald: + case MapBoxHighContrast: + case GoogleSatellite: + case BingSatellite: + case BingHybrid: + format = "jpg"; + break; + default: + qWarning("UrlFactory::getImageFormat() Unknown map id %d", type); + break; + } + } + } + return format; +} + +//----------------------------------------------------------------------------- +QNetworkRequest +UrlFactory::getTileURL(MapType type, int x, int y, int zoom, QNetworkAccessManager* networkManager) +{ + //-- Build URL + QNetworkRequest request; + QString url = _getURL(type, x, y, zoom, networkManager); + if(url.isEmpty()) + return request; + request.setUrl(QUrl(url)); + request.setRawHeader("Accept", "*/*"); + request.setRawHeader("User-Agent", _userAgent); + switch (type) { + case GoogleMap: + case GoogleSatellite: + case GoogleLabels: + case GoogleTerrain: + case GoogleHybrid: + request.setRawHeader("Referrer", "http://maps.google.com/"); + break; + case BingHybrid: + case BingMap: + case BingSatellite: + request.setRawHeader("Referrer", "http://www.bing.com/maps/"); + break; + /* + case OpenStreetMapSurfer: + case OpenStreetMapSurferTerrain: + request.setRawHeader("Referrer", "http://www.mapsurfer.net/"); + break; + case OpenStreetMap: + case OpenStreetOsm: + request.setRawHeader("Referrer", "https://www.openstreetmap.org/"); + break; + */ + default: + break; + } + return request; +} + +//----------------------------------------------------------------------------- +void +UrlFactory::_getSecGoogleWords(int x, int y, QString &sec1, QString &sec2) +{ + sec1 = ""; // after &x=... + sec2 = ""; // after &zoom=... + int seclen = ((x * 3) + y) % 8; + sec2 = _secGoogleWord.left(seclen); + if (y >= 10000 && y < 100000) { + sec1 = "&s="; + } +} + +//----------------------------------------------------------------------------- +QString +UrlFactory::_getURL(MapType type, int x, int y, int zoom, QNetworkAccessManager* networkManager) +{ + switch (type) { + case GoogleMap: + { + // http://mt1.google.com/vt/lyrs=m + QString server = "mt"; + QString request = "vt"; + QString sec1 = ""; // after &x=... + QString sec2 = ""; // after &zoom=... + _getSecGoogleWords(x, y, sec1, sec2); + _tryCorrectGoogleVersions(networkManager); + return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleMap).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2); + } + break; + case GoogleSatellite: + { + // http://mt1.google.com/vt/lyrs=s + QString server = "khm"; + QString request = "kh"; + QString sec1 = ""; // after &x=... + QString sec2 = ""; // after &zoom=... + _getSecGoogleWords(x, y, sec1, sec2); + _tryCorrectGoogleVersions(networkManager); + return QString("http://%1%2.google.com/%3/v=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleSatellite).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2); + } + break; + case GoogleLabels: + { + QString server = "mts"; + QString request = "vt"; + QString sec1 = ""; // after &x=... + QString sec2 = ""; // after &zoom=... + _getSecGoogleWords(x, y, sec1, sec2); + _tryCorrectGoogleVersions(networkManager); + return QString("http://%1%2.google.com/%3/lyrs=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleLabels).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2); + } + break; + case GoogleTerrain: + { + QString server = "mt"; + QString request = "vt"; + QString sec1 = ""; // after &x=... + QString sec2 = ""; // after &zoom=... + _getSecGoogleWords(x, y, sec1, sec2); + _tryCorrectGoogleVersions(networkManager); + return QString("http://%1%2.google.com/%3/v=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(_getServerNum(x, y, 4)).arg(request).arg(_versionGoogleTerrain).arg(_language).arg(x).arg(sec1).arg(y).arg(zoom).arg(sec2); + } + break; + /* + case OpenStreetMap: + { + char letter = "abc"[_getServerNum(x, y, 3)]; + return QString("https://%1.tile.openstreetmap.org/%2/%3/%4.png").arg(letter).arg(zoom).arg(x).arg(y); + } + break; + case OpenStreetOsm: + { + char letter = "abc"[_getServerNum(x, y, 3)]; + return QString("http://%1.tah.openstreetmap.org/Tiles/tile/%2/%3/%4.png").arg(letter).arg(zoom).arg(x).arg(y); + } + break; + case OpenStreetMapSurfer: + { + // http://tiles1.mapsurfer.net/tms_r.ashx?x=37378&y=20826&z=16 + return QString("http://tiles1.mapsurfer.net/tms_r.ashx?x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom); + } + break; + case OpenStreetMapSurferTerrain: + { + // http://tiles2.mapsurfer.net/tms_t.ashx?x=9346&y=5209&z=14 + return QString("http://tiles2.mapsurfer.net/tms_t.ashx?x=%1&y=%2&z=%3").arg(x).arg(y).arg(zoom); + } + break; + */ + case BingMap: + { + QString key = _tileXYToQuadKey(x, y, zoom); + return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/r%2.png?g=%3&mkt=%4").arg(_getServerNum(x, y, 4)).arg(key).arg(_versionBingMaps).arg(_language); + } + break; + case BingSatellite: + { + QString key = _tileXYToQuadKey(x, y, zoom); + return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/a%2.jpeg?g=%3&mkt=%4").arg(_getServerNum(x, y, 4)).arg(key).arg(_versionBingMaps).arg(_language); + } + break; + case BingHybrid: + { + QString key = _tileXYToQuadKey(x, y, zoom); + return QString("http://ecn.t%1.tiles.virtualearth.net/tiles/h%2.jpeg?g=%3&mkt=%4").arg(_getServerNum(x, y, 4)).arg(key).arg(_versionBingMaps).arg(_language); + } + case MapQuestMap: + { + char letter = "1234"[_getServerNum(x, y, 4)]; + return QString("http://otile%1.mqcdn.com/tiles/1.0.0/map/%2/%3/%4.jpg").arg(letter).arg(zoom).arg(x).arg(y); + } + break; + case MapQuestSat: + { + char letter = "1234"[_getServerNum(x, y, 4)]; + return QString("http://otile%1.mqcdn.com/tiles/1.0.0/sat/%2/%3/%4.jpg").arg(letter).arg(zoom).arg(x).arg(y); + } + break; + + case MapBoxStreets: + case MapBoxLight: + case MapBoxDark: + case MapBoxSatellite: + case MapBoxHybrid: + case MapBoxWheatPaste: + case MapBoxStreetsBasic: + case MapBoxComic: + case MapBoxOutdoors: + case MapBoxRunBikeHike: + case MapBoxPencil: + case MapBoxPirates: + case MapBoxEmerald: + case MapBoxHighContrast: + { + QString mapBoxToken = getQGCMapEngine()->getMapBoxToken(); + if(!mapBoxToken.isEmpty()) { + QString server = "https://api.mapbox.com/v4/"; + switch(type) { + case MapBoxStreets: + server += "mapbox.streets"; + break; + case MapBoxLight: + server += "mapbox.light"; + break; + case MapBoxDark: + server += "mapbox.dark"; + break; + case MapBoxSatellite: + server += "mapbox.satellite"; + break; + case MapBoxHybrid: + server += "mapbox.streets-satellite"; + break; + case MapBoxWheatPaste: + server += "mapbox.wheatpaste"; + break; + case MapBoxStreetsBasic: + server += "mapbox.streets-basic"; + break; + case MapBoxComic: + server += "mapbox.comic"; + break; + case MapBoxOutdoors: + server += "mapbox.outdoors"; + break; + case MapBoxRunBikeHike: + server += "mapbox.run-bike-hike"; + break; + case MapBoxPencil: + server += "mapbox.pencil"; + break; + case MapBoxPirates: + server += "mapbox.pirates"; + break; + case MapBoxEmerald: + server += "mapbox.emerald"; + break; + case MapBoxHighContrast: + server += "mapbox.high-contrast"; + break; + default: + return QString::null; + } + server += QString("/%1/%2/%3.jpg80?access_token=%4").arg(zoom).arg(x).arg(y).arg(mapBoxToken); + return server; + } + } + break; + + default: + qWarning("Unknown map id %d\n", type); + break; + } + return QString::null; +} + +//----------------------------------------------------------------------------- +QString +UrlFactory::_tileXYToQuadKey(int tileX, int tileY, int levelOfDetail) +{ + QString quadKey; + for (int i = levelOfDetail; i > 0; i--) { + char digit = '0'; + int mask = 1 << (i - 1); + if ((tileX & mask) != 0) { + digit++; + } + if ((tileY & mask) != 0) { + digit++; + digit++; + } + quadKey.append(digit); + } + return quadKey; +} + +//----------------------------------------------------------------------------- +int +UrlFactory::_getServerNum(int x, int y, int max) +{ + return (x + 2 * y) % max; +} + +//----------------------------------------------------------------------------- +void +UrlFactory::_networkReplyError(QNetworkReply::NetworkError error) +{ + qWarning() << "Could not connect to google maps. Error:" << error; + if(_googleReply) + { + _googleReply->deleteLater(); + _googleReply = NULL; + } +} + +//----------------------------------------------------------------------------- +void +UrlFactory::_replyDestroyed() +{ + _googleReply = NULL; +} + +//----------------------------------------------------------------------------- +void +UrlFactory::_googleVersionCompleted() +{ + if (!_googleReply || (_googleReply->error() != QNetworkReply::NoError)) { + qDebug() << "Error collecting Google maps version info"; + return; + } + QString html = QString(_googleReply->readAll()); + QRegExp reg("\"*http://mt0.google.com/vt/lyrs=m@(\\d*)",Qt::CaseInsensitive); + if (reg.indexIn(html) != -1) { + QStringList gc = reg.capturedTexts(); + _versionGoogleMap = QString("m@%1").arg(gc[1]); + } + reg = QRegExp("\"*http://mt0.google.com/vt/lyrs=h@(\\d*)",Qt::CaseInsensitive); + if (reg.indexIn(html) != -1) { + QStringList gc = reg.capturedTexts(); + _versionGoogleLabels = QString("h@%1").arg(gc[1]); + } + reg = QRegExp("\"*http://khm\\D?\\d.google.com/kh/v=(\\d*)",Qt::CaseInsensitive); + if (reg.indexIn(html) != -1) { + QStringList gc = reg.capturedTexts(); + _versionGoogleSatellite = gc[1]; + } + reg = QRegExp("\"*http://mt0.google.com/vt/lyrs=t@(\\d*),r@(\\d*)",Qt::CaseInsensitive); + if (reg.indexIn(html) != -1) { + QStringList gc = reg.capturedTexts(); + _versionGoogleTerrain = QString("t@%1,r@%2").arg(gc[1]).arg(gc[2]); + } + _googleReply->deleteLater(); + _googleReply = NULL; +} + +//----------------------------------------------------------------------------- +void +UrlFactory::_tryCorrectGoogleVersions(QNetworkAccessManager* networkManager) +{ + QMutexLocker locker(&_googleVersionMutex); + if (_googleVersionRetrieved) { + return; + } + _googleVersionRetrieved = true; + if(networkManager) + { + QNetworkRequest qheader; + QNetworkProxy proxy = networkManager->proxy(); + QNetworkProxy tProxy; + tProxy.setType(QNetworkProxy::NoProxy); + networkManager->setProxy(tProxy); + QString url = "http://maps.google.com/maps"; + qheader.setUrl(QUrl(url)); + QByteArray ua; + ua.append(getQGCMapEngine()->userAgent()); + qheader.setRawHeader("User-Agent", ua); + _googleReply = networkManager->get(qheader); + connect(_googleReply, &QNetworkReply::finished, this, &UrlFactory::_googleVersionCompleted); + connect(_googleReply, &QNetworkReply::destroyed, this, &UrlFactory::_replyDestroyed); + connect(_googleReply, static_cast(&QNetworkReply::error), + this, &UrlFactory::_networkReplyError); + networkManager->setProxy(proxy); + } +} + +#define AVERAGE_GOOGLE_STREET_MAP 4913 +#define AVERAGE_GOOGLE_TERRAIN_MAP 19391 +#define AVERAGE_BING_STREET_MAP 1297 +#define AVERAGE_BING_SAT_MAP 19597 +#define AVERAGE_GOOGLE_SAT_MAP 56887 +#define AVERAGE_MAPBOX_SAT_MAP 15739 +#define AVERAGE_MAPBOX_STREET_MAP 5648 +#define AVERAGE_TILE_SIZE 13652 + +//----------------------------------------------------------------------------- +quint32 +UrlFactory::averageSizeForType(MapType type) +{ + switch (type) { + case GoogleMap: + return AVERAGE_GOOGLE_STREET_MAP; + case BingMap: + return AVERAGE_BING_STREET_MAP; + case GoogleSatellite: + return AVERAGE_GOOGLE_SAT_MAP; + case MapBoxSatellite: + return AVERAGE_MAPBOX_SAT_MAP; + case BingHybrid: + case BingSatellite: + return AVERAGE_BING_SAT_MAP; + case GoogleTerrain: + return AVERAGE_GOOGLE_TERRAIN_MAP; + case MapBoxStreets: + case MapBoxStreetsBasic: + case MapBoxRunBikeHike: + return AVERAGE_MAPBOX_STREET_MAP; + case GoogleLabels: + case MapBoxDark: + case MapBoxLight: + case MapBoxOutdoors: + case MapBoxPencil: + case OpenStreetMap: + case GoogleHybrid: + case MapBoxComic: + case MapBoxEmerald: + case MapBoxHighContrast: + case MapBoxHybrid: + case MapBoxPirates: + case MapBoxWheatPaste: + default: + break; + } + return AVERAGE_TILE_SIZE; +} diff --git a/src/QtLocationPlugin/QGCMapUrlEngine.h b/src/QtLocationPlugin/QGCMapUrlEngine.h new file mode 100644 index 0000000000000000000000000000000000000000..57db8512736d103579c6e33204a7d12dacde1075 --- /dev/null +++ b/src/QtLocationPlugin/QGCMapUrlEngine.h @@ -0,0 +1,121 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @author Gus Grubba + */ + +#ifndef QGC_MAP_URL_ENGINE_H +#define QGC_MAP_URL_ENGINE_H + +#include +#include +#include +#include +#include +#include + +#define MAX_MAP_ZOOM (18.0) + +class UrlFactory : public QObject { + Q_OBJECT +public: + + enum MapType + { + Invalid = -1, + + GoogleMap = 1, + GoogleSatellite = 4, + GoogleLabels = 8, + GoogleTerrain = 16, + GoogleHybrid = 20, + + OpenStreetMap = 32, + OpenStreetOsm = 33, + OpenStreetMapSurfer = 34, + OpenStreetMapSurferTerrain=35, + + BingMap = 444, + BingSatellite = 555, + BingHybrid = 666, + + MapQuestMap = 700, + MapQuestSat = 701, + + MapBoxStreets = 6000, + MapBoxLight = 6001, + MapBoxDark = 6002, + MapBoxSatellite = 6003, + MapBoxHybrid = 6004, + MapBoxWheatPaste = 6005, + MapBoxStreetsBasic = 6006, + MapBoxComic = 6007, + MapBoxOutdoors = 6008, + MapBoxRunBikeHike = 6009, + MapBoxPencil = 6010, + MapBoxPirates = 6011, + MapBoxEmerald = 6012, + MapBoxHighContrast = 6013 + }; + + UrlFactory (); + ~UrlFactory (); + + QNetworkRequest getTileURL (MapType type, int x, int y, int zoom, QNetworkAccessManager* networkManager); + QString getImageFormat (MapType type, const QByteArray& image); + + static quint32 averageSizeForType (MapType type); + +private slots: + void _networkReplyError (QNetworkReply::NetworkError error); + void _googleVersionCompleted (); + void _replyDestroyed (); + +private: + QString _getURL (MapType type, int x, int y, int zoom, QNetworkAccessManager* networkManager); + void _getSecGoogleWords (int x, int y, QString& sec1, QString& sec2); + int _getServerNum (int x, int y, int max); + void _tryCorrectGoogleVersions (QNetworkAccessManager* networkManager); + QString _tileXYToQuadKey (int tileX, int tileY, int levelOfDetail); + + int _timeout; + bool _googleVersionRetrieved; + QNetworkReply* _googleReply; + QMutex _googleVersionMutex; + QByteArray _userAgent; + QString _language; + + // Google version strings + QString _versionGoogleMap; + QString _versionGoogleSatellite; + QString _versionGoogleLabels; + QString _versionGoogleTerrain; + QString _secGoogleWord; + // BingMaps + QString _versionBingMaps; + +}; + +#endif diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.cpp b/src/QtLocationPlugin/QGCTileCacheWorker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fdc155315a853631015b3fd38492283109dc7146 --- /dev/null +++ b/src/QtLocationPlugin/QGCTileCacheWorker.cpp @@ -0,0 +1,751 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Map Tile Cache Worker Thread + * + * @author Gus Grubba + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "time.h" + +#include "QGCMapEngine.h" +#include "QGCMapTileSet.h" + +const char* kDefaultSet = "Default Tile Set"; +const QString kSession = QLatin1String("QGeoTileWorkerSession"); + +QGC_LOGGING_CATEGORY(QGCTileCacheLog, "QGCTileCacheLog") + +//-- Update intervals + +#define LONG_TIMEOUT 5 +#define SHORT_TIMEOUT 2 + +//----------------------------------------------------------------------------- +QGCCacheWorker::QGCCacheWorker() + : _db(NULL) + , _valid(false) + , _failed(false) + , _defaultSet(UINT64_MAX) + , _totalSize(0) + , _totalCount(0) + , _defaultSize(0) + , _defaultCount(0) + , _lastUpdate(0) + , _updateTimeout(SHORT_TIMEOUT) +{ + +} + +//----------------------------------------------------------------------------- +QGCCacheWorker::~QGCCacheWorker() +{ + +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::setDatabaseFile(const QString& path) +{ + _databasePath = path; +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::quit() +{ + _mutex.lock(); + while(_taskQueue.count()) { + QGCMapTask* task = _taskQueue.dequeue(); + delete task; + } + _mutex.unlock(); + if(this->isRunning()) { + _waitc.wakeAll(); + } +} + +//----------------------------------------------------------------------------- +bool +QGCCacheWorker::enqueueTask(QGCMapTask* task) +{ + //-- If not initialized, the only allowed task is Init + if(!_valid && task->type() != QGCMapTask::taskInit) { + task->setError("Database Not Initialized"); + task->deleteLater(); + return false; + } + if(!_taskQueue.contains(task)) + { + _mutex.lock(); + _taskQueue.enqueue(task); + _mutex.unlock(); + if(this->isRunning()) { + _waitc.wakeAll(); + } else { + this->start(QThread::NormalPriority); + } + return true; + } + //-- Should never happen + return false; +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::run() +{ + if(!_valid && !_failed) { + _init(); + } + if(_valid) { + _db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession)); + _db->setDatabaseName(_databasePath); + _db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE"); + _valid = _db->open(); + } + while(true) { + QGCMapTask* task; + if(_taskQueue.count()) { + _mutex.lock(); + task = _taskQueue.dequeue(); + _mutex.unlock(); + switch(task->type()) { + case QGCMapTask::taskInit: + break; + case QGCMapTask::taskCacheTile: + _saveTile(task); + break; + case QGCMapTask::taskFetchTile: + _getTile(task); + break; + case QGCMapTask::taskFetchTileSets: + _getTileSets(task); + break; + case QGCMapTask::taskCreateTileSet: + _createTileSet(task); + break; + case QGCMapTask::taskGetTileDownloadList: + _getTileDownloadList(task); + break; + case QGCMapTask::taskUpdateTileDownloadState: + _updateTileDownloadState(task); + break; + case QGCMapTask::taskDeleteTileSet: + _deleteTileSet(task); + break; + case QGCMapTask::taskPruneCache: + _pruneCache(task); + break; + case QGCMapTask::taskReset: + _resetCacheDatabase(task); + break; + } + task->deleteLater(); + //-- Check for update timeout + size_t count = _taskQueue.count(); + if(count > 100) { + _updateTimeout = LONG_TIMEOUT; + } else if(count < 25) { + _updateTimeout = SHORT_TIMEOUT; + } + if(!count || (time(0) - _lastUpdate > _updateTimeout)) { + _updateTotals(); + } + } else { + //-- Wait a bit before shutting things down + _waitmutex.lock(); + int timeout = 5000; + if(!_waitc.wait(&_waitmutex, timeout)) + { + _waitmutex.unlock(); + _mutex.lock(); + //-- If nothing to do, close db and leave thread + if(!_taskQueue.count()) { + _mutex.unlock(); + break; + } + _mutex.unlock(); + } + _waitmutex.unlock(); + } + } + if(_db) { + delete _db; + _db = NULL; + QSqlDatabase::removeDatabase(kSession); + } +} +//----------------------------------------------------------------------------- +bool +QGCCacheWorker::_findTileSetID(const QString name, quint64& setID) +{ + 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 +QGCCacheWorker::_getDefaultTileSet() +{ + 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; +} + +//----------------------------------------------------------------------------- +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().toTime_t()); + if(query.exec()) { + quint64 tileID = query.lastInsertId().toULongLong(); + quint64 setID = task->tile()->set() == UINT64_MAX ? _getDefaultTileSet() : task->tile()->set(); + 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(QGCTileCacheLog) << "_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(); + } +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_getTile(QGCMapTask* mtask) +{ + if(!_valid) { + mtask->setError("No 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()) { + QByteArray ar = query.value(0).toByteArray(); + QString format = query.value(1).toString(); + UrlFactory::MapType type = (UrlFactory::MapType)query.value(2).toInt(); + qCDebug(QGCTileCacheLog) << "_getTile() (Found in DB) HASH:" << task->hash(); + QGCCacheTile* tile = new QGCCacheTile(task->hash(), ar, format, type); + task->setTileFetched(tile); + found = true; + } + } + if(!found) { + qCDebug(QGCTileCacheLog) << "_getTile() (NOT in DB) HASH:" << task->hash(); + task->setError("Tile not in cache database"); + } +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_getTileSets(QGCMapTask* mtask) +{ + if(!_valid) { + mtask->setError("No Cache Database"); + return; + } + QGCFetchTileSetTask* task = static_cast(mtask); + QSqlQuery query(*_db); + QString s = QString("SELECT * FROM TileSets ORDER BY defaultSet DESC, name ASC"); + if(query.exec(s)) { + while(query.next()) { + QString name = query.value("name").toString(); + QString desc = query.value("description").toString(); + QGCCachedTileSet* set = new QGCCachedTileSet(name, desc); + 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::MapType)query.value("type").toInt()); + set->setNumTiles(query.value("numTiles").toUInt()); + set->setTilesSize(query.value("tilesSize").toULongLong()); + set->setDefaultSet(query.value("defaultSet").toInt() != 0); + set->setCreationDate(QDateTime::fromTime_t(query.value("date").toUInt())); + //-- Load thumbnail (if not default set) + if(!set->defaultSet()) { + int w = query.value("thumbW").toInt(); + int h = query.value("thumbH").toInt(); + if(w && h) { + QByteArray ba = query.value("thumbNail").toByteArray(); + set->setThumbNail(QImage((uchar*)(void*)ba.data(), w, h, QImage::Format_RGB32)); + } + } + _updateSetTotals(set); + //-- Object created here must be moved to app thread to be used there + set->moveToThread(QApplication::instance()->thread()); + task->tileSetFetched(set); + } + } else { + task->setError("No tile set in database"); + } +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_updateSetTotals(QGCCachedTileSet* set) +{ + _updateTotals(); + if(set->defaultSet()) { + //-- Default Set is already computed + set->setSavedTiles(_totalCount); + set->setSavedSize(_totalSize); + set->setNumTiles(_defaultCount); + set->setTilesSize(_defaultSize); + return; + } + QSqlQuery subquery(*_db); + //-- Count everythin for Default Set + 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()); + if(subquery.exec(sq)) { + if(subquery.next()) { + set->setSavedTiles(subquery.value(0).toUInt()); + set->setSavedSize(subquery.value(1).toULongLong()); + //-- Update estimated size + if(set->savedTiles() > 10 && set->savedSize()) { + quint32 avg = set->savedSize() / set->savedTiles(); + set->setTilesSize(avg * set->numTiles()); + } + } + } +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_updateTotals() +{ + QSqlQuery query(*_db); + QString s; + s = QString("SELECT COUNT(size), SUM(size) FROM Tiles"); + 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 A INNER JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = %1").arg(_getDefaultTileSet()); + if(query.exec(s)) { + if(query.next()) { + _defaultCount = query.value(0).toUInt(); + _defaultSize = query.value(1).toULongLong(); + } + } + emit updateTotals(_totalCount, _totalSize, _defaultCount, _defaultSize); + _lastUpdate = time(0); +} + +//----------------------------------------------------------------------------- +bool +QGCCacheWorker::_findTile(const QString hash) +{ + QSqlQuery query(*_db); + QString s = QString("SELECT type FROM Tiles WHERE hash = \"%1\"").arg(hash); + if(query.exec(s)) { + if(query.next()) { + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +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, description, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, tilesSize, thumbNail, thumbW, thumbH, date" + ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + query.addBindValue(task->tileSet()->name()); + query.addBindValue(task->tileSet()->description()); + 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(task->tileSet()->type()); + query.addBindValue(task->tileSet()->numTiles()); + query.addBindValue(task->tileSet()->tilesSize()); + if(task->tileSet()->thumbNail().isNull()) { + query.addBindValue(QByteArray(1,'\0')); + query.addBindValue(0); + query.addBindValue(0); + } else { + query.addBindValue(QByteArray((const char *)(void*)task->tileSet()->thumbNail().convertToFormat(QImage::Format_RGB32).bits(), task->tileSet()->thumbNail().byteCount())); + query.addBindValue(task->tileSet()->thumbNail().width()); + query.addBindValue(task->tileSet()->thumbNail().height()); + } + query.addBindValue(QDateTime::currentDateTime().toTime_t()); + if(!query.exec()) { + qWarning() << "Map Cache SQL error (add tileSet into TileSets):" << query.lastError().text(); + } else { + //-- Get just creted (auto-incremented) setID + quint64 setID = query.lastInsertId().toULongLong(); + task->tileSet()->setId(setID); + //-- Prepare Download List + for(int z = task->tileSet()->minZoom(); z <= task->tileSet()->maxZoom(); z++) { + QGCTileSet set = QGCMapEngine::getTileCount(z, + task->tileSet()->topleftLon(), task->tileSet()->topleftLat(), + task->tileSet()->bottomRightLon(), task->tileSet()->bottomRightLat(), task->tileSet()->type()); + UrlFactory::MapType 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 = QGCMapEngine::getTileHash(type, x, y, z); + if(!_findTile(hash)) { + //-- 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(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++; + } + } + } + } + //-- Now update how many tiles we actually have to download + quint64 actual_size = actual_count * UrlFactory::averageSizeForType(task->tileSet()->type()); + QString s = QString("UPDATE TileSets SET numTiles = %1, tilesSize = %2 WHERE setID = %3").arg(actual_count).arg(actual_size).arg(task->tileSet()->setID()); + if(!query.exec(s)) { + qWarning() << "Map Cache SQL error (set TilesDownload state):" << query.lastError().text(); + } + //-- Done + _updateSetTotals(task->tileSet()); + task->setTileSetSaved(); + return; + } + } + mtask->setError("Error saving tile set"); +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_getTileDownloadList(QGCMapTask* mtask) +{ + if(!_valid) { + mtask->setError("No Cache Database"); + return; + } + QList 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->setHash(query.value("hash").toString()); + tile->setType((UrlFactory::MapType)query.value("type").toInt()); + tile->setX(query.value("x").toInt()); + tile->setY(query.value("y").toInt()); + tile->setZ(query.value("z").toInt()); + tiles.append(tile); + } + for(int i = 0; i < tiles.size(); i++) { + s = QString("UPDATE TilesDownload SET state = %1 WHERE setID = %2 and hash = \"%3\"").arg((int)QGCTile::StateDownloading).arg(task->setID()).arg(tiles[i]->hash()); + if(!query.exec(s)) { + qWarning() << "Map Cache SQL error (set TilesDownload state):" << query.lastError().text(); + } + } + } + task->setTileListFetched(tiles); +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_updateTileDownloadState(QGCMapTask* mtask) +{ + if(!_valid) { + mtask->setError("No Cache Database"); + 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((int)task->state()).arg(task->setID()); + } else { + s = QString("UPDATE TilesDownload SET state = %1 WHERE setID = %2 AND hash = \"%3\"").arg((int)task->state()).arg(task->setID()).arg(task->hash()); + } + } + if(!query.exec(s)) { + qWarning() << "QGCCacheWorker::_updateTileDownloadState() Error:" << query.lastError().text(); + } +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_pruneCache(QGCMapTask* mtask) +{ + if(!_valid) { + mtask->setError("No Cache Database"); + return; + } + QGCPruneCacheTask* task = static_cast(mtask); + QSqlQuery query(*_db); + QString s; + s = QString("SELECT tileID, size, hash FROM Tiles WHERE tileID IN (SELECT tileID FROM SetTiles WHERE setID = %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(QGCTileCacheLog) << "_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; + } + task->setPruned(); + } +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_deleteTileSet(QGCMapTask* mtask) +{ + if(!_valid) { + mtask->setError("No Cache Database"); + return; + } + QGCDeleteTileSetTask* task = static_cast(mtask); + QSqlQuery query(*_db); + QString s; + s = QString("DELETE FROM Tiles WHERE tileID IN (SELECT tileID FROM SetTiles WHERE setID = %1)").arg(task->setID()); + query.exec(s); + s = QString("DELETE FROM TilesDownload WHERE setID = %1").arg(task->setID()); + query.exec(s); + s = QString("DELETE FROM TileSets WHERE setID = %1").arg(task->setID()); + query.exec(s); + s = QString("DELETE FROM SetTiles WHERE setID = %1").arg(task->setID()); + query.exec(s); + _updateTotals(); + task->setTileSetDeleted(); +} +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_resetCacheDatabase(QGCMapTask* mtask) +{ + if(!_valid) { + mtask->setError("No Cache Database"); + 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); + _createDB(); + task->setResetCompleted(); +} + +//----------------------------------------------------------------------------- +bool +QGCCacheWorker::_init() +{ + _failed = false; + if(!_databasePath.isEmpty()) { + qCDebug(QGCTileCacheLog) << "Mapping cache directory:" << _databasePath; + //-- Initialize Database + _db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession)); + _db->setDatabaseName(_databasePath); + _db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE"); + if (_db->open()) { + _createDB(); + } else { + qCritical() << "Map Cache SQL error (init() open db):" << _db->lastError(); + _failed = true; + } + delete _db; + _db = NULL; + QSqlDatabase::removeDatabase(kSession); + } else { + qCritical() << "Could not find suitable cache directory."; + _failed = true; + } + return _failed; +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_createDB() +{ + 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 { + if(!query.exec( + "CREATE TABLE IF NOT EXISTS TileSets (" + "setID INTEGER PRIMARY KEY NOT NULL, " + "name TEXT NOT NULL UNIQUE, " + "description TEXT NOT NULL, " + "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, " + "tilesSize INTEGER DEFAULT 0, " + "defaultSet INTEGER DEFAULT 0, " + "thumbNail BLOB NULL, " + "thumbW INTEGER DEFAULT 0, " + "thumbH 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 + _valid = true; + } + } + } + } + //-- Create default tile set + if(_valid) { + QString s = QString("SELECT name FROM TileSets WHERE name = \"%1\"").arg(kDefaultSet); + if(query.exec(s)) { + if(!query.next()) { + query.prepare("INSERT INTO TileSets(name, description, defaultSet, date) VALUES(?, ?, ?, ?)"); + query.addBindValue(kDefaultSet); + query.addBindValue("System wide tile cache"); + 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; + } + } + } else { + qWarning() << "Map Cache SQL error (Looking for default tile set):" << _db->lastError(); + } + } + if(!_valid) { + QFile file(_databasePath); + file.remove(); + } + _failed = !_valid; +} diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.h b/src/QtLocationPlugin/QGCTileCacheWorker.h new file mode 100644 index 0000000000000000000000000000000000000000..646f761e5838ab038d74bb43f67681ab3034c528 --- /dev/null +++ b/src/QtLocationPlugin/QGCTileCacheWorker.h @@ -0,0 +1,105 @@ +/*===================================================================== + +QGroundControl Open Source Ground Control Station + +(c) 2009, 2016 QGROUNDCONTROL PROJECT + +This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + +======================================================================*/ + +/** + * @file + * @brief Map Tile Cache Worker Thread + * + * @author Gus Grubba + * + */ + +#ifndef QGC_TILE_CACHE_WORKER_H +#define QGC_TILE_CACHE_WORKER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "QGCLoggingCategory.h" + +Q_DECLARE_LOGGING_CATEGORY(QGCTileCacheLog) + +class QGCMapTask; +class QGCCachedTileSet; + +//----------------------------------------------------------------------------- +class QGCCacheWorker : public QThread +{ + Q_OBJECT +public: + QGCCacheWorker (); + ~QGCCacheWorker (); + + void quit (); + bool enqueueTask (QGCMapTask* task); + void setDatabaseFile (const QString& path); + +protected: + void run (); + +private: + void _saveTile (QGCMapTask* mtask); + void _getTile (QGCMapTask* mtask); + void _getTileSets (QGCMapTask* mtask); + void _createTileSet (QGCMapTask* mtask); + void _getTileDownloadList (QGCMapTask* mtask); + void _updateTileDownloadState(QGCMapTask* mtask); + void _deleteTileSet (QGCMapTask* mtask); + void _resetCacheDatabase (QGCMapTask* mtask); + void _pruneCache (QGCMapTask* mtask); + + bool _findTile (const QString hash); + bool _findTileSetID (const QString name, quint64& setID); + void _updateSetTotals (QGCCachedTileSet* set); + bool _init (); + void _createDB (); + quint64 _getDefaultTileSet (); + void _updateTotals (); + +signals: + void updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); + +private: + QQueue _taskQueue; + QMutex _mutex; + QMutex _waitmutex; + QWaitCondition _waitc; + QString _databasePath; + QSqlDatabase* _db; + bool _valid; + bool _failed; + quint64 _defaultSet; + quint64 _totalSize; + quint32 _totalCount; + quint64 _defaultSize; + quint32 _defaultCount; + time_t _lastUpdate; + int _updateTimeout; +}; + +#endif // QGC_TILE_CACHE_WORKER_H diff --git a/src/QtLocationPlugin/qgeocodereplyqgc.cpp b/src/QtLocationPlugin/QGeoCodeReplyQGC.cpp similarity index 99% rename from src/QtLocationPlugin/qgeocodereplyqgc.cpp rename to src/QtLocationPlugin/QGeoCodeReplyQGC.cpp index 48f6af56f9e6ff588718e2b5eb2073b2fc829de7..aeca6d4b5862e63ffe4e673914038b40f6121d6e 100644 --- a/src/QtLocationPlugin/qgeocodereplyqgc.cpp +++ b/src/QtLocationPlugin/QGeoCodeReplyQGC.cpp @@ -54,7 +54,7 @@ #include #include -#include "qgeocodereplyqgc.h" +#include "QGeoCodeReplyQGC.h" enum QGeoCodeTypeGoogle { GeoCodeTypeUnknown, diff --git a/src/QtLocationPlugin/qgeocodereplyqgc.h b/src/QtLocationPlugin/QGeoCodeReplyQGC.h similarity index 96% rename from src/QtLocationPlugin/qgeocodereplyqgc.h rename to src/QtLocationPlugin/QGeoCodeReplyQGC.h index b53b1726c85f6a04c15bb31fe56189ff8e3cf62f..4a94ce3baa2721b4d866737612be1998e6d8ced1 100644 --- a/src/QtLocationPlugin/qgeocodereplyqgc.h +++ b/src/QtLocationPlugin/QGeoCodeReplyQGC.h @@ -44,8 +44,8 @@ ** ****************************************************************************/ -#ifndef QGEOCODEREPLYGOOGLE_H -#define QGEOCODEREPLYGOOGLE_H +#ifndef QGEOCODEREPLYQGC_H +#define QGEOCODEREPLYQGC_H #include #include @@ -68,4 +68,4 @@ private: QNetworkReply *m_reply; }; -#endif // QGEOCODEREPLYGOOGLE_H +#endif // QGEOCODEREPLYQGC_H diff --git a/src/QtLocationPlugin/qgeocodingmanagerengineqgc.cpp b/src/QtLocationPlugin/QGeoCodingManagerEngineQGC.cpp similarity index 98% rename from src/QtLocationPlugin/qgeocodingmanagerengineqgc.cpp rename to src/QtLocationPlugin/QGeoCodingManagerEngineQGC.cpp index 567c329902723841d6d0173329bb071a358d52cc..384c1ac18705c691933c90349fb31dadcc43b079 100644 --- a/src/QtLocationPlugin/qgeocodingmanagerengineqgc.cpp +++ b/src/QtLocationPlugin/QGeoCodingManagerEngineQGC.cpp @@ -57,8 +57,8 @@ #include #include -#include "qgeocodingmanagerengineqgc.h" -#include "qgeocodereplyqgc.h" +#include "QGeoCodingManagerEngineQGC.h" +#include "QGeoCodeReplyQGC.h" static QString addressToQuery(const QGeoAddress &address) { diff --git a/src/QtLocationPlugin/qgeocodingmanagerengineqgc.h b/src/QtLocationPlugin/QGeoCodingManagerEngineQGC.h similarity index 96% rename from src/QtLocationPlugin/qgeocodingmanagerengineqgc.h rename to src/QtLocationPlugin/QGeoCodingManagerEngineQGC.h index 56040f4cdbf5eaf360573f10aed755c6cbe48023..14697c0f4cfa7fb01ce11faec949951fcaf1fd93 100644 --- a/src/QtLocationPlugin/qgeocodingmanagerengineqgc.h +++ b/src/QtLocationPlugin/QGeoCodingManagerEngineQGC.h @@ -44,8 +44,8 @@ ** ****************************************************************************/ -#ifndef QGEOCODINGMANAGERENGINEGOOGLE_H -#define QGEOCODINGMANAGERENGINEGOOGLE_H +#ifndef QGEOCODINGMANAGERENGINEQGC_H +#define QGEOCODINGMANAGERENGINEQGC_H #include #include @@ -78,4 +78,4 @@ private: QT_END_NAMESPACE -#endif // QGEOCODINGMANAGERENGINEGOOGLE_H +#endif // QGEOCODINGMANAGERENGINEQGC_H diff --git a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30d69e919cde031f224c5e0f7bda4ef89331a257 --- /dev/null +++ b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Aaron McCarthy +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +** 2015.4.4 +** Adapted for use with QGroundControl +** +** Gus Grubba +** +****************************************************************************/ + +#include +#include +#include + +#include "QGCMapEngine.h" +#include "QGeoMapReplyQGC.h" + +//----------------------------------------------------------------------------- +QGeoTiledMapReplyQGC::QGeoTiledMapReplyQGC(QNetworkAccessManager *networkManager, const QNetworkRequest &request, const QGeoTileSpec &spec, QObject *parent) + : QGeoTiledMapReply(spec, parent) + , _reply(NULL) + , _request(request) + , _networkManager(networkManager) +{ + if(_request.url().isEmpty()) { + if(!_badMapBox.size()) { + QFile b(":/res/notile.png"); + if(b.open(QFile::ReadOnly)) + _badMapBox = b.readAll(); + } + setMapImageData(_badMapBox); + setMapImageFormat("png"); + setFinished(true); + setCached(false); + } else { + QGCFetchTileTask* task = getQGCMapEngine()->createFetchTileTask((UrlFactory::MapType)spec.mapId(), spec.x(), spec.y(), spec.zoom()); + connect(task, &QGCFetchTileTask::tileFetched, this, &QGeoTiledMapReplyQGC::cacheReply); + connect(task, &QGCMapTask::error, this, &QGeoTiledMapReplyQGC::cacheError); + getQGCMapEngine()->addTask(task); + } +} + +//----------------------------------------------------------------------------- +QGeoTiledMapReplyQGC::~QGeoTiledMapReplyQGC() +{ + if (_reply) { + _reply->deleteLater(); + _reply = 0; + } +} +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::abort() +{ + if (_reply) + _reply->abort(); +} + +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::replyDestroyed() +{ + _reply = 0; +} + +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::networkReplyFinished() +{ + if (!_reply) { + return; + } + if (_reply->error() != QNetworkReply::NoError) { + return; + } + QByteArray a = _reply->readAll(); + setMapImageData(a); + QString format = getQGCMapEngine()->urlFactory()->getImageFormat((UrlFactory::MapType)tileSpec().mapId(), a); + if(!format.isEmpty()) { + setMapImageFormat(format); + getQGCMapEngine()->cacheTile((UrlFactory::MapType)tileSpec().mapId(), tileSpec().x(), tileSpec().y(), tileSpec().zoom(), a, format); + } + setFinished(true); + _reply->deleteLater(); + _reply = 0; +} + +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::networkReplyError(QNetworkReply::NetworkError error) +{ + if (!_reply) { + return; + } + if (error != QNetworkReply::OperationCanceledError) { + qWarning() << "Fetch tile error:" << _reply->errorString(); + } + _reply->deleteLater(); + _reply = 0; + if(!_badTile.size()) { + QFile b(":/res/notile.png"); + if(b.open(QFile::ReadOnly)) + _badTile = b.readAll(); + } + setMapImageData(_badTile); + setMapImageFormat("png"); + setFinished(true); + setCached(false); +} + +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::cacheError(QGCMapTask::TaskType type, QString /*errorString*/) +{ + if(type != QGCMapTask::taskFetchTile) { + qWarning() << "QGeoTiledMapReplyQGC::cacheError() for wrong task"; + } + //-- Tile not in cache. Get it off the Internet. + _reply = _networkManager->get(_request); + _reply->setParent(0); + connect(_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(_reply, SIGNAL(destroyed()), this, SLOT(replyDestroyed())); +} + +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::cacheReply(QGCCacheTile* tile) +{ + setMapImageData(tile->img()); + setMapImageFormat(tile->format()); + setFinished(true); + setCached(true); + tile->deleteLater(); +} diff --git a/src/QtLocationPlugin/qgeomapreplyqgc.h b/src/QtLocationPlugin/QGeoMapReplyQGC.h similarity index 77% rename from src/QtLocationPlugin/qgeomapreplyqgc.h rename to src/QtLocationPlugin/QGeoMapReplyQGC.h index 90d85583cd18a0bc37e1cec472e2f60a5c08c980..b66791090344cd64801a144e567f91bead548209 100644 --- a/src/QtLocationPlugin/qgeomapreplyqgc.h +++ b/src/QtLocationPlugin/QGeoMapReplyQGC.h @@ -44,31 +44,35 @@ ** ****************************************************************************/ -#ifndef QGEOMAPREPLYGOOGLE_H -#define QGEOMAPREPLYGOOGLE_H +#ifndef QGEOMAPREPLYQGC_H +#define QGEOMAPREPLYQGC_H #include #include -class QGeoMapReplyQGC : public QGeoTiledMapReply +#include "QGCMapEngineData.h" + +class QGeoTiledMapReplyQGC : public QGeoTiledMapReply { Q_OBJECT - public: - explicit QGeoMapReplyQGC(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent = 0); - ~QGeoMapReplyQGC(); - + QGeoTiledMapReplyQGC(QNetworkAccessManager* networkManager, const QNetworkRequest& request, const QGeoTileSpec &spec, QObject *parent = 0); + ~QGeoTiledMapReplyQGC(); void abort(); - QNetworkReply *networkReply() const; - -private Q_SLOTS: +private slots: void replyDestroyed (); void networkReplyFinished (); void networkReplyError (QNetworkReply::NetworkError error); + void cacheReply (QGCCacheTile* tile); + void cacheError (QGCMapTask::TaskType type, QString errorString); private: - QNetworkReply* m_reply; + QNetworkReply* _reply; + QNetworkRequest _request; + QNetworkAccessManager* _networkManager; + QByteArray _badMapBox; + QByteArray _badTile; }; -#endif // QGEOMAPREPLYGOOGLE_H +#endif // QGEOMAPREPLYQGC_H diff --git a/src/QtLocationPlugin/qgeoserviceproviderpluginqgc.cpp b/src/QtLocationPlugin/QGeoServiceProviderPluginQGC.cpp similarity index 78% rename from src/QtLocationPlugin/qgeoserviceproviderpluginqgc.cpp rename to src/QtLocationPlugin/QGeoServiceProviderPluginQGC.cpp index bead3e336e8c235155077aa9c967d10e6924db5c..1b24429ebfe357207ce82e6d645bbd600bf6b911 100644 --- a/src/QtLocationPlugin/qgeoserviceproviderpluginqgc.cpp +++ b/src/QtLocationPlugin/QGeoServiceProviderPluginQGC.cpp @@ -47,38 +47,48 @@ #include #include "qdebug.h" -#include "qgeoserviceproviderpluginqgc.h" -#include "qgeotiledmappingmanagerengineqgc.h" -#include "qgeocodingmanagerengineqgc.h" +#include "QGeoServiceProviderPluginQGC.h" +#include "QGeoTiledMappingManagerEngineQGC.h" +#include "QGeoCodingManagerEngineQGC.h" Q_EXTERN_C Q_DECL_EXPORT const char *qt_plugin_query_metadata(); Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance(); + +//----------------------------------------------------------------------------- const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_QGeoServiceProviderFactoryQGC() { QT_PREPEND_NAMESPACE(QStaticPlugin) plugin = { qt_plugin_instance, qt_plugin_query_metadata}; return plugin; } -QGeoCodingManagerEngine *QGeoServiceProviderFactoryQGC::createGeocodingManagerEngine( +//----------------------------------------------------------------------------- +QGeoCodingManagerEngine* +QGeoServiceProviderFactoryQGC::createGeocodingManagerEngine( const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const { return new QGeoCodingManagerEngineQGC(parameters, error, errorString); } -QGeoMappingManagerEngine *QGeoServiceProviderFactoryQGC::createMappingManagerEngine( +//----------------------------------------------------------------------------- +QGeoMappingManagerEngine* +QGeoServiceProviderFactoryQGC::createMappingManagerEngine( const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const { return new QGeoTiledMappingManagerEngineQGC(parameters, error, errorString); } -QGeoRoutingManagerEngine *QGeoServiceProviderFactoryQGC::createRoutingManagerEngine( +//----------------------------------------------------------------------------- +QGeoRoutingManagerEngine* +QGeoServiceProviderFactoryQGC::createRoutingManagerEngine( const QVariantMap &, QGeoServiceProvider::Error *, QString *) const { // Not implemented for QGC return NULL; } -QPlaceManagerEngine *QGeoServiceProviderFactoryQGC::createPlaceManagerEngine( +//----------------------------------------------------------------------------- +QPlaceManagerEngine* +QGeoServiceProviderFactoryQGC::createPlaceManagerEngine( const QVariantMap &, QGeoServiceProvider::Error *, QString *) const { // Not implemented for QGC diff --git a/src/QtLocationPlugin/qgeoserviceproviderpluginqgc.h b/src/QtLocationPlugin/QGeoServiceProviderPluginQGC.h similarity index 96% rename from src/QtLocationPlugin/qgeoserviceproviderpluginqgc.h rename to src/QtLocationPlugin/QGeoServiceProviderPluginQGC.h index 49f598b2376a4d13c47fb432a3aca613dead44f1..4f1659f049f5bd1e21c12bf9c506c69f86ceb05c 100644 --- a/src/QtLocationPlugin/qgeoserviceproviderpluginqgc.h +++ b/src/QtLocationPlugin/QGeoServiceProviderPluginQGC.h @@ -44,8 +44,8 @@ ** ****************************************************************************/ -#ifndef QGEOSERVICEPROVIDER_GOOGLE_H -#define QGEOSERVICEPROVIDER_GOOGLE_H +#ifndef QGEOSERVICEPROVIDERQGC_H +#define QGEOSERVICEPROVIDERQGC_H #include #include @@ -64,4 +64,4 @@ public: QPlaceManagerEngine* createPlaceManagerEngine (const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const; }; -#endif // QGEOSERVICEPROVIDER_GOOGLE_H +#endif // QGEOSERVICEPROVIDERQGC_H diff --git a/src/QtLocationPlugin/QGeoTileFetcherQGC.cpp b/src/QtLocationPlugin/QGeoTileFetcherQGC.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c9e34958a23c4612d8f78b3d5541199ea30a25d9 --- /dev/null +++ b/src/QtLocationPlugin/QGeoTileFetcherQGC.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Aaron McCarthy +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +** 2015.4.4 +** Adapted for use with QGroundControl +** +** Gus Grubba +** +****************************************************************************/ + +#include +#include +#include + +#include "QGCMapEngine.h" +#include "QGeoTileFetcherQGC.h" +#include "QGeoMapReplyQGC.h" + +//----------------------------------------------------------------------------- +QGeoTileFetcherQGC::QGeoTileFetcherQGC(QGeoTiledMappingManagerEngine *parent) + : QGeoTileFetcher(parent) + , _networkManager(new QNetworkAccessManager(this)) +{ +} + +//----------------------------------------------------------------------------- +QGeoTileFetcherQGC::~QGeoTileFetcherQGC() +{ + +} + +//----------------------------------------------------------------------------- +QGeoTiledMapReply* +QGeoTileFetcherQGC::getTileImage(const QGeoTileSpec &spec) +{ + //-- Build URL + QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL((UrlFactory::MapType)spec.mapId(), spec.x(), spec.y(), spec.zoom(), _networkManager); + return new QGeoTiledMapReplyQGC(_networkManager, request, spec); +} diff --git a/src/QtLocationPlugin/qgeotilefetcherqgc.h b/src/QtLocationPlugin/QGeoTileFetcherQGC.h similarity index 82% rename from src/QtLocationPlugin/qgeotilefetcherqgc.h rename to src/QtLocationPlugin/QGeoTileFetcherQGC.h index 5f3dfab8df9ab1ff0a5ad681dfb5c39837e86b9a..846306351f68635f707f821fa3c5b8b282ef739c 100644 --- a/src/QtLocationPlugin/qgeotilefetcherqgc.h +++ b/src/QtLocationPlugin/QGeoTileFetcherQGC.h @@ -44,12 +44,12 @@ ** ****************************************************************************/ -#ifndef QGEOTILEFETCHERGOOGLE_H -#define QGEOTILEFETCHERGOOGLE_H +#ifndef QGEOTILEFETCHERQGC_H +#define QGEOTILEFETCHERQGC_H #include #include -#include "OpenPilotMaps.h" +#include "QGCMapUrlEngine.h" class QGeoTiledMappingManagerEngine; class QNetworkAccessManager; @@ -57,19 +57,13 @@ class QNetworkAccessManager; class QGeoTileFetcherQGC : public QGeoTileFetcher { Q_OBJECT - public: - explicit QGeoTileFetcherQGC(QGeoTiledMappingManagerEngine *parent = 0); + explicit QGeoTileFetcherQGC (QGeoTiledMappingManagerEngine *parent = 0); ~QGeoTileFetcherQGC(); - - void setUserAgent(const QByteArray &userAgent); - private: - QGeoTiledMapReply* getTileImage(const QGeoTileSpec &spec); - QNetworkAccessManager* m_networkManager; - QByteArray m_userAgent; - OpenPilot::UrlFactory* m_UrlFactory; - QString m_Language; + QGeoTiledMapReply* getTileImage (const QGeoTileSpec &spec); +private: + QNetworkAccessManager* _networkManager; }; -#endif // QGEOTILEFETCHERGOOGLE_H +#endif // QGEOTILEFETCHERQGC_H diff --git a/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c99ca9f78b00fc6bf395ab7fc7edc5466e6b7a3d --- /dev/null +++ b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Aaron McCarthy +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +** 2015.4.4 +** Adapted for use with QGroundControl +** +** Gus Grubba +** +****************************************************************************/ + +#include +#include +#if QT_VERSION < 0x050500 +#include +#else +#include +#endif +#include +#include + +#include "QGCMapEngine.h" +#include "QGeoTiledMappingManagerEngineQGC.h" +#include "QGeoTileFetcherQGC.h" + +#if QT_VERSION >= 0x050500 +//----------------------------------------------------------------------------- +QGeoTiledMapQGC::QGeoTiledMapQGC(QGeoTiledMappingManagerEngine *engine, QObject *parent) + : QGeoTiledMap(engine, parent) +{ + +} +#endif + +//----------------------------------------------------------------------------- +QGeoTiledMappingManagerEngineQGC::QGeoTiledMappingManagerEngineQGC(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) +: QGeoTiledMappingManagerEngine() +{ + + QGeoCameraCapabilities cameraCaps; + cameraCaps.setMinimumZoomLevel(2.0); + cameraCaps.setMaximumZoomLevel(MAX_MAP_ZOOM); + cameraCaps.setSupportsBearing(true); + setCameraCapabilities(cameraCaps); + + setTileSize(QSize(256, 256)); + + /* + * Most of these don't seem kosher at all. This was based on original code from OpenPilot and heavily modified to be used in QGC. + */ + + + //-- IMPORTANT + // Changes here must reflect those in QGCMapEngine.cpp + + QList mapTypes; + mapTypes << QGeoMapType(QGeoMapType::StreetMap, "Google Street Map", "Google street map", false, false, UrlFactory::GoogleMap); + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, "Google Satellite Map", "Google satellite map", false, false, UrlFactory::GoogleSatellite); + mapTypes << QGeoMapType(QGeoMapType::TerrainMap, "Google Terrain Map", "Google terrain map", false, false, UrlFactory::GoogleTerrain); + + /* TODO: + * Proper google hybrid maps requires collecting two separate bimaps and overlaying them. + * + * mapTypes << QGeoMapType(QGeoMapType::HybridMap, "Google Hybrid Map", "Google hybrid map", false, false, UrlFactory::GoogleHybrid); + * + */ + + // Bing + mapTypes << QGeoMapType(QGeoMapType::StreetMap, "Bing Street Map", "Bing street map", false, false, UrlFactory::BingMap); + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, "Bing Satellite Map", "Bing satellite map", false, false, UrlFactory::BingSatellite); + mapTypes << QGeoMapType(QGeoMapType::HybridMap, "Bing Hybrid Map", "Bing hybrid map", false, false, UrlFactory::BingHybrid); + + /* See: https://wiki.openstreetmap.org/wiki/Tile_usage_policy + mapTypes << QGeoMapType(QGeoMapType::StreetMap, "Open Street Map", "Open Street map", false, false, UrlFactory::OpenStreetMap); + */ + + // MapQuest + mapTypes << QGeoMapType(QGeoMapType::StreetMap, "MapQuest Street Map", "MapQuest street map", false, false, UrlFactory::MapQuestMap); + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, "MapQuest Satellite Map", "MapQuest satellite map", false, false, UrlFactory::MapQuestSat); + + /* + * These are OK as you need your own token for accessing it. Out-of-the box, QGC does not even offer these unless you enter a proper MapBox token. + */ + + mapTypes << QGeoMapType(QGeoMapType::StreetMap, "MapBox Street Map", "MapBox Street Map", false, false, UrlFactory::MapBoxStreets); + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, "MapBox Satellite Map", "MapBox Satellite Map", false, false, UrlFactory::MapBoxSatellite); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox High Contrast Map", "MapBox High Contrast Map", false, false, UrlFactory::MapBoxHighContrast); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Light Map", "MapBox Light Map", false, false, UrlFactory::MapBoxLight); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Dark Map", "MapBox Dark Map", false, false, UrlFactory::MapBoxDark); + mapTypes << QGeoMapType(QGeoMapType::HybridMap, "MapBox Hybrid Map", "MapBox Hybrid Map", false, false, UrlFactory::MapBoxHybrid); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Wheat Paste Map", "MapBox Wheat Paste Map", false, false, UrlFactory::MapBoxWheatPaste); + mapTypes << QGeoMapType(QGeoMapType::StreetMap, "MapBox Streets Basic Map", "MapBox Streets Basic Map", false, false, UrlFactory::MapBoxStreetsBasic); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Comic Map", "MapBox Comic Map", false, false, UrlFactory::MapBoxComic); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Outdoors Map", "MapBox Outdoors Map", false, false, UrlFactory::MapBoxOutdoors); + mapTypes << QGeoMapType(QGeoMapType::CycleMap, "MapBox Run, Byke and Hike Map", "MapBox Run, Byke and Hike Map", false, false, UrlFactory::MapBoxRunBikeHike); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Pencil Map", "MapBox Pencil Map", false, false, UrlFactory::MapBoxPencil); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Pirates Map", "MapBox Pirates Map", false, false, UrlFactory::MapBoxPirates); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, "MapBox Emerald Map", "MapBox Emerald Map", false, false, UrlFactory::MapBoxEmerald); + + setSupportedMapTypes(mapTypes); + + //-- Users (QML code) can define a different user agent + if (parameters.contains(QStringLiteral("useragent"))) { + getQGCMapEngine()->setUserAgent(parameters.value(QStringLiteral("useragent")).toString().toLatin1()); + } + +#if QT_VERSION >= 0x050500 + _setCache(parameters); +#endif + + setTileFetcher(new QGeoTileFetcherQGC(this)); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); + +#if QT_VERSION >= 0x050500 + if (parameters.contains(QStringLiteral("mapping.copyright"))) + m_customCopyright = parameters.value(QStringLiteral("mapping.copyright")).toString().toLatin1(); +#endif +} + +//----------------------------------------------------------------------------- +QGeoTiledMappingManagerEngineQGC::~QGeoTiledMappingManagerEngineQGC() +{ +} + +#if QT_VERSION < 0x050500 + +//----------------------------------------------------------------------------- +QGeoMapData *QGeoTiledMappingManagerEngineQGC::createMapData() +{ + return new QGeoTiledMapData(this, 0); +} + +#else + +//----------------------------------------------------------------------------- +QGeoMap* +QGeoTiledMappingManagerEngineQGC::createMap() +{ + return new QGeoTiledMapQGC(this); +} + +//----------------------------------------------------------------------------- +QString +QGeoTiledMappingManagerEngineQGC::customCopyright() const +{ + return m_customCopyright; +} + +#endif + +#if QT_VERSION >= 0x050500 +//----------------------------------------------------------------------------- +void +QGeoTiledMappingManagerEngineQGC::_setCache(const QVariantMap ¶meters) +{ + QString cacheDir; + if (parameters.contains(QStringLiteral("mapping.cache.directory"))) + cacheDir = parameters.value(QStringLiteral("mapping.cache.directory")).toString(); + else { + cacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/QGCMapCache55"); + if(!QDir::root().mkpath(cacheDir)) { + qWarning() << "Could not create mapping disk cache directory: " << cacheDir; + cacheDir = QDir::homePath() + QLatin1String("/.qgcmapscache/"); + } + } + if(!QFileInfo(cacheDir).exists()) { + if(!QDir::root().mkpath(cacheDir)) { + qWarning() << "Could not create mapping disk cache directory: " << cacheDir; + cacheDir.clear(); + } + } + //-- Memory Cache + uint32_t memLimit = 0; + if (parameters.contains(QStringLiteral("mapping.cache.memory.size"))) { + bool ok = false; + memLimit = parameters.value(QStringLiteral("mapping.cache.memory.size")).toString().toUInt(&ok); + if (!ok) + memLimit = 0; + } + if(!memLimit) + { + //-- Value saved in MB + memLimit = getQGCMapEngine()->getMaxMemCache() * (1024 * 1024); + } + //-- It won't work with less than 1M of memory cache + if(memLimit < 1024 * 1024) + memLimit = 1024 * 1024; + //-- Disable Qt's disk cache (set memory cache otherwise Qtlocation won't work) + QGeoTileCache* pTileCache = createTileCacheWithDir(cacheDir); + if(pTileCache) + { + pTileCache->setMaxDiskUsage(1); + pTileCache->setMaxMemoryUsage(memLimit); + } +} +#endif diff --git a/src/QtLocationPlugin/qgeotiledmappingmanagerengineqgc.h b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.h similarity index 94% rename from src/QtLocationPlugin/qgeotiledmappingmanagerengineqgc.h rename to src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.h index 6bc8447d9202049be783121d93bb957e9968d05d..2d6d1dc63401d92a1f92bc90eee32ec53627496b 100644 --- a/src/QtLocationPlugin/qgeotiledmappingmanagerengineqgc.h +++ b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.h @@ -44,8 +44,8 @@ ** ****************************************************************************/ -#ifndef QGEOTILEDMAPPINGMANAGERENGINEGOOGLE_H -#define QGEOTILEDMAPPINGMANAGERENGINEGOOGLE_H +#ifndef QGEOTILEDMAPPINGMANAGERENGINEQGC_H +#define QGEOTILEDMAPPINGMANAGERENGINEQGC_H #include #if QT_VERSION >= 0x050500 @@ -62,6 +62,8 @@ public: }; #endif +class QGeoTileFetcherQGC; + class QGeoTiledMappingManagerEngineQGC : public QGeoTiledMappingManagerEngine { Q_OBJECT @@ -81,4 +83,4 @@ private: #endif }; -#endif // QGEOTILEDMAPPINGMANAGERENGINEGOOGLE_H +#endif // QGEOTILEDMAPPINGMANAGERENGINEQGC_H diff --git a/src/QtLocationPlugin/QMLControl/OfflineMap.qml b/src/QtLocationPlugin/QMLControl/OfflineMap.qml new file mode 100644 index 0000000000000000000000000000000000000000..70480a866ad815103ba8f8d6cca8e1eab8c3c729 --- /dev/null +++ b/src/QtLocationPlugin/QMLControl/OfflineMap.qml @@ -0,0 +1,902 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 +import QtQuick.Dialogs 1.1 +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 + +Rectangle { + id: _offlineMapRoot + color: __qgcPal.window + anchors.fill: parent + anchors.margins: ScreenTools.defaultFontPixelWidth + + property var _currentSelection: null + + property string mapKey: "lastMapType" + + property string mapType: QGroundControl.mapEngineManager.loadSetting(mapKey, "Google Street Map") + property int mapMargin: (ScreenTools.defaultFontPixelHeight * 0.2).toFixed(0) + property real infoWidth: Math.max(Math.max(nameLabel.width, descLabel.width), (ScreenTools.defaultFontPixelWidth * 40)) + property bool isDefaultSet: _offlineMapRoot._currentSelection && _offlineMapRoot._currentSelection.defaultSet + + property real oldlon0: 0 + property real oldlon1: 0 + property real oldlat0: 0 + property real oldlat1: 0 + property int oldz0: 0 + property int oldz1: 0 + + Component.onCompleted: { + QGroundControl.mapEngineManager.loadTileSets() + updateMap() + } + + Connections { + target: QGroundControl.mapEngineManager + onTileSetsChanged: { + setName.text = QGroundControl.mapEngineManager.getUniqueName() + } + onErrorMessageChanged: { + errorDialog.visible = true + } + } + + ExclusiveGroup { id: setGroup } + + function handleChanges() { + var xl = mapMargin + var yl = mapMargin + var xr = _map.width.toFixed(0) - mapMargin + var yr = _map.height.toFixed(0) - mapMargin + var c0 = _map.toCoordinate(Qt.point(xl, yl)) + var c1 = _map.toCoordinate(Qt.point(xr, yr)) + if(oldlon0 !== c0.longitude || oldlat0 !== c0.latitude || oldlon1 !== c1.longitude || oldlat1 !== c1.latitude || oldz0 !== _slider0.value || oldz1 !== _slider1.value) { + QGroundControl.mapEngineManager.updateForCurrentView(c0.longitude, c0.latitude, c1.longitude, c1.latitude, _slider0.value, _slider1.value, mapType) + } + } + + function checkSanity() { + if(QGroundControl.mapEngineManager.crazySize) { + _slider1.value = _slider1.value - 1 + handleChanges() + } + } + + function updateMap() { + for (var i = 0; i < _map.supportedMapTypes.length; i++) { + if (mapType === _map.supportedMapTypes[i].name) { + _map.activeMapType = _map.supportedMapTypes[i] + handleChanges() + return + } + } + } + + function showOptions() { + _tileSetList.visible = false + _infoView.visible = false + _mapView.visible = false + _optionsView.visible = true + } + + function showMap() { + _tileSetList.visible = false + _infoView.visible = false + _mapView.visible = true + _optionsView.visible = false + } + + function showList() { + _tileSetList.visible = true + _infoView.visible = false + _mapView.visible = false + _optionsView.visible = false + } + + function showInfo() { + if(_currentSelection && !_offlineMapRoot._currentSelection.deleting) { + _tileSetList.visible = false + _mapView.visible = false + _infoView.visible = true + _optionsView.visible = false + } else + showList() + } + + ExclusiveGroup { + id: _dropButtonsExclusiveGroup + } + + onMapTypeChanged: { + updateMap() + QGroundControl.mapEngineManager.saveSetting(mapKey, mapType) + } + + MessageDialog { + id: errorDialog + visible: false + text: QGroundControl.mapEngineManager.errorMessage + icon: StandardIcon.Critical + standardButtons: StandardButton.Ok + title: "Errror Message" + onYes: { + errorDialog.visible = false + } + } + Rectangle { + id: _offlineMapTopRect + width: parent.width + height: labelTitle.height + ScreenTools.defaultFontPixelHeight + color: __qgcPal.window + anchors.top: parent.top + Row { + spacing: ScreenTools.defaultFontPixelHeight * 2 + anchors.verticalCenter: parent.verticalCenter + QGCLabel { + id: labelTitle + text: "Offline Maps" + font.pixelSize: ScreenTools.mediumFontPixelSize + anchors.verticalCenter: parent.verticalCenter + } + QGCCheckBox { + id: showTilePreview + text: "Show tile min/max zoom level preview" + checked: false + visible: _mapView.visible + anchors.verticalCenter: parent.verticalCenter + } + } + } + QGCFlickable { + id: _tileSetList + clip: true + anchors.top: _offlineMapTopRect.bottom + width: parent.width + height: parent.height - _offlineMapTopRect.height + contentHeight: _cacheList.height + contentWidth: parent.width + flickableDirection: Flickable.VerticalFlick + Column { + id: _cacheList + width: _offlineMapRoot.width + anchors.margins: ScreenTools.defaultFontPixelWidth + spacing: (ScreenTools.defaultFontPixelHeight * 0.5).toFixed(0) + OfflineMapButton { + text: "Add new set" + width: (ScreenTools.defaultFontPixelWidth * 50).toFixed(0) + height: (ScreenTools.defaultFontPixelHeight * 2).toFixed(0) + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + _offlineMapRoot._currentSelection = null + showMap() + } + } + Repeater { + model: QGroundControl.mapEngineManager.tileSets + delegate: OfflineMapButton { + text: object.name + size: object.downloadStatus + complete: object.complete + width: (ScreenTools.defaultFontPixelWidth * 50).toFixed(0) + height: (ScreenTools.defaultFontPixelHeight * 2).toFixed(0) + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + _offlineMapRoot._currentSelection = object + showInfo() + } + } + } + } + } + QGCButton { + id: _optionsButton + text: "Options" + width: ScreenTools.defaultFontPixelWidth * 10 + visible: _tileSetList.visible + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: ScreenTools.defaultFontPixelWidth + onClicked: showOptions() + } + //-- Offline Map Definition + Rectangle { + id: _mapView + color: __qgcPal.window + width: parent.width + anchors.top: _offlineMapTopRect.bottom + anchors.bottom: parent.bottom + anchors.margins: ScreenTools.defaultFontPixelWidth + visible: false + + Rectangle { + width: parent.width + anchors.top: parent.top + anchors.bottom: bottomRect.top + color: (__qgcPal.globalTheme === QGCPalette.Light) ? "black" : "#98aca4" + Map { + id: _map + anchors.fill: parent + anchors.margins: ScreenTools.defaultFontPixelHeight * 0.15 + zoomLevel: 10 + center: QGroundControl.defaultMapPosition + gesture.flickDeceleration: 3000 + gesture.activeGestures: MapGestureArea.ZoomGesture | MapGestureArea.PanGesture | MapGestureArea.FlickGesture + plugin: Plugin { name: "QGroundControl" } + onCenterChanged: { + handleChanges() + checkSanity() + } + onZoomLevelChanged: { + handleChanges() + checkSanity() + } + onWidthChanged: { + handleChanges() + checkSanity() + } + onHeightChanged: { + handleChanges() + checkSanity() + } + } + Rectangle { + width: ScreenTools.defaultFontPixelHeight * 16 + height: ScreenTools.defaultFontPixelHeight * 9 + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: ScreenTools.defaultFontPixelHeight + color: "black" + visible: showTilePreview.checked + Map { + id: _mapMin + anchors.fill: parent + anchors.margins: 2 + zoomLevel: _slider0.value + center: _map.center + gesture.enabled: false + activeMapType: _map.activeMapType + plugin: Plugin { name: "QGroundControl" } + } + } + Rectangle { + width: ScreenTools.defaultFontPixelHeight * 16 + height: ScreenTools.defaultFontPixelHeight * 9 + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: ScreenTools.defaultFontPixelHeight + color: "black" + visible: showTilePreview.checked + Map { + id: _mapMax + anchors.fill: parent + anchors.margins: 2 + zoomLevel: _slider1.value + center: _map.center + gesture.enabled: false + activeMapType: _map.activeMapType + plugin: Plugin { name: "QGroundControl" } + } + } + } + Rectangle { + id: bottomRect + width: parent.width + height: _controlRow.height + (ScreenTools.defaultFontPixelHeight * 2) + color: __qgcPal.window + anchors.bottom: parent.bottom + Row { + id: _controlRow + anchors.centerIn: parent + spacing: ScreenTools.defaultFontPixelWidth * 2 + Rectangle { + height: _zoomRow.height + ScreenTools.defaultFontPixelHeight * 1.5 + width: _zoomRow.width + ScreenTools.defaultFontPixelWidth + color: "#98aca4" + border.color: "black" + border.width: 2 + radius: ScreenTools.defaultFontPixelWidth * 0.5 + anchors.verticalCenter: parent.verticalCenter + Row { + id: _zoomRow + anchors.centerIn: parent + Column { + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.verticalCenter: parent.verticalCenter + Row { + spacing: ScreenTools.defaultFontPixelWidth * 0.5 + Column { + anchors.verticalCenter: parent.verticalCenter + Label { + text: "Min" + color: "black" + width: ScreenTools.defaultFontPixelWidth * 5 + font.pixelSize: ScreenTools.smallFontPixelSize + horizontalAlignment: Text.AlignHCenter + } + Label { + text: "Zoom" + color: "black" + width: ScreenTools.defaultFontPixelWidth * 5 + font.pixelSize: ScreenTools.smallFontPixelSize + horizontalAlignment: Text.AlignHCenter + } + } + Slider { + id: _slider0 + minimumValue: 3 + maximumValue: 18 + stepSize: 1 + tickmarksEnabled: false + orientation: Qt.Horizontal + updateValueWhileDragging: true + anchors.verticalCenter: parent.verticalCenter + style: SliderStyle { + groove: Rectangle { + implicitWidth: ScreenTools.defaultFontPixelWidth * 16 + implicitHeight: 4 + color: "gray" + radius: 4 + } + handle: Rectangle { + anchors.centerIn: parent + color: control.pressed ? "white" : "lightgray" + border.color: "gray" + border.width: 2 + implicitWidth: 30 + implicitHeight: 30 + radius: 10 + Label { + text: _slider0.value + anchors.centerIn: parent + } + } + } + Component.onCompleted: { + _slider0.value = _map.zoomLevel - 2 + } + onValueChanged: { + if(_slider1) { + if(_slider0.value > _slider1.value) + _slider1.value = _slider0.value + else { + handleChanges() + checkSanity() + } + } + } + } + } + Row { + spacing: ScreenTools.defaultFontPixelWidth * 0.5 + Column { + anchors.verticalCenter: parent.verticalCenter + Label { + text: "Max" + color: "black" + width: ScreenTools.defaultFontPixelWidth * 5 + font.pixelSize: ScreenTools.smallFontPixelSize + horizontalAlignment: Text.AlignHCenter + } + Label { + text: "Zoom" + color: "black" + width: ScreenTools.defaultFontPixelWidth * 5 + font.pixelSize: ScreenTools.smallFontPixelSize + horizontalAlignment: Text.AlignHCenter + } + } + Slider { + id: _slider1 + minimumValue: 3 + maximumValue: 18 + stepSize: 1 + tickmarksEnabled: false + orientation: Qt.Horizontal + updateValueWhileDragging: true + anchors.verticalCenter: parent.verticalCenter + style: SliderStyle { + groove: Rectangle { + implicitWidth: ScreenTools.defaultFontPixelWidth * 16 + implicitHeight: 4 + color: "gray" + radius: 4 + } + handle: Rectangle { + anchors.centerIn: parent + color: control.pressed ? "white" : "lightgray" + border.color: "gray" + border.width: 2 + implicitWidth: 30 + implicitHeight: 30 + radius: 10 + Label { + text: _slider1.value + anchors.centerIn: parent + } + } + } + Component.onCompleted: { + _slider1.value = _map.zoomLevel + 2 + } + onValueChanged: { + if(_slider1.value < _slider0.value) + _slider0.value = _slider1.value + else { + handleChanges() + checkSanity() + } + } + } + } + } + Column { + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.verticalCenter: parent.verticalCenter + Label { + text: "Tile Count" + color: "black" + width: ScreenTools.defaultFontPixelWidth * 12 + font.pixelSize: ScreenTools.smallFontPixelSize + horizontalAlignment: Text.AlignHCenter + } + Label { + text: QGroundControl.mapEngineManager.tileCountStr + color: "black" + width: ScreenTools.defaultFontPixelWidth * 12 + horizontalAlignment: Text.AlignHCenter + } + Label { + text: "Set Size (Est)" + color: "black" + width: ScreenTools.defaultFontPixelWidth * 12 + font.pixelSize: ScreenTools.smallFontPixelSize + horizontalAlignment: Text.AlignHCenter + } + Label { + text: QGroundControl.mapEngineManager.tileSizeStr + color: "black" + width: ScreenTools.defaultFontPixelWidth * 12 + horizontalAlignment: Text.AlignHCenter + } + } + } + } + Column { + anchors.verticalCenter: parent.verticalCenter + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + Row { + spacing: ScreenTools.defaultFontPixelWidth * 2 + QGCLabel { + text: "Name:" + width: ScreenTools.defaultFontPixelWidth * 10 + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignRight + } + QGCTextField { + id: setName + width: ScreenTools.defaultFontPixelWidth * 30 + anchors.verticalCenter: parent.verticalCenter + } + } + Row { + spacing: ScreenTools.defaultFontPixelWidth * 2 + QGCLabel { + text: "Description:" + width: ScreenTools.defaultFontPixelWidth * 10 + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignRight + } + QGCTextField { + id: setDescription + text: "Description" + width: ScreenTools.defaultFontPixelWidth * 30 + anchors.verticalCenter: parent.verticalCenter + } + } + Row { + spacing: ScreenTools.defaultFontPixelWidth * 2 + QGCLabel { + text: "Map Type:" + width: ScreenTools.defaultFontPixelWidth * 10 + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignRight + } + QGCComboBox { + id: mapCombo + width: ScreenTools.defaultFontPixelWidth * 30 + model: QGroundControl.mapEngineManager.mapList + onActivated: { + mapType = textAt(index) + if(_dropButtonsExclusiveGroup.current) + _dropButtonsExclusiveGroup.current.checked = false + _dropButtonsExclusiveGroup.current = null + } + Component.onCompleted: { + var index = mapCombo.find(mapType) + if (index === -1) { + console.warn("Active map name not in combo", mapType) + } else { + mapCombo.currentIndex = index + } + } + } + } + } + Item { + height: 1 + width: ScreenTools.defaultFontPixelWidth * 2 + } + Column { + anchors.verticalCenter: parent.verticalCenter + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + QGCButton { + text: "Download" + enabled: setName.text.length > 0 + width: ScreenTools.defaultFontPixelWidth * 10 + onClicked: { + if(QGroundControl.mapEngineManager.findName(setName.text)) { + duplicateName.visible = true + } else { + /* Broken in Qt 5.5.1 + var mapImage + _map.grabToImage(function(result) { mapImage = result; }) + QGroundControl.mapEngineManager.startDownload(setName.text, setDescription.text, mapType, mapImage); + */ + QGroundControl.mapEngineManager.startDownload(setName.text, setDescription.text, mapType); + showList() + } + } + } + QGCButton { + text: "Cancel" + width: ScreenTools.defaultFontPixelWidth * 10 + onClicked: { + showList() + } + } + MessageDialog { + id: duplicateName + visible: false + icon: StandardIcon.Warning + standardButtons: StandardButton.Ok + title: "Tile Set Already Exists" + text: "Tile Set \"" + setName.text + "\" already exists.\nPlease select a different name." + onYes: { + duplicateName.visible = false + } + } + } + } + } + } + Rectangle { + id: _infoView + color: __qgcPal.windowShade + width: parent.width + anchors.top: _offlineMapTopRect.bottom + anchors.bottom: parent.bottom + anchors.margins: ScreenTools.defaultFontPixelWidth + visible: false + + Column { + width: parent.width + spacing: ScreenTools.defaultFontPixelHeight + Item { + height: ScreenTools.defaultFontPixelHeight + width: 1 + } + Rectangle { + width: infoWidth + height: nameLabel.height + (ScreenTools.defaultFontPixelHeight * 2) + color: __qgcPal.window + radius: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + id: nameLabel + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.name : "" + font.pixelSize: ScreenTools.largeFontPixelSize + anchors.centerIn: parent + } + } + QGCLabel { + id: descLabel + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.description : "" + anchors.horizontalCenter: parent.horizontalCenter + } + Rectangle { + id: infoRect + width: infoWidth + height: infoGrid.height + (ScreenTools.defaultFontPixelHeight * 4) + color: __qgcPal.window + radius: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + GridLayout { + id: infoGrid + columns: 2 + anchors.centerIn: parent + anchors.margins: ScreenTools.defaultFontPixelWidth * 2 + rowSpacing: ScreenTools.defaultFontPixelWidth + columnSpacing: ScreenTools.defaultFontPixelHeight * 2 + QGCLabel { + text: "Map Type:" + visible: !isDefaultSet + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.mapTypeStr : "" + visible: !isDefaultSet + } + QGCLabel { + text: "Min Zoom:" + visible: !isDefaultSet + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.minZoom : "" + visible: !isDefaultSet + } + QGCLabel { + text: "Max Zoom:" + visible: !isDefaultSet + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.maxZoom : "" + visible: !isDefaultSet + } + QGCLabel { + text: isDefaultSet ? "Default Set Size:" : "Total Size:" + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.tilesSizeStr : "" + } + QGCLabel { + text: isDefaultSet ? "Default Set Tile Count:" : "Total Tile Count:" + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.numTilesStr : "" + } + QGCLabel { + text: isDefaultSet ? "Total Size:" : "Downloaded Size:" + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.savedSizeStr : "" + } + QGCLabel { + text: isDefaultSet ? "Total Count:" : "Downloaded Count:" + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.savedTilesStr : "" + } + QGCLabel { + text: "Error Count:" + visible: !isDefaultSet && _offlineMapRoot._currentSelection && !_offlineMapRoot._currentSelection.complete + } + QGCLabel { + text: _offlineMapRoot._currentSelection ? _offlineMapRoot._currentSelection.errorCountStr : "" + visible: !isDefaultSet && _offlineMapRoot._currentSelection && !_offlineMapRoot._currentSelection.complete + } + } + } + Item { + height: ScreenTools.defaultFontPixelHeight + width: 1 + } + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: ScreenTools.defaultFontPixelWidth + QGCButton { + text: "Back" + width: ScreenTools.defaultFontPixelWidth * 18 + onClicked: showList() + } + QGCButton { + width: ScreenTools.defaultFontPixelWidth * 18 + text: "Delete" + enabled: _offlineMapRoot._currentSelection && (!_offlineMapRoot._currentSelection.deleting) + onClicked: { + if(_offlineMapRoot._currentSelection) + deleteDialog.visible = true + } + MessageDialog { + id: deleteDialog + visible: false + icon: StandardIcon.Warning + standardButtons: StandardButton.Yes | StandardButton.No + title: "Delete Tile Set" + text: { + if(_offlineMapRoot._currentSelection) { + var blurb = "Delete " + _offlineMapRoot._currentSelection.name + " and all its tiles.\nIs this really what you want?" + if(_offlineMapRoot._currentSelection.defaultSet) + return blurb + "\nNote that deleteting the Default Set deletes all tiles from all sets." + else + return blurb + } + return "" + } + onYes: { + if(_offlineMapRoot._currentSelection) + QGroundControl.mapEngineManager.deleteTileSet(_offlineMapRoot._currentSelection) + deleteDialog.visible = false + showList() + } + onNo: { + deleteDialog.visible = false + } + } + } + QGCButton { + text: "Resume Download" + width: ScreenTools.defaultFontPixelWidth * 18 + enabled: _offlineMapRoot._currentSelection && (!_offlineMapRoot._currentSelection.deleting && !_offlineMapRoot._currentSelection.downloading) + visible: !isDefaultSet && _offlineMapRoot._currentSelection && (!_offlineMapRoot._currentSelection.complete && !_offlineMapRoot._currentSelection.downloading) + onClicked: { + if(_offlineMapRoot._currentSelection) + _offlineMapRoot._currentSelection.resumeDownloadTask() + } + } + QGCButton { + text: "Cancel Download" + width: ScreenTools.defaultFontPixelWidth * 18 + enabled: _offlineMapRoot._currentSelection && (!_offlineMapRoot._currentSelection.deleting && _offlineMapRoot._currentSelection.downloading) + visible: !isDefaultSet && _offlineMapRoot._currentSelection && (!_offlineMapRoot._currentSelection.complete && _offlineMapRoot._currentSelection.downloading) + onClicked: { + if(_offlineMapRoot._currentSelection) + _offlineMapRoot._currentSelection.cancelDownloadTask() + } + } + } + } + } + Rectangle { + id: _optionsView + color: __qgcPal.windowShade + width: parent.width + anchors.top: _offlineMapTopRect.bottom + anchors.bottom: parent.bottom + anchors.margins: ScreenTools.defaultFontPixelWidth + visible: false + onVisibleChanged: { + if(_optionsView.visible) { + mapBoxToken.text = QGroundControl.mapEngineManager.mapboxToken + maxCacheSize.text = QGroundControl.mapEngineManager.maxDiskCache + maxCacheMemSize.text = QGroundControl.mapEngineManager.maxDiskCache + } + } + Column { + width: parent.width + spacing: ScreenTools.defaultFontPixelHeight + Item { + height: ScreenTools.defaultFontPixelHeight + width: 1 + } + Rectangle { + width: infoWidth + height: optionsLabel.height + (ScreenTools.defaultFontPixelHeight * 2) + color: __qgcPal.window + radius: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + id: optionsLabel + text: "Offline Map Options" + font.pixelSize: ScreenTools.largeFontPixelSize + anchors.centerIn: parent + } + } + Rectangle { + id: optionsRect + width: optionsGrid.width + (ScreenTools.defaultFontPixelWidth * 4) + height: optionsGrid.height + (ScreenTools.defaultFontPixelHeight * 4) + color: __qgcPal.window + radius: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + GridLayout { + id: optionsGrid + columns: 2 + anchors.centerIn: parent + anchors.margins: ScreenTools.defaultFontPixelWidth * 2 + rowSpacing: ScreenTools.defaultFontPixelWidth * 1.5 + columnSpacing: ScreenTools.defaultFontPixelHeight * 2 + QGCLabel { + text: "Max Cache Disk Size (MB):" + } + QGCTextField { + id: maxCacheSize + maximumLength: 256 + inputMethodHints: Qt.ImhDigitsOnly + validator: IntValidator {bottom: 1; top: 262144;} + } + QGCLabel { + text: "Max Cache Memory Size (MB):" + } + QGCTextField { + id: maxCacheMemSize + maximumLength: 256 + inputMethodHints: Qt.ImhDigitsOnly + validator: IntValidator {bottom: 1; top: 4096;} + } + Item { + Layout.columnSpan: 2 + Layout.fillWidth: true + implicitHeight: ScreenTools.defaultFontPixelHeight * 1.5 + QGCLabel { + anchors.centerIn: parent + text: "Memory cache changes require a restart to take effect." + font.pixelSize: ScreenTools.defaultFontPixelSize * 0.85 + } + } + Rectangle { + Layout.columnSpan: 2 + Layout.fillWidth: true + implicitHeight: 1 + color: __qgcPal.text + } + QGCLabel { + text: "MapBox Access Token" + } + QGCTextField { + id: mapBoxToken + Layout.fillWidth: true + maximumLength: 256 + implicitWidth : ScreenTools.defaultFontPixelHeight * 30 + } + Item { + Layout.columnSpan: 2 + Layout.fillWidth: true + implicitHeight: ScreenTools.defaultFontPixelHeight * 1.5 + QGCLabel { + anchors.centerIn: parent + text: "With an access token, you can use MapBox Maps." + font.pixelSize: ScreenTools.defaultFontPixelSize * 0.85 + } + } + } + } + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: ScreenTools.defaultFontPixelWidth + QGCButton { + text: "Save" + width: ScreenTools.defaultFontPixelWidth * 18 + onClicked: { + showList() + } + } + QGCButton { + text: "Cancel" + width: ScreenTools.defaultFontPixelWidth * 18 + onClicked: { + showList() + } + } + } + } + } +} diff --git a/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc new file mode 100644 index 0000000000000000000000000000000000000000..c8a79cbcb4ca44d8366b4e55d5db1a5ed3eff391 --- /dev/null +++ b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc @@ -0,0 +1,418 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2016 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @author Gus Grubba + +#include "QGCMapEngineManager.h" +#include "QGCApplication.h" +#include +#include "QGCMapTileSet.h" +#include "QGCMapUrlEngine.h" +#include + +QGC_LOGGING_CATEGORY(QGCMapEngineManagerLog, "QGCMapEngineManagerLog") + +static const char* kQmlOfflineMapKeyName = "QGCOfflineMap"; + +//----------------------------------------------------------------------------- +QGCMapEngineManager::QGCMapEngineManager(QGCApplication* app) + : QGCTool(app) + , _topleftLat(0.0) + , _topleftLon(0.0) + , _bottomRightLat(0.0) + , _bottomRightLon(0.0) + , _minZoom(0) + , _maxZoom(0) + , _setID(UINT64_MAX) + , _freeDiskSpace(0) + , _diskSpace(0) +{ + +} + +//----------------------------------------------------------------------------- +QGCMapEngineManager::~QGCMapEngineManager() +{ + //_clearTileSets(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::setToolbox(QGCToolbox *toolbox) +{ + QGCTool::setToolbox(toolbox); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.QGCMapEngineManager", 1, 0, "QGCMapEngineManager", "Reference only"); + connect(getQGCMapEngine(), &QGCMapEngine::updateTotals, this, &QGCMapEngineManager::_updateTotals); + _updateDiskFreeSpace(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::updateForCurrentView(double lon0, double lat0, double lon1, double lat1, int minZoom, int maxZoom, const QString& mapName) +{ + UrlFactory::MapType mapType = QGCMapEngine::getTypeFromName(mapName); + + _topleftLat = lat0; + _topleftLon = lon0; + _bottomRightLat = lat1; + _bottomRightLon = lon1; + _minZoom = minZoom; + _maxZoom = maxZoom; + _totalSet.clear(); + for(int z = minZoom; z <= maxZoom; z++) { + QGCTileSet set = QGCMapEngine::getTileCount(z, lon0, lat0, lon1, lat1, mapType); + _totalSet += set; + } + //-- Beyond 100,000,000 tiles is just nuts + if(_totalSet.tileCount > 100 * 1000 * 1000) { + _crazySize = true; + emit crazySizeChanged(); + } else { + _crazySize = false; + emit crazySizeChanged(); + emit tileX0Changed(); + emit tileX1Changed(); + emit tileY0Changed(); + emit tileY1Changed(); + emit tileCountChanged(); + emit tileSizeChanged(); + } +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngineManager::tileCountStr() +{ + return QGCMapEngine::numberToString(_totalSet.tileCount); +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngineManager::tileSizeStr() +{ + return QGCMapEngine::bigSizeToString(_totalSet.tileSize); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::loadTileSets() +{ + if(_tileSets.count()) { + _clearTileSets(); + emit tileSetsChanged(); + } + QGCFetchTileSetTask* task = new QGCFetchTileSetTask(); + connect(task, &QGCFetchTileSetTask::tileSetFetched, this, &QGCMapEngineManager::_tileSetFetched); + connect(task, &QGCMapTask::error, this, &QGCMapEngineManager::taskError); + getQGCMapEngine()->addTask(task); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::_tileSetFetched(QGCCachedTileSet* tileSet) +{ + //-- A blank (default) type means it uses various types and not just one + if(tileSet->type() == UrlFactory::Invalid) { + tileSet->setMapTypeStr("Various"); + } + _tileSets.append(tileSet); + tileSet->setManager(this); + emit tileSetsChanged(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::_clearTileSets() +{ + while(_tileSets.count()) { + QGCCachedTileSet* tileSet = qobject_cast(_tileSets[0]); + if(tileSet) + delete tileSet; + _tileSets.removeAt(0); + } +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::startDownload(const QString& name, const QString& description, const QString& mapType, const QImage& image) +{ + if(_totalSet.tileSize) { + QGCCachedTileSet* set = new QGCCachedTileSet(name, description); + set->setMapTypeStr(mapType); + set->setTopleftLat(_topleftLat); + set->setTopleftLon(_topleftLon); + set->setBottomRightLat(_bottomRightLat); + set->setBottomRightLon(_bottomRightLon); + set->setMinZoom(_minZoom); + set->setMaxZoom(_maxZoom); + set->setTilesSize(_totalSet.tileSize); + set->setNumTiles(_totalSet.tileCount); + set->setType(QGCMapEngine::getTypeFromName(mapType)); + if(!image.isNull()) + set->setThumbNail(image); + QGCCreateTileSetTask* task = new QGCCreateTileSetTask(set); + //-- Create Tile Set (it will also create a list of tiles to download) + connect(task, &QGCCreateTileSetTask::tileSetSaved, this, &QGCMapEngineManager::_tileSetSaved); + connect(task, &QGCMapTask::error, this, &QGCMapEngineManager::taskError); + getQGCMapEngine()->addTask(task); + } else { + qWarning() << "QGCMapEngineManager::startDownload() No Tiles to save"; + } +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::_tileSetSaved(QGCCachedTileSet *set) +{ + qCDebug(QGCMapEngineManagerLog) << "New tile set saved (" << set->name() << "). Starting download..."; + _tileSets.append(set); + emit tileSetsChanged(); + //-- Start downloading tiles + set->createDownloadTask(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::saveSetting (const QString& key, const QString& value) +{ + QSettings settings; + settings.beginGroup(kQmlOfflineMapKeyName); + settings.setValue(key, value); +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngineManager::loadSetting (const QString& key, const QString& defaultValue) +{ + QSettings settings; + settings.beginGroup(kQmlOfflineMapKeyName); + return settings.value(key, defaultValue).toString(); +} + +//----------------------------------------------------------------------------- +QStringList +QGCMapEngineManager::mapList() +{ + return getQGCMapEngine()->getMapNameList(); +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngineManager::mapboxToken() +{ + return getQGCMapEngine()->getMapBoxToken(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::setMapboxToken(QString token) +{ + getQGCMapEngine()->setMapBoxToken(token); +} + +//----------------------------------------------------------------------------- +quint32 +QGCMapEngineManager::maxMemCache() +{ + return getQGCMapEngine()->getMaxMemCache(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::setMaxMemCache(quint32 size) +{ + getQGCMapEngine()->setMaxMemCache(size); +} + +//----------------------------------------------------------------------------- +quint32 +QGCMapEngineManager::maxDiskCache() +{ + return getQGCMapEngine()->getMaxDiskCache(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::setMaxDiskCache(quint32 size) +{ + getQGCMapEngine()->setMaxDiskCache(size); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::deleteTileSet(QGCCachedTileSet* tileSet) +{ + qCDebug(QGCMapEngineManagerLog) << "Deleting tile set " << tileSet->name(); + //-- If deleting default set, delete it all + if(tileSet->defaultSet()) { + for(int i = 0; i < _tileSets.count(); i++ ) { + QGCCachedTileSet* set = qobject_cast(_tileSets.get(i)); + Q_ASSERT(set); + set->setDeleting(true); + } + QGCResetTask* task = new QGCResetTask(); + connect(task, &QGCResetTask::resetCompleted, this, &QGCMapEngineManager::_resetCompleted); + connect(task, &QGCMapTask::error, this, &QGCMapEngineManager::taskError); + getQGCMapEngine()->addTask(task); + } else { + tileSet->setDeleting(true); + QGCDeleteTileSetTask* task = new QGCDeleteTileSetTask(tileSet->setID()); + connect(task, &QGCDeleteTileSetTask::tileSetDeleted, this, &QGCMapEngineManager::_tileSetDeleted); + connect(task, &QGCMapTask::error, this, &QGCMapEngineManager::taskError); + getQGCMapEngine()->addTask(task); + } +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::_resetCompleted() +{ + //-- Reload sets + loadTileSets(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::_tileSetDeleted(quint64 setID) +{ + //-- Tile Set successfully deleted + QGCCachedTileSet* setToDelete = NULL; + int i = 0; + for(i = 0; i < _tileSets.count(); i++ ) { + QGCCachedTileSet* set = qobject_cast(_tileSets.get(i)); + Q_ASSERT(set); + if (set->setID() == setID) { + setToDelete = set; + break; + } + } + if(setToDelete) { + _tileSets.removeAt(i); + delete setToDelete; + } + emit tileSetsChanged(); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::taskError(QGCMapTask::TaskType type, QString error) +{ + QString task; + switch(type) { + case QGCMapTask::taskFetchTileSets: + task = "Fetch Tile Set"; + break; + case QGCMapTask::taskCreateTileSet: + task = "Create Tile Set"; + break; + case QGCMapTask::taskGetTileDownloadList: + task = "Get Tile Download List"; + break; + case QGCMapTask::taskUpdateTileDownloadState: + task = "Update Tile Download Status"; + break; + case QGCMapTask::taskDeleteTileSet: + task = "Delete Tile Set"; + break; + case QGCMapTask::taskReset: + task = "Reset Tile Sets"; + break; + default: + task = "Database Error"; + break; + } + QString serror = "Error in task: " + task; + serror += "\nError description:\n"; + serror += error; + qWarning() << "QGCMapEngineManager::_taskError()"; + setErrorMessage(serror); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::_updateTotals(quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize) +{ + for(int i = 0; i < _tileSets.count(); i++ ) { + QGCCachedTileSet* set = qobject_cast(_tileSets.get(i)); + Q_ASSERT(set); + if (set->defaultSet()) { + set->setSavedSize(totalsize); + set->setSavedTiles(totaltiles); + set->setNumTiles(defaulttiles); + set->setTilesSize(defaultsize); + return; + } + } + _updateDiskFreeSpace(); +} + +//----------------------------------------------------------------------------- +bool +QGCMapEngineManager::findName(const QString& name) +{ + for(int i = 0; i < _tileSets.count(); i++ ) { + QGCCachedTileSet* set = qobject_cast(_tileSets.get(i)); + Q_ASSERT(set); + if (set->name() == name) { + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +QString +QGCMapEngineManager::getUniqueName() +{ + QString test = "Tile Set "; + QString name; + int count = 1; + while (true) { + char numb[16]; + snprintf(numb, sizeof(numb), "%03d", count++); + name = test; + name += numb; + if(!findName(name)) + return name; + } +} + +//----------------------------------------------------------------------------- +void +QGCMapEngineManager::_updateDiskFreeSpace() +{ + QString path = getQGCMapEngine()->getCachePath(); + if(!path.isEmpty()) { + QStorageInfo info(path); + quint32 total = (quint32)(info.bytesTotal() / 1024); + quint32 free = (quint32)(info.bytesFree() / 1024); + qCDebug(QGCMapEngineManagerLog) << info.rootPath() << "has" << free << "Mbytes available."; + if(_freeDiskSpace != free) { + _freeDiskSpace = free; + _diskSpace = total; + emit freeDiskSpaceChanged(); + } + } +} diff --git a/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h new file mode 100644 index 0000000000000000000000000000000000000000..76d21ab83e3a4730e49f72c0d4f583e89c6ddb29 --- /dev/null +++ b/src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h @@ -0,0 +1,145 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2016 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @author Gus Grubba + +#ifndef OfflineMapsManager_H +#define OfflineMapsManager_H + +#include "QmlObjectListModel.h" +#include "QGCToolbox.h" +#include "QGCLoggingCategory.h" +#include "QGCMapEngine.h" +#include "QGCMapTileSet.h" + +Q_DECLARE_LOGGING_CATEGORY(QGCMapEngineManagerLog) + +class QGCMapEngineManager : public QGCTool +{ + Q_OBJECT +public: + QGCMapEngineManager(QGCApplication* app); + ~QGCMapEngineManager(); + + Q_PROPERTY(int tileX0 READ tileX0 NOTIFY tileX0Changed) + Q_PROPERTY(int tileX1 READ tileX1 NOTIFY tileX1Changed) + Q_PROPERTY(int tileY0 READ tileY0 NOTIFY tileY0Changed) + Q_PROPERTY(int tileY1 READ tileY1 NOTIFY tileY1Changed) + Q_PROPERTY(quint64 tileCount READ tileCount NOTIFY tileCountChanged) + Q_PROPERTY(QString tileCountStr READ tileCountStr NOTIFY tileCountChanged) + Q_PROPERTY(quint64 tileSize READ tileSize NOTIFY tileSizeChanged) + Q_PROPERTY(QString tileSizeStr READ tileSizeStr NOTIFY tileSizeChanged) + Q_PROPERTY(bool crazySize READ crazySize NOTIFY crazySizeChanged) + Q_PROPERTY(QmlObjectListModel* tileSets READ tileSets NOTIFY tileSetsChanged) + Q_PROPERTY(QStringList mapList READ mapList CONSTANT) + Q_PROPERTY(QString mapboxToken READ mapboxToken WRITE setMapboxToken NOTIFY mapboxTokenChanged) + Q_PROPERTY(quint32 maxMemCache READ maxMemCache WRITE setMaxMemCache NOTIFY maxMemCacheChanged) + Q_PROPERTY(quint32 maxDiskCache READ maxDiskCache WRITE setMaxDiskCache NOTIFY maxDiskCacheChanged) + Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) + //-- Disk Space in MB + Q_PROPERTY(quint32 freeDiskSpace READ freeDiskSpace NOTIFY freeDiskSpaceChanged) + Q_PROPERTY(quint32 diskSpace READ diskSpace CONSTANT) + + Q_INVOKABLE void loadTileSets (); + Q_INVOKABLE void updateForCurrentView (double lon0, double lat0, double lon1, double lat1, int minZoom, int maxZoom, const QString& mapName); + Q_INVOKABLE void startDownload (const QString& name, const QString& description, const QString& mapType, const QImage& image = QImage()); + Q_INVOKABLE void saveSetting (const QString& key, const QString& value); + Q_INVOKABLE QString loadSetting (const QString& key, const QString& defaultValue); + Q_INVOKABLE void deleteTileSet (QGCCachedTileSet* tileSet); + Q_INVOKABLE QString getUniqueName (); + Q_INVOKABLE bool findName (const QString& name); + + int tileX0 () { return _totalSet.tileX0; } + int tileX1 () { return _totalSet.tileX1; } + int tileY0 () { return _totalSet.tileY0; } + int tileY1 () { return _totalSet.tileY1; } + quint64 tileCount () { return _totalSet.tileCount; } + QString tileCountStr (); + quint64 tileSize () { return _totalSet.tileSize; } + QString tileSizeStr (); + bool crazySize () { return _crazySize; } + QStringList mapList (); + QString mapboxToken (); + QmlObjectListModel* tileSets () { return &_tileSets; } + quint32 maxMemCache (); + quint32 maxDiskCache (); + QString errorMessage () { return _errorMessage; } + quint64 freeDiskSpace () { return _freeDiskSpace; } + quint64 diskSpace () { return _diskSpace; } + + void setMapboxToken (QString token); + void setMaxMemCache (quint32 size); + void setMaxDiskCache (quint32 size); + + void setErrorMessage (const QString& error) { _errorMessage = error; emit errorMessageChanged(); } + + // Override from QGCTool + void setToolbox(QGCToolbox *toolbox); + +signals: + void tileX0Changed (); + void tileX1Changed (); + void tileY0Changed (); + void tileY1Changed (); + void tileCountChanged (); + void tileSizeChanged (); + void crazySizeChanged (); + void mapboxTokenChanged (); + void tileSetsChanged (); + void maxMemCacheChanged (); + void maxDiskCacheChanged (); + void errorMessageChanged (); + void freeDiskSpaceChanged (); + +public slots: + void taskError (QGCMapTask::TaskType type, QString error); + +private slots: + void _tileSetSaved (QGCCachedTileSet* set); + void _tileSetFetched (QGCCachedTileSet* tileSets); + void _tileSetDeleted (quint64 setID); + void _updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); + void _resetCompleted (); + +private: + void _clearTileSets (); + void _updateDiskFreeSpace (); + +private: + QGCTileSet _totalSet; + bool _crazySize; + double _topleftLat; + double _topleftLon; + double _bottomRightLat; + double _bottomRightLon; + int _minZoom; + int _maxZoom; + quint64 _setID; + quint32 _freeDiskSpace; + quint32 _diskSpace; + QmlObjectListModel _tileSets; + QString _errorMessage; +}; + +#endif diff --git a/src/QtLocationPlugin/qgeomapreplyqgc.cpp b/src/QtLocationPlugin/qgeomapreplyqgc.cpp deleted file mode 100644 index 298bca0626e462373f9abf9d44a68285ea069534..0000000000000000000000000000000000000000 --- a/src/QtLocationPlugin/qgeomapreplyqgc.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Aaron McCarthy -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtLocation module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -** 2015.4.4 -** Adapted for use with QGroundControl -** -** Gus Grubba -** -****************************************************************************/ - -#include - -#include "qgeomapreplyqgc.h" -#include "OpenPilotMaps.h" - -QGeoMapReplyQGC::QGeoMapReplyQGC(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent) - : QGeoTiledMapReply(spec, parent) - , m_reply(reply) -{ - if(!reply) - { - setError(QGeoTiledMapReply::UnknownError, "Invalid tile request"); - setFinished(true); - } - else - { - connect(m_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); - connect(m_reply, SIGNAL(destroyed()), this, SLOT(replyDestroyed())); - } -} - -QGeoMapReplyQGC::~QGeoMapReplyQGC() -{ - if (m_reply) { - m_reply->deleteLater(); - m_reply = 0; - } -} - -void QGeoMapReplyQGC::abort() -{ - if (!m_reply) - return; - m_reply->abort(); -} - -QNetworkReply *QGeoMapReplyQGC::networkReply() const -{ - return m_reply; -} - -void QGeoMapReplyQGC::replyDestroyed() -{ - m_reply = 0; -} - -void QGeoMapReplyQGC::networkReplyFinished() -{ - if (!m_reply) - { - return; - } - - if (m_reply->error() != QNetworkReply::NoError) - { - return; - } - - QByteArray a = m_reply->readAll(); - setMapImageData(a); - - if(a.size() > 2) - { - if((char)a[0] == (char)0xff && (char)a[1] == (char)0xd8) - setMapImageFormat("jpg"); - else if((char)a[0] == (char)0x89 && (char)a[1] == (char)0x50) - setMapImageFormat("png"); - else - { - switch ((OpenPilot::MapType)tileSpec().mapId()) { - case OpenPilot::GoogleMap: - case OpenPilot::GoogleLabels: - case OpenPilot::GoogleTerrain: - case OpenPilot::GoogleHybrid: - case OpenPilot::BingMap: - case OpenPilot::OpenStreetMap: - setMapImageFormat("png"); - break; - case OpenPilot::GoogleSatellite: - case OpenPilot::BingSatellite: - case OpenPilot::BingHybrid: - setMapImageFormat("jpg"); - break; - default: - qWarning("Unknown map id %d", tileSpec().mapId()); - break; - } - } - } - - setFinished(true); - m_reply->deleteLater(); - m_reply = 0; -} - -void QGeoMapReplyQGC::networkReplyError(QNetworkReply::NetworkError error) -{ - if (!m_reply) - { - return; - } - - if (error != QNetworkReply::OperationCanceledError) - { - setError(QGeoTiledMapReply::CommunicationError, m_reply->errorString()); - } - - setFinished(true); - m_reply->deleteLater(); - m_reply = 0; -} diff --git a/src/QtLocationPlugin/qgeotiledmappingmanagerengineqgc.cpp b/src/QtLocationPlugin/qgeotiledmappingmanagerengineqgc.cpp deleted file mode 100644 index 37d359d2c35d4fb2b63616736973bb8c7c56adef..0000000000000000000000000000000000000000 --- a/src/QtLocationPlugin/qgeotiledmappingmanagerengineqgc.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Aaron McCarthy -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtLocation module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -** 2015.4.4 -** Adapted for use with QGroundControl -** -** Gus Grubba -** -****************************************************************************/ - -#include -#include -#if QT_VERSION < 0x050500 -#include -#else -#include -#endif -#include -#include - -#include "qgeotiledmappingmanagerengineqgc.h" -#include "qgeotilefetcherqgc.h" -#include "OpenPilotMaps.h" - -#if QT_VERSION >= 0x050500 -QGeoTiledMapQGC::QGeoTiledMapQGC(QGeoTiledMappingManagerEngine *engine, QObject *parent) - : QGeoTiledMap(engine, parent) -{ - -} -#endif - -QGeoTiledMappingManagerEngineQGC::QGeoTiledMappingManagerEngineQGC(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) -: QGeoTiledMappingManagerEngine() -{ - QGeoCameraCapabilities cameraCaps; - cameraCaps.setMinimumZoomLevel(2.0); - cameraCaps.setMaximumZoomLevel(MAX_MAP_ZOOM); - cameraCaps.setSupportsBearing(true); - setCameraCapabilities(cameraCaps); - - setTileSize(QSize(256, 256)); - - QList mapTypes; - mapTypes << QGeoMapType(QGeoMapType::StreetMap, tr("Google Street Map"), tr("Google street map"), false, false, OpenPilot::GoogleMap); - mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Google Satellite Map"),tr("Google satellite map"), false, false, OpenPilot::GoogleSatellite); - mapTypes << QGeoMapType(QGeoMapType::TerrainMap, tr("Google Terrain Map"), tr("Google terrain map"), false, false, OpenPilot::GoogleTerrain); - // TODO: - // Proper google hybrid maps requires collecting two separate bimaps and overlaying them. - //mapTypes << QGeoMapType(QGeoMapType::HybridMap, tr("Google Hybrid Map"), tr("Google hybrid map"), false, false, OpenPilot::GoogleHybrid); - // Bing - mapTypes << QGeoMapType(QGeoMapType::StreetMap, tr("Bing Street Map"), tr("Bing street map"), false, false, OpenPilot::BingMap); - mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Bing Satellite Map"), tr("Bing satellite map"), false, false, OpenPilot::BingSatellite); - mapTypes << QGeoMapType(QGeoMapType::HybridMap, tr("Bing Hybrid Map"), tr("Bing hybrid map"), false, false, OpenPilot::BingHybrid); - mapTypes << QGeoMapType(QGeoMapType::StreetMap, tr("Open Street Map"), tr("Open Street map"), false, false, OpenPilot::OpenStreetMap); - setSupportedMapTypes(mapTypes); - - QGeoTileFetcherQGC *tileFetcher = new QGeoTileFetcherQGC(this); - if (parameters.contains(QStringLiteral("useragent"))) { - const QByteArray ua = parameters.value(QStringLiteral("useragent")).toString().toLatin1(); - tileFetcher->setUserAgent(ua); - } else - // QGC Default - tileFetcher->setUserAgent("Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7"); - -#if QT_VERSION >= 0x050500 - _setCache(parameters); -#endif - setTileFetcher(tileFetcher); - - *error = QGeoServiceProvider::NoError; - errorString->clear(); - -#if QT_VERSION >= 0x050500 - if (parameters.contains(QStringLiteral("mapping.copyright"))) - m_customCopyright = parameters.value(QStringLiteral("mapping.copyright")).toString().toLatin1(); -#endif -} - -QGeoTiledMappingManagerEngineQGC::~QGeoTiledMappingManagerEngineQGC() -{ -} - -#if QT_VERSION < 0x050500 - -QGeoMapData *QGeoTiledMappingManagerEngineQGC::createMapData() -{ - return new QGeoTiledMapData(this, 0); -} - -#else - -QGeoMap *QGeoTiledMappingManagerEngineQGC::createMap() -{ - return new QGeoTiledMapQGC(this); -} - -QString QGeoTiledMappingManagerEngineQGC::customCopyright() const -{ - return m_customCopyright; -} - -#endif - -#if QT_VERSION >= 0x050500 -void QGeoTiledMappingManagerEngineQGC::_setCache(const QVariantMap ¶meters) -{ - - QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/QGCMapCache"); - //-- Clear old cache - QDir baseDir(cacheDir); - if (baseDir.exists()) { - const QStringList oldCacheFiles = baseDir.entryList(QDir::Files); - foreach (const QString& file, oldCacheFiles) - baseDir.remove(file); - } - - if (parameters.contains(QStringLiteral("mapping.cache.directory"))) - cacheDir = parameters.value(QStringLiteral("mapping.cache.directory")).toString(); - else { - cacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/QGCMapCache55"); - if(!QDir::root().mkpath(cacheDir)) { - qWarning() << "Could not create mapping disk cache directory: " << cacheDir; - cacheDir = QDir::homePath() + QLatin1String("/.qgcmapscache/"); - } - } - - if(!QDir::root().mkpath(cacheDir)) - { - qWarning() << "Could not create mapping disk cache directory: " << cacheDir; - cacheDir.clear(); - } - else { - qDebug() << "Mapping cache directory:" << cacheDir; - } - QGeoTileCache* pTileCache = createTileCacheWithDir(cacheDir); - if(pTileCache) - { - int cacheLimit = 0; - //-- Disk Cache - if (parameters.contains(QStringLiteral("mapping.cache.disk.size"))) { - bool ok = false; - cacheLimit = parameters.value(QStringLiteral("mapping.cache.disk.size")).toString().toInt(&ok); - if (!ok) - cacheLimit = 0; - } - if(!cacheLimit) - { -#ifdef __mobile__ - cacheLimit = 128 * 1024 * 1024; -#else - cacheLimit = 1024 * 1024 * 1024; -#endif - } - pTileCache->setMaxDiskUsage(cacheLimit); - //-- Memory Cache - cacheLimit = 0; - if (parameters.contains(QStringLiteral("mapping.cache.memory.size"))) { - bool ok = false; - cacheLimit = parameters.value(QStringLiteral("mapping.cache.memory.size")).toString().toInt(&ok); - if (!ok) - cacheLimit = 0; - } - if(!cacheLimit) - { -#ifdef __mobile__ - cacheLimit = 16 * 1024 * 1024; -#else - cacheLimit = 128 * 1024 * 1024; -#endif - } - pTileCache->setMaxMemoryUsage(cacheLimit); - } -} -#endif diff --git a/src/QtLocationPlugin/qgeotilefetcherqgc.cpp b/src/QtLocationPlugin/qgeotilefetcherqgc.cpp deleted file mode 100644 index 54c32d0be107ddde87a0b70f96f611a4dc3b44d5..0000000000000000000000000000000000000000 --- a/src/QtLocationPlugin/qgeotilefetcherqgc.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Aaron McCarthy -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtLocation module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -** 2015.4.4 -** Adapted for use with QGroundControl -** -** Gus Grubba -** -****************************************************************************/ - -#include -#include -#include -#include - -#include "qgeotilefetcherqgc.h" -#include "qgeomapreplyqgc.h" - -QGeoTileFetcherQGC::QGeoTileFetcherQGC(QGeoTiledMappingManagerEngine *parent) - : QGeoTileFetcher(parent) - , m_networkManager(new QNetworkAccessManager(this)) -#if defined Q_OS_MAC - , m_userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0") -#elif defined Q_OS_WIN32 - , m_userAgent("Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7") -#else - , m_userAgent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20130331 Firefox/21.0") -#endif - , m_UrlFactory(NULL) -{ - QStringList langs = QLocale::system().uiLanguages(); - if (langs.length() > 0) { - m_Language = langs[0]; - } - m_UrlFactory = new OpenPilot::UrlFactory(m_networkManager); -} - -QGeoTileFetcherQGC::~QGeoTileFetcherQGC() -{ - if(m_UrlFactory) - delete m_UrlFactory; -} - -void QGeoTileFetcherQGC::setUserAgent(const QByteArray &userAgent) -{ - m_userAgent = userAgent; -} - -QGeoTiledMapReply *QGeoTileFetcherQGC::getTileImage(const QGeoTileSpec &spec) -{ - QNetworkRequest request; - QString url = m_UrlFactory->makeImageUrl((OpenPilot::MapType)spec.mapId(), QPoint(spec.x(), spec.y()), spec.zoom(), m_Language); - - request.setUrl(QUrl(url)); - request.setRawHeader("User-Agent", m_userAgent); - request.setRawHeader("Accept", "*/*"); - switch ((OpenPilot::MapType)spec.mapId()) { - case OpenPilot::GoogleMap: - case OpenPilot::GoogleSatellite: - case OpenPilot::GoogleLabels: - case OpenPilot::GoogleTerrain: - case OpenPilot::GoogleHybrid: - { - request.setRawHeader("Referrer", "http://maps.google.com/"); - } - break; - case OpenPilot::GoogleMapChina: - case OpenPilot::GoogleSatelliteChina: - case OpenPilot::GoogleLabelsChina: - case OpenPilot::GoogleTerrainChina: - case OpenPilot::GoogleHybridChina: - { - request.setRawHeader("Referrer", "http://ditu.google.cn/"); - } - break; - case OpenPilot::BingHybrid: - case OpenPilot::BingMap: - case OpenPilot::BingSatellite: - { - request.setRawHeader("Referrer", "http://www.bing.com/maps/"); - } - break; - case OpenPilot::YahooHybrid: - case OpenPilot::YahooLabels: - case OpenPilot::YahooMap: - case OpenPilot::YahooSatellite: - { - request.setRawHeader("Referrer", "http://maps.yahoo.com/"); - } - break; - case OpenPilot::ArcGIS_MapsLT_Map_Labels: - case OpenPilot::ArcGIS_MapsLT_Map: - case OpenPilot::ArcGIS_MapsLT_OrtoFoto: - case OpenPilot::ArcGIS_MapsLT_Map_Hybrid: - { - request.setRawHeader("Referrer", "http://www.maps.lt/map_beta/"); - } - break; - case OpenPilot::OpenStreetMapSurfer: - case OpenPilot::OpenStreetMapSurferTerrain: - { - request.setRawHeader("Referrer", "http://www.mapsurfer.net/"); - } - break; - case OpenPilot::OpenStreetMap: - case OpenPilot::OpenStreetOsm: - { - request.setRawHeader("Referrer", "http://www.openstreetmap.org/"); - } - break; - case OpenPilot::YandexMapRu: - { - request.setRawHeader("Referrer", "http://maps.yandex.ru/"); - } - break; - default: - break; - } - - QNetworkReply *reply = m_networkManager->get(request); - reply->setParent(0); - return new QGeoMapReplyQGC(reply, spec); - -} diff --git a/src/main.cc b/src/main.cc index 91c48eb1fdf90b2ba899fbd94ff0bf6991e727e8..8acb16b63ceb081abd619db1bf876caf0049142f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -56,6 +56,7 @@ This file is part of the QGROUNDCONTROL project #endif #include +#include "QGCMapEngine.h" /* SDL does ugly things to main() */ #ifdef main @@ -218,6 +219,8 @@ int main(int argc, char *argv[]) qRegisterMetaType > >(); app->_initCommon(); + //-- Initialize Cache System + getQGCMapEngine()->init(); int exitCode = 0; @@ -251,6 +254,8 @@ int main(int argc, char *argv[]) } delete app; + //-- Shutdown Cache System + destroyMapEngine(); qDebug() << "After app delete"; diff --git a/src/ui/MainWindowLeftPanel.qml b/src/ui/MainWindowLeftPanel.qml index b508ba6c0be0cd897a1e9849436610d950b4943d..d83f724fc34db85ea8493862b413dbb2498e351e 100644 --- a/src/ui/MainWindowLeftPanel.qml +++ b/src/ui/MainWindowLeftPanel.qml @@ -174,6 +174,19 @@ Item { checked = true } } + QGCButton { + width: parent.width * 0.85 + height: ScreenTools.defaultFontPixelHeight * 2.5 + text: "Offline Maps" + exclusiveGroup: panelActionGroup + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + if(__rightPanel.source != "OfflineMap.qml") { + __rightPanel.source = "OfflineMap.qml" + } + checked = true + } + } QGCButton { width: parent.width * 0.85 height: ScreenTools.defaultFontPixelHeight * 2.5 diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 7762d23481941885914a47e2015959167aa32a70..450f4d4f0d088310720cc9bb3ba11dfd32c0eaaf 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -133,7 +133,6 @@ Rectangle { height: ScreenTools.defaultFontPixelHeight / 2 width: parent.width } - //----------------------------------------------------------------- //-- Map Providers Row {