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 if (QNetworkReply* const reply = _networkReply.data()) {
34 _networkReply.clear();
35 (void) disconnect(reply, nullptr, this, nullptr);
36 reply->abort();
37 reply->deleteLater();
38 }
39 qCDebug(QGeoTiledMapReplyQGCLog) << this;
40}
41
43{
44 if (m_initialized) {
45 return true;
46 }
47
48 m_initialized = true;
49
50 _initDataFromResources();
51
52 (void) connect(this, &QGeoTiledMapReplyQGC::errorOccurred, this, [this](QGeoTiledMapReply::Error error, const QString &errorString) {
53 qCWarning(QGeoTiledMapReplyQGCLog) << error << errorString;
54 setMapImageData(_badTile);
55 setMapImageFormat(QStringLiteral("png"));
56 setCached(false);
57 }, Qt::AutoConnection);
58
59 QGCFetchTileTask *task = QGeoFileTileCacheQGC::createFetchTileTask(UrlFactory::getProviderTypeFromQtMapId(tileSpec().mapId()), tileSpec().x(), tileSpec().y(), tileSpec().zoom());
60 if (!task) {
61 qCWarning(QGeoTiledMapReplyQGCLog) << "Failed to create fetch tile task";
62 m_initialized = false;
63 return false;
64 }
65 (void) connect(task, &QGCFetchTileTask::tileFetched, this, &QGeoTiledMapReplyQGC::_cacheReply);
66 (void) connect(task, &QGCMapTask::error, this, &QGeoTiledMapReplyQGC::_cacheError);
67 if (!getQGCMapEngine()->addTask(task)) {
68 task->deleteLater();
69 m_initialized = false;
70 return false;
71 }
72
73 return true;
74}
75
76void QGeoTiledMapReplyQGC::_initDataFromResources()
77{
78 if (_bingNoTileImage.isEmpty()) {
79 QFile file(":/res/BingNoTileBytes.dat");
80 if (file.open(QFile::ReadOnly)) {
81 _bingNoTileImage = file.readAll();
82 file.close();
83 }
84 }
85
86 if (_badTile.isEmpty()) {
87 QFile file(":/res/images/notile.png");
88 if (file.open(QFile::ReadOnly)) {
89 _badTile = file.readAll();
90 file.close();
91 }
92 }
93}
94
95void QGeoTiledMapReplyQGC::_networkReplyFinished()
96{
97 QNetworkReply* const reply = qobject_cast<QNetworkReply*>(sender());
98 if (!reply) {
99 setError(QGeoTiledMapReply::UnknownError, tr("Unexpected Error"));
100 return;
101 }
102 if (reply == _networkReply) {
103 _networkReply.clear();
104 }
105 reply->deleteLater();
106
107 if (reply->error() != QNetworkReply::NoError) {
108 return;
109 }
110
111 if (!reply->isOpen()) {
112 setError(QGeoTiledMapReply::ParseError, tr("Empty Reply"));
113 return;
114 }
115
116 const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
117 if (!QGCNetworkHelper::isHttpSuccess(statusCode)) {
118 setError(QGeoTiledMapReply::CommunicationError, reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
119 return;
120 }
121
122 QByteArray image = reply->readAll();
123 if (image.isEmpty()) {
124 setError(QGeoTiledMapReply::ParseError, tr("Image is Empty"));
125 return;
126 }
127
128 const SharedMapProvider mapProvider = UrlFactory::getMapProviderFromQtMapId(tileSpec().mapId());
129 if (!mapProvider) {
130 setError(QGeoTiledMapReply::UnknownError, tr("Invalid Map Provider"));
131 return;
132 }
133
134 if (mapProvider->isBingProvider() && (image == _bingNoTileImage)) {
135 setError(QGeoTiledMapReply::CommunicationError, tr("Bing Tile Above Zoom Level"));
136 return;
137 }
138
139 if (mapProvider->isElevationProvider()) {
140 const SharedElevationProvider elevationProvider = std::dynamic_pointer_cast<const ElevationProvider>(mapProvider);
141 image = elevationProvider->serialize(image);
142 if (image.isEmpty()) {
143 setError(QGeoTiledMapReply::ParseError, tr("Failed to Serialize Terrain Tile"));
144 return;
145 }
146 }
147 setMapImageData(image);
148
149 const QString format = mapProvider->getImageFormat(image);
150 if (format.isEmpty()) {
151 setError(QGeoTiledMapReply::ParseError, tr("Unknown Format"));
152 return;
153 }
154 setMapImageFormat(format);
155
156 QGeoFileTileCacheQGC::cacheTile(mapProvider->getMapName(), tileSpec().x(), tileSpec().y(), tileSpec().zoom(), image, format);
157
158 setFinished(true);
159}
160
161void QGeoTiledMapReplyQGC::_networkReplyError(QNetworkReply::NetworkError error)
162{
163 if (error != QNetworkReply::OperationCanceledError) {
164 const QNetworkReply* const reply = qobject_cast<const QNetworkReply*>(sender());
165 if (!reply) {
166 setError(QGeoTiledMapReply::CommunicationError, tr("Invalid Reply"));
167 } else {
168 setError(QGeoTiledMapReply::CommunicationError, reply->errorString());
169 }
170 } else {
171 setFinished(true);
172 }
173}
174
175void QGeoTiledMapReplyQGC::_networkReplySslErrors(const QList<QSslError> &errors)
176{
177 QString errorString;
178 for (const QSslError &error : errors) {
179 if (!errorString.isEmpty()) {
180 (void) errorString.append('\n');
181 }
182 (void) errorString.append(error.errorString());
183 }
184
185 if (!errorString.isEmpty()) {
186 setError(QGeoTiledMapReply::CommunicationError, errorString);
187 }
188}
189
190void QGeoTiledMapReplyQGC::_cacheReply(QGCCacheTile *tile)
191{
192 if (tile) {
193 setMapImageData(tile->img);
194 setMapImageFormat(tile->format);
195 setCached(true);
196 setFinished(true);
197 delete tile;
198 } else {
199 setError(QGeoTiledMapReply::UnknownError, tr("Invalid Cache Tile"));
200 }
201}
202
203void QGeoTiledMapReplyQGC::_cacheError(QGCMapTask::TaskType type, QStringView errorString)
204{
205 Q_UNUSED(errorString);
206
208 setError(QGeoTiledMapReply::UnknownError, tr("Unexpected Cache Error"));
209 return;
210 }
211
212 if (!_networkManager) {
213 setError(QGeoTiledMapReply::CommunicationError, tr("Network Manager Not Available"));
214 return;
215 }
216
218 setError(QGeoTiledMapReply::CommunicationError, tr("Network Not Available"));
219 return;
220 }
221
222 _request.setOriginatingObject(this);
223
224 _networkReply = _networkManager->get(_request);
225 if (!_networkReply) {
226 setError(QGeoTiledMapReply::CommunicationError, tr("Failed to Create Network Reply"));
227 return;
228 }
229
230 QNetworkReply* const reply = _networkReply.data();
232
233 (void) connect(reply, &QNetworkReply::finished, this, &QGeoTiledMapReplyQGC::_networkReplyFinished);
234 (void) connect(reply, &QNetworkReply::errorOccurred, this, &QGeoTiledMapReplyQGC::_networkReplyError);
235 (void) connect(reply, &QNetworkReply::sslErrors, this, &QGeoTiledMapReplyQGC::_networkReplySslErrors);
236 (void) connect(this, &QGeoTiledMapReplyQGC::aborted, reply, &QNetworkReply::abort);
237}
238
240{
241 QGeoTiledMapReply::abort();
242}
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