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