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