地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
12 KiB

/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
/**
* @file
* @brief Map Tile Set
*
* @author Gus Grubba <gus@auterion.com>
*
*/
#include "QGCMapEngine.h"
#include "QGCMapTileSet.h"
#include "QGCMapEngineManager.h"
#include "TerrainTile.h"
#include <QSettings>
#include <math.h>
QGC_LOGGING_CATEGORY(QGCCachedTileSetLog, "QGCCachedTileSetLog")
#define TILE_BATCH_SIZE 256
//-----------------------------------------------------------------------------
QGCCachedTileSet::QGCCachedTileSet(const QString& name)
: _name(name)
, _topleftLat(0.0)
, _topleftLon(0.0)
, _bottomRightLat(0.0)
, _bottomRightLon(0.0)
, _totalTileCount(0)
, _totalTileSize(0)
, _uniqueTileCount(0)
, _uniqueTileSize(0)
, _savedTileCount(0)
, _savedTileSize(0)
, _minZoom(3)
, _maxZoom(3)
, _defaultSet(false)
, _deleting(false)
, _downloading(false)
, _id(0)
, _type("Invalid")
, _networkManager(nullptr)
, _errorCount(0)
, _noMoreTiles(false)
, _batchRequested(false)
, _manager(nullptr)
, _selected(false)
{
}
//-----------------------------------------------------------------------------
QGCCachedTileSet::~QGCCachedTileSet()
{
delete _networkManager;
_networkManager = nullptr;
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::errorCountStr() const
{
return QGCMapEngine::numberToString(_errorCount);
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::totalTileCountStr() const
{
return QGCMapEngine::numberToString(_totalTileCount);
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::totalTilesSizeStr() const
{
return QGCMapEngine::bigSizeToString(_totalTileSize);
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::uniqueTileSizeStr() const
{
return QGCMapEngine::bigSizeToString(_uniqueTileSize);
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::uniqueTileCountStr() const
{
return QGCMapEngine::numberToString(_uniqueTileCount);
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::savedTileCountStr() const
{
return QGCMapEngine::numberToString(_savedTileCount);
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::savedTileSizeStr() const
{
return QGCMapEngine::bigSizeToString(_savedTileSize);
}
//-----------------------------------------------------------------------------
QString
QGCCachedTileSet::downloadStatus()
{
if(_defaultSet) {
return totalTilesSizeStr();
}
if(_totalTileCount <= _savedTileCount) {
return savedTileSizeStr();
} else {
return savedTileSizeStr() + " / " + totalTilesSizeStr();
}
}
//-----------------------------------------------------------------------------
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 totalTileCountChanged();
emit totalTilesSizeChanged();
_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<QGCTile *> tiles)
{
_batchRequested = false;
//-- Done?
if(tiles.size() < TILE_BATCH_SIZE) {
_noMoreTiles = true;
}
if(!tiles.size()) {
_doneWithDownload();
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::_doneWithDownload()
{
if(!_errorCount) {
_totalTileCount = _savedTileCount;
_totalTileSize = _savedTileSize;
//-- Too expensive to compute the real size now. Estimate it for the time being.
quint32 avg;
if(_savedTileSize != 0){
avg = _savedTileSize / _savedTileCount;
}
else{
qWarning() << "QGCMapEngineManager::_doneWithDownload _savedTileSize=0 !";
avg = 0;
}
_uniqueTileSize = _uniqueTileCount * avg;
}
emit totalTileCountChanged();
emit totalTilesSizeChanged();
emit savedTileSizeChanged();
emit savedTileCountChanged();
emit uniqueTileSizeChanged();
_downloading = false;
emit downloadingChanged();
emit completeChanged();
}
//-----------------------------------------------------------------------------
void QGCCachedTileSet::_prepareDownload()
{
if(!_tilesToDownload.count()) {
//-- Are we done?
if(_noMoreTiles) {
_doneWithDownload();
} 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());
#if !defined(__mobile__)
QNetworkProxy proxy = _networkManager->proxy();
QNetworkProxy tProxy;
tProxy.setType(QNetworkProxy::DefaultProxy);
_networkManager->setProxy(tProxy);
#endif
QNetworkReply* reply = _networkManager->get(request);
reply->setParent(0);
connect(reply, &QNetworkReply::finished, this, &QGCCachedTileSet::_networkReplyFinished);
connect(reply, &QNetworkReply::errorOccurred, this, &QGCCachedTileSet::_networkReplyError);
_replies.insert(tile->hash(), reply);
#if !defined(__mobile__)
_networkManager->setProxy(proxy);
#endif
delete tile;
//-- 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<QNetworkReply*>(QObject::sender());
if(!reply) {
qWarning() << "QGCMapEngineManager::networkReplyFinished() NULL Reply";
return;
}
if (reply->error() == QNetworkReply::NoError) {
//-- 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;
}
qCDebug(QGCCachedTileSetLog) << "Tile fetched" << hash;
QByteArray image = reply->readAll();
QString type = getQGCMapEngine()->hashToType(hash);
if (type == "Airmap Elevation" ) {
image = TerrainTile::serializeFromAirMapJson(image);
}
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
_savedTileSize += image.size();
_savedTileCount++;
emit savedTileSizeChanged();
emit savedTileCountChanged();
//-- Update estimate
if(_savedTileCount % 10 == 0) {
quint32 avg = _savedTileSize / _savedTileCount;
_totalTileSize = avg * _totalTileCount;
_uniqueTileSize = avg * _uniqueTileCount;
emit totalTilesSizeChanged();
emit uniqueTileSizeChanged();
}
}
//-- 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<QNetworkReply*>(QObject::sender());
if (!reply) {
return;
}
//-- Update 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;
}
//-----------------------------------------------------------------------------
void
QGCCachedTileSet::setSelected(bool sel)
{
_selected = sel;
emit selectedChanged();
if(_manager) {
emit _manager->selectedCountChanged();
}
}