QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGeoMapReplyQGC.cpp
Go to the documentation of this file.
1#include "QGeoMapReplyQGC.h"
2
3#include <QtCore/QFile>
4#include <QtLocation/private/qgeotilespec_p.h>
5#include <QtNetwork/QNetworkAccessManager>
6#include <QtNetwork/QSslError>
7
9#include "MapProvider.h"
10#include "QGCCacheTile.h"
11#include "QGCLoggingCategory.h"
12#include "QGCMapEngine.h"
13#include "QGCMapTasks.h"
14#include "QGCMapUrlEngine.h"
15#include "QGCNetworkHelper.h"
17
18QGC_LOGGING_CATEGORY(QGeoTiledMapReplyQGCLog, "QtLocationPlugin.QGeoTiledMapReplyQGC")
19
20QByteArray QGeoTiledMapReplyQGC::_bingNoTileImage;
21QByteArray QGeoTiledMapReplyQGC::_badTile;
22
23QGeoTiledMapReplyQGC::QGeoTiledMapReplyQGC(QNetworkAccessManager *networkManager, const QNetworkRequest &request, const QGeoTileSpec &spec, QObject *parent)
24 : QGeoTiledMapReply(spec, parent)
25 , _networkManager(networkManager)
26 , _request(request)
27{
28 qCDebug(QGeoTiledMapReplyQGCLog) << this;
29}
30
32{
33 qCDebug(QGeoTiledMapReplyQGCLog) << this;
34}
35
37{
38 if (m_initialized) {
39 return true;
40 }
41
42 m_initialized = true;
43
44 _initDataFromResources();
45
46 (void) connect(this, &QGeoTiledMapReplyQGC::errorOccurred, this, [this](QGeoTiledMapReply::Error error, const QString &errorString) {
47 qCWarning(QGeoTiledMapReplyQGCLog) << error << errorString;
48 setMapImageData(_badTile);
49 setMapImageFormat(QStringLiteral("png"));
50 setCached(false);
51 }, Qt::AutoConnection);
52
53 QGCFetchTileTask *task = QGeoFileTileCacheQGC::createFetchTileTask(UrlFactory::getProviderTypeFromQtMapId(tileSpec().mapId()), tileSpec().x(), tileSpec().y(), tileSpec().zoom());
54 if (!task) {
55 qCWarning(QGeoTiledMapReplyQGCLog) << "Failed to create fetch tile task";
56 m_initialized = false;
57 return false;
58 }
59 (void) connect(task, &QGCFetchTileTask::tileFetched, this, &QGeoTiledMapReplyQGC::_cacheReply);
60 (void) connect(task, &QGCMapTask::error, this, &QGeoTiledMapReplyQGC::_cacheError);
61 if (!getQGCMapEngine()->addTask(task)) {
62 task->deleteLater();
63 m_initialized = false;
64 return false;
65 }
66
67 return true;
68}
69
70void QGeoTiledMapReplyQGC::_initDataFromResources()
71{
72 if (_bingNoTileImage.isEmpty()) {
73 QFile file(":/res/BingNoTileBytes.dat");
74 if (file.open(QFile::ReadOnly)) {
75 _bingNoTileImage = file.readAll();
76 file.close();
77 }
78 }
79
80 if (_badTile.isEmpty()) {
81 QFile file(":/res/images/notile.png");
82 if (file.open(QFile::ReadOnly)) {
83 _badTile = file.readAll();
84 file.close();
85 }
86 }
87}
88
89void QGeoTiledMapReplyQGC::_networkReplyFinished()
90{
91 QNetworkReply* const reply = qobject_cast<QNetworkReply*>(sender());
92 if (!reply) {
93 setError(QGeoTiledMapReply::UnknownError, tr("Unexpected Error"));
94 return;
95 }
96 reply->deleteLater();
97
98 if (reply->error() != QNetworkReply::NoError) {
99 return;
100 }
101
102 if (!reply->isOpen()) {
103 setError(QGeoTiledMapReply::ParseError, tr("Empty Reply"));
104 return;
105 }
106
107 const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
108 if (!QGCNetworkHelper::isHttpSuccess(statusCode)) {
109 setError(QGeoTiledMapReply::CommunicationError, reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
110 return;
111 }
112
113 QByteArray image = reply->readAll();
114 if (image.isEmpty()) {
115 setError(QGeoTiledMapReply::ParseError, tr("Image is Empty"));
116 return;
117 }
118
119 const SharedMapProvider mapProvider = UrlFactory::getMapProviderFromQtMapId(tileSpec().mapId());
120 if (!mapProvider) {
121 setError(QGeoTiledMapReply::UnknownError, tr("Invalid Map Provider"));
122 return;
123 }
124
125 if (mapProvider->isBingProvider() && (image == _bingNoTileImage)) {
126 setError(QGeoTiledMapReply::CommunicationError, tr("Bing Tile Above Zoom Level"));
127 return;
128 }
129
130 if (mapProvider->isElevationProvider()) {
131 const SharedElevationProvider elevationProvider = std::dynamic_pointer_cast<const ElevationProvider>(mapProvider);
132 image = elevationProvider->serialize(image);
133 if (image.isEmpty()) {
134 setError(QGeoTiledMapReply::ParseError, tr("Failed to Serialize Terrain Tile"));
135 return;
136 }
137 }
138 setMapImageData(image);
139
140 const QString format = mapProvider->getImageFormat(image);
141 if (format.isEmpty()) {
142 setError(QGeoTiledMapReply::ParseError, tr("Unknown Format"));
143 return;
144 }
145 setMapImageFormat(format);
146
147 QGeoFileTileCacheQGC::cacheTile(mapProvider->getMapName(), tileSpec().x(), tileSpec().y(), tileSpec().zoom(), image, format);
148
149 setFinished(true);
150}
151
152void QGeoTiledMapReplyQGC::_networkReplyError(QNetworkReply::NetworkError error)
153{
154 if (error != QNetworkReply::OperationCanceledError) {
155 const QNetworkReply* const reply = qobject_cast<const QNetworkReply*>(sender());
156 if (!reply) {
157 setError(QGeoTiledMapReply::CommunicationError, tr("Invalid Reply"));
158 } else {
159 setError(QGeoTiledMapReply::CommunicationError, reply->errorString());
160 }
161 } else {
162 setFinished(true);
163 }
164}
165
166void QGeoTiledMapReplyQGC::_networkReplySslErrors(const QList<QSslError> &errors)
167{
168 QString errorString;
169 for (const QSslError &error : errors) {
170 if (!errorString.isEmpty()) {
171 (void) errorString.append('\n');
172 }
173 (void) errorString.append(error.errorString());
174 }
175
176 if (!errorString.isEmpty()) {
177 setError(QGeoTiledMapReply::CommunicationError, errorString);
178 }
179}
180
181void QGeoTiledMapReplyQGC::_cacheReply(QGCCacheTile *tile)
182{
183 if (tile) {
184 setMapImageData(tile->img);
185 setMapImageFormat(tile->format);
186 setCached(true);
187 setFinished(true);
188 delete tile;
189 } else {
190 setError(QGeoTiledMapReply::UnknownError, tr("Invalid Cache Tile"));
191 }
192}
193
194void QGeoTiledMapReplyQGC::_cacheError(QGCMapTask::TaskType type, QStringView errorString)
195{
196 Q_UNUSED(errorString);
197
198 Q_ASSERT(type == QGCMapTask::TaskType::taskFetchTile);
199
201 setError(QGeoTiledMapReply::CommunicationError, tr("Network Not Available"));
202 return;
203 }
204
205 _request.setOriginatingObject(this);
206
207 QNetworkReply* const reply = _networkManager->get(_request);
208 reply->setParent(this);
210
211 (void) connect(reply, &QNetworkReply::finished, this, &QGeoTiledMapReplyQGC::_networkReplyFinished);
212 (void) connect(reply, &QNetworkReply::errorOccurred, this, &QGeoTiledMapReplyQGC::_networkReplyError);
213 (void) connect(reply, &QNetworkReply::sslErrors, this, &QGeoTiledMapReplyQGC::_networkReplySslErrors);
214 (void) connect(this, &QGeoTiledMapReplyQGC::aborted, reply, &QNetworkReply::abort);
215}
216
218{
219 QGeoTiledMapReply::abort();
220}
QString errorString
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QGCMapEngine * getQGCMapEngine()
std::shared_ptr< const ElevationProvider > SharedElevationProvider
std::shared_ptr< const MapProvider > SharedMapProvider
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 QString getProviderTypeFromQtMapId(int qtMapId)
static std::shared_ptr< const MapProvider > getMapProviderFromQtMapId(int qtMapId)
QByteArray format(const QList< LogEntry > &entries, int fmt)
void ignoreSslErrorsIfNeeded(QNetworkReply *reply)
bool isHttpSuccess(int statusCode)
Check if HTTP status indicates success (2xx)
bool isInternetAvailable()
Check if internet is reachable (online state, stricter than isNetworkAvailable)
QByteArray img
QString format