QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCCachedTileSet.cpp
Go to the documentation of this file.
1#include "QGCCachedTileSet.h"
2
4#include "QGCApplication.h"
6#include "QGCMapEngine.h"
8#include "QGCNetworkHelper.h"
9#include "QGCMapTasks.h"
10#include "QGCMapUrlEngine.h"
12#include "QGeoTileFetcherQGC.h"
13
14QGC_LOGGING_CATEGORY(QGCCachedTileSetLog, "QtLocationPlugin.QGCCachedTileSet")
15
16QGCCachedTileSet::QGCCachedTileSet(const QString &name, QObject *parent)
17 : QObject(parent)
18 , _name(name)
19{
20 qCDebug(QGCCachedTileSetLog) << this;
21}
22
23QGCCachedTileSet::~QGCCachedTileSet()
24{
25 qCDebug(QGCCachedTileSetLog) << this;
26}
27
28QString QGCCachedTileSet::downloadStatus() const
29{
30 if (_defaultSet) {
31 return totalTilesSizeStr();
32 }
33
34 if (_totalTileCount <= _savedTileCount) {
35 return savedTileSizeStr();
36 }
37
38 return (savedTileSizeStr() + " / " + totalTilesSizeStr());
39}
40
41void QGCCachedTileSet::createDownloadTask()
42{
43 if (_cancelPending) {
44 setDownloading(false);
45 return;
46 }
47
48 if (!_downloading) {
49 setErrorCount(0);
50 setDownloading(true);
51 _noMoreTiles = false;
52 }
53
54 QGCGetTileDownloadListTask *task = new QGCGetTileDownloadListTask(_id, kTileBatchSize);
55 (void) connect(task, &QGCGetTileDownloadListTask::tileListFetched, this, &QGCCachedTileSet::_tileListFetched);
56 if (_manager) {
57 (void) connect(task, &QGCMapTask::error, _manager, &QGCMapEngineManager::taskError);
58 }
59 if (!getQGCMapEngine()->addTask(task)) {
60 task->deleteLater();
61 }
62
65
66 _batchRequested = true;
67}
68
69void QGCCachedTileSet::resumeDownloadTask()
70{
71 _cancelPending = false;
72
74 if (!getQGCMapEngine()->addTask(task)) {
75 task->deleteLater();
76 }
77
78 createDownloadTask();
79}
80
81void QGCCachedTileSet::cancelDownloadTask()
82{
83 _cancelPending = true;
84}
85
86void QGCCachedTileSet::_tileListFetched(const QQueue<QGCTile*> &tiles)
87{
88 _batchRequested = false;
89 if (tiles.size() < kTileBatchSize) {
90 _noMoreTiles = true;
91 }
92
93 if (tiles.isEmpty()) {
94 _doneWithDownload();
95 return;
96 }
97
98 if (!_networkManager) {
99 _networkManager = new QNetworkAccessManager(this);
100 QGCNetworkHelper::configureProxy(_networkManager);
101 }
102
103 _tilesToDownload.append(tiles);
104 _prepareDownload();
105}
106
107void QGCCachedTileSet::_doneWithDownload()
108{
109 if (_errorCount == 0) {
110 setTotalTileCount(_savedTileCount);
111 setTotalTileSize(_savedTileSize);
112
113 quint32 avg = 0;
114 if (_savedTileCount != 0) {
115 avg = _savedTileSize / _savedTileCount;
116 } else {
117 qCWarning(QGCCachedTileSetLog) << "_savedTileCount=0";
118 }
119
120 setUniqueTileSize(_uniqueTileCount * avg);
121 }
122
123 setDownloading(false);
124
125 emit completeChanged();
126}
127
128void QGCCachedTileSet::_prepareDownload()
129{
130 if (_tilesToDownload.isEmpty()) {
131 if (_noMoreTiles) {
132 _doneWithDownload();
133 } else if (!_batchRequested) {
134 createDownloadTask();
135 }
136 return;
137 }
138
139 for (qsizetype i = _replies.count(); i < QGeoTileFetcherQGC::concurrentDownloads(_type); i++) {
140 if (_tilesToDownload.isEmpty()) {
141 break;
142 }
143
144 QGCTile* const tile = _tilesToDownload.dequeue();
145 QNetworkRequest request = QGeoTileFetcherQGC::getNetworkRequest(tile->type, tile->x, tile->y, tile->z);
146 if (!request.url().isValid()) {
147 qCWarning(QGCCachedTileSetLog) << "Invalid URL for tile" << tile->hash << "- skipping";
148 setErrorCount(_errorCount + 1);
149 delete tile;
150 continue;
151 }
152 request.setOriginatingObject(this);
153 request.setAttribute(QNetworkRequest::User, tile->hash);
154
155 QNetworkReply* const reply = _networkManager->get(request);
156 reply->setParent(this);
158 (void) connect(reply, &QNetworkReply::finished, this, &QGCCachedTileSet::_networkReplyFinished);
159 (void) connect(reply, &QNetworkReply::errorOccurred, this, &QGCCachedTileSet::_networkReplyError);
160 {
161 QMutexLocker lock(&_repliesMutex);
162 (void) _replies.insert(tile->hash, reply);
163 }
164
165 delete tile;
166 if (!_batchRequested && !_noMoreTiles && (_tilesToDownload.count() < (QGeoTileFetcherQGC::concurrentDownloads(_type) * 10))) {
167 createDownloadTask();
168 }
169 }
170}
171
172void QGCCachedTileSet::_networkReplyFinished()
173{
174 QNetworkReply* const reply = qobject_cast<QNetworkReply*>(QObject::sender());
175 if (!reply) {
176 qCWarning(QGCCachedTileSetLog) << "NULL Reply";
177 return;
178 }
179 reply->deleteLater();
180
181 if (reply->error() != QNetworkReply::NoError) {
182 return;
183 }
184
185 if (!reply->isOpen()) {
186 qCWarning(QGCCachedTileSetLog) << "Empty Reply";
187 return;
188 }
189
190 const QString hash = reply->request().attribute(QNetworkRequest::User).toString();
191 if (hash.isEmpty()) {
192 qCWarning(QGCCachedTileSetLog) << "Empty Hash";
193 return;
194 }
195
196 {
197 QMutexLocker lock(&_repliesMutex);
198 if (_replies.contains(hash)) {
199 (void) _replies.remove(hash);
200 } else {
201 qCWarning(QGCCachedTileSetLog) << "Reply not in list: " << hash;
202 }
203 }
204 qCDebug(QGCCachedTileSetLog) << "Tile fetched:" << hash;
205
206 QByteArray image = reply->readAll();
207 if (image.isEmpty()) {
208 qCWarning(QGCCachedTileSetLog) << "Empty Image";
209 return;
210 }
211
212 const QString type = UrlFactory::tileHashToType(hash);
214 if (!mapProvider) {
215 qCWarning(QGCCachedTileSetLog) << "Invalid map provider for type:" << type;
216 return;
217 }
218
219 if (mapProvider->isElevationProvider()) {
220 const SharedElevationProvider elevationProvider = std::dynamic_pointer_cast<const ElevationProvider>(mapProvider);
221 image = elevationProvider->serialize(image);
222 if (image.isEmpty()) {
223 qCWarning(QGCCachedTileSetLog) << "Failed to Serialize Terrain Tile";
224 return;
225 }
226 }
227
228 const QString format = mapProvider->getImageFormat(image);
229 if (format.isEmpty()) {
230 qCWarning(QGCCachedTileSetLog) << "Empty Format";
231 return;
232 }
233
234 QGeoFileTileCacheQGC::cacheTile(type, hash, image, format, _id);
235
237 if (!getQGCMapEngine()->addTask(task)) {
238 task->deleteLater();
239 }
240
241 setSavedTileSize(_savedTileSize + image.size());
242 setSavedTileCount(_savedTileCount + 1);
243
244 if (_savedTileCount % 10 == 0) {
245 const quint32 avg = _savedTileSize / _savedTileCount;
246 setTotalTileSize(avg * _totalTileCount);
247 setUniqueTileSize(avg * _uniqueTileCount);
248 }
249
250 _prepareDownload();
251}
252
253void QGCCachedTileSet::_networkReplyError(QNetworkReply::NetworkError error)
254{
255 QNetworkReply* const reply = qobject_cast<QNetworkReply*>(QObject::sender());
256 if (!reply) {
257 return;
258 }
259 qCDebug(QGCCachedTileSetLog) << "Error fetching tile" << reply->errorString();
260
261 setErrorCount(_errorCount + 1);
262
263 const QString hash = reply->request().attribute(QNetworkRequest::User).toString();
264 if (hash.isEmpty()) {
265 qCWarning(QGCCachedTileSetLog) << "Empty Hash";
266 return;
267 }
268
269 {
270 QMutexLocker lock(&_repliesMutex);
271 if (_replies.contains(hash)) {
272 (void) _replies.remove(hash);
273 } else {
274 qCWarning(QGCCachedTileSetLog) << "Reply not in list:" << hash;
275 }
276 }
277
278 if (error != QNetworkReply::OperationCanceledError) {
279 qCWarning(QGCCachedTileSetLog) << "Error:" << reply->errorString();
280 }
281
283 if (!getQGCMapEngine()->addTask(task)) {
284 task->deleteLater();
285 }
286
287 _prepareDownload();
288}
289
290void QGCCachedTileSet::setSelected(bool sel)
291{
292 if (sel != _selected) {
293 _selected = sel;
294 emit selectedChanged();
295 if (_manager) {
296 emit _manager->selectedCountChanged();
297 }
298 }
299}
300
301QString QGCCachedTileSet::errorCountStr() const
302{
303 return qgcApp()->numberToString(_errorCount);
304}
305
306QString QGCCachedTileSet::totalTileCountStr() const
307{
308 return qgcApp()->numberToString(_totalTileCount);
309}
310
311QString QGCCachedTileSet::totalTilesSizeStr() const
312{
313 return qgcApp()->bigSizeToString(_totalTileSize);
314}
315
316QString QGCCachedTileSet::uniqueTileSizeStr() const
317{
318 return qgcApp()->bigSizeToString(_uniqueTileSize);
319}
320
321QString QGCCachedTileSet::uniqueTileCountStr() const
322{
323 return qgcApp()->numberToString(_uniqueTileCount);
324}
325
326QString QGCCachedTileSet::savedTileCountStr() const
327{
328 return qgcApp()->numberToString(_savedTileCount);
329}
330
331QString QGCCachedTileSet::savedTileSizeStr() const
332{
333 return qgcApp()->bigSizeToString(_savedTileSize);
334}
#define qgcApp()
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QGCMapEngine * getQGCMapEngine()
std::shared_ptr< const ElevationProvider > SharedElevationProvider
std::shared_ptr< const MapProvider > SharedMapProvider
void completeChanged()
void totalTileCountChanged()
void totalTilesSizeChanged()
void selectedChanged()
void tileListFetched(QQueue< QGCTile * > tiles)
void taskError(QGCMapTask::TaskType type, const QString &error)
bool addTask(QGCMapTask *task)
void error(QGCMapTask::TaskType type, const QString &errorString)
static void cacheTile(const QString &type, int x, int y, int z, const QByteArray &image, const QString &format, qulonglong set=UINT64_MAX)
static uint32_t concurrentDownloads(const QString &type)
static QNetworkRequest getNetworkRequest(int mapId, int x, int y, int zoom)
static QString tileHashToType(QStringView tileHash)
static std::shared_ptr< const MapProvider > getMapProviderFromProviderType(QStringView type)
void configureProxy(QNetworkAccessManager *manager)
Set up default proxy configuration on a network manager.
void ignoreSslErrorsIfNeeded(QNetworkReply *reply)
int type
Definition QGCTile.h:21
int x
Definition QGCTile.h:16
QString hash
Definition QGCTile.h:20
int z
Definition QGCTile.h:18
int y
Definition QGCTile.h:17
@ StateComplete
Definition QGCTile.h:13
@ StatePending
Definition QGCTile.h:10
@ StateError
Definition QGCTile.h:12