QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
Viewer3DTileReply.cc
Go to the documentation of this file.
1#include "Viewer3DTileReply.h"
2
3#include "MapProvider.h"
4#include "QGCCacheTile.h"
6#include "QGCMapEngine.h"
7#include "QGCMapTasks.h"
8#include "QGCMapUrlEngine.h"
10#include "QGeoTileFetcherQGC.h"
11
12#include <QtCore/QFile>
13#include <QtCore/QString>
14#include <QtCore/QTimer>
15#include <QtNetwork/QNetworkAccessManager>
16#include <QtNetwork/QNetworkReply>
17
18#include <mutex>
19
20QGC_LOGGING_CATEGORY(Viewer3DTileReplyLog, "Viewer3d.Viewer3DTileReply")
21
22QByteArray Viewer3DTileReply::_bingNoTileImage;
23static std::once_flag s_bingNoTileInitFlag;
24
25Viewer3DTileReply::Viewer3DTileReply(int zoomLevel, int tileX, int tileY, int mapId, const QString &mapType, QNetworkAccessManager *networkManager, QObject *parent)
26 : QObject{parent}
27 , _networkManager(networkManager)
28{
29 std::call_once(s_bingNoTileInitFlag, []() {
30 QFile file(QStringLiteral(":/res/BingNoTileBytes.dat"));
31 if (file.open(QFile::ReadOnly)) {
32 _bingNoTileImage = file.readAll();
33 } else {
34 qWarning() << "Error opening file" << file.fileName();
35 }
36 });
37
38 _timeoutTimer = new QTimer(this);
39
40 _tile.x = tileX;
41 _tile.y = tileY;
42 _tile.zoomLevel = zoomLevel;
43 _tile.mapId = mapId;
44
45 QGCFetchTileTask *task = QGeoFileTileCacheQGC::createFetchTileTask(mapType, tileX, tileY, zoomLevel);
46 connect(task, &QGCFetchTileTask::tileFetched, this, &Viewer3DTileReply::_onCacheHit);
47 connect(task, &QGCMapTask::error, this, [this](QGCMapTask::TaskType, const QString &) { _onCacheMiss(); });
48 if (!getQGCMapEngine()->addTask(task)) {
49 task->deleteLater();
50 _onCacheMiss();
51 }
52}
53
55{
56 if (_reply) {
57 _disconnectReply();
58 _reply->abort();
59 delete _reply;
60 _reply = nullptr;
61 }
62}
63
64void Viewer3DTileReply::_prepareDownload()
65{
66 const QNetworkRequest request = QGeoTileFetcherQGC::getNetworkRequest(_tile.mapId, _tile.x, _tile.y, _tile.zoomLevel);
67 _reply = _networkManager->get(request);
68 connect(_reply, &QNetworkReply::finished, this, &Viewer3DTileReply::_onRequestFinished);
69 connect(_reply, &QNetworkReply::errorOccurred, this, &Viewer3DTileReply::_onRequestError);
70}
71
72void Viewer3DTileReply::_onCacheHit(QGCCacheTile *tile)
73{
74 if (!tile) {
75 _onCacheMiss();
76 return;
77 }
78
79 _tile.data = tile->img;
80 delete tile;
81
82 if (_isBingEmptyTile()) {
83 _tile.data.clear();
84 emit tileEmpty(_tile);
85 return;
86 }
87
88 emit tileDone(_tile);
89}
90
91void Viewer3DTileReply::_onCacheMiss()
92{
93 _prepareDownload();
94 _timeoutTimer->start(kTimeoutMs);
95 connect(_timeoutTimer, &QTimer::timeout, this, &Viewer3DTileReply::_onTimeout);
96}
97
98void Viewer3DTileReply::_onRequestFinished()
99{
100 _tile.data = _reply->readAll();
101 _timeoutTimer->stop();
102 _disconnectReply();
103 disconnect(_timeoutTimer, &QTimer::timeout, this, &Viewer3DTileReply::_onTimeout);
104
105 if (_isBingEmptyTile()) {
106 _tile.data.clear();
107 emit tileEmpty(_tile);
108 return;
109 }
110
112 if (mapProvider && !_tile.data.isEmpty()) {
113 const QString format = mapProvider->getImageFormat(_tile.data);
114 QGeoFileTileCacheQGC::cacheTile(mapProvider->getMapName(), _tile.x, _tile.y, _tile.zoomLevel, _tile.data, format);
115 }
116
117 emit tileDone(_tile);
118}
119
120void Viewer3DTileReply::_onRequestError()
121{
122 qCWarning(Viewer3DTileReplyLog) << "Request error for tile x:" << _tile.x << "y:" << _tile.y << "zoom:" << _tile.zoomLevel;
123 _timeoutTimer->stop();
124 disconnect(_timeoutTimer, &QTimer::timeout, this, &Viewer3DTileReply::_onTimeout);
125 _disconnectReply();
126 emit tileGiveUp(_tile);
127}
128
129void Viewer3DTileReply::_onTimeout()
130{
131 qCDebug(Viewer3DTileReplyLog) << "Timeout for tile x:" << _tile.x << "y:" << _tile.y << "retry:" << _timeoutCounter << "/" << kMaxRetries;
132 if (_timeoutCounter > kMaxRetries) {
133 _disconnectReply();
134 disconnect(_timeoutTimer, &QTimer::timeout, this, &Viewer3DTileReply::_onTimeout);
135 emit tileGiveUp(_tile);
136 _timeoutTimer->stop();
137 } else if (_tile.data.isEmpty()) {
138 if (_reply) {
139 _disconnectReply();
140 _reply->abort();
141 delete _reply;
142 _reply = nullptr;
143 }
144 _prepareDownload();
145 _timeoutCounter++;
146 }
147}
148
149void Viewer3DTileReply::_disconnectReply()
150{
151 disconnect(_reply, &QNetworkReply::finished, this, &Viewer3DTileReply::_onRequestFinished);
152 disconnect(_reply, &QNetworkReply::errorOccurred, this, &Viewer3DTileReply::_onRequestError);
153}
154
155bool Viewer3DTileReply::_isBingEmptyTile() const
156{
158 return mapProvider && mapProvider->isBingProvider() && !_tile.data.isEmpty() && _tile.data == _bingNoTileImage;
159}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QGCMapEngine * getQGCMapEngine()
std::shared_ptr< const MapProvider > SharedMapProvider
static std::once_flag s_bingNoTileInitFlag
void tileFetched(QGCCacheTile *tile)
bool addTask(QGCMapTask *task)
void error(QGCMapTask::TaskType type, const QString &errorString)
static QGCFetchTileTask * createFetchTileTask(const QString &type, int x, int y, int z)
static void cacheTile(const QString &type, int x, int y, int z, const QByteArray &image, const QString &format, qulonglong set=UINT64_MAX)
static QNetworkRequest getNetworkRequest(int mapId, int x, int y, int zoom)
static std::shared_ptr< const MapProvider > getMapProviderFromQtMapId(int qtMapId)
void tileDone(TileInfo_t)
void tileEmpty(TileInfo_t)
void tileGiveUp(TileInfo_t)
QByteArray format(const QList< LogEntry > &entries, int fmt)
QByteArray img