QGIS API Documentation 4.3.0-Master (389d690bb77)
Loading...
Searching...
No Matches
qgsstaccontroller.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsstaccontroller.cpp
3 ---------------------
4 begin : August 2024
5 copyright : (C) 2024 by Stefanos Natsis
6 email : uclaros at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsstaccontroller.h"
17
18#include "qgsapplication.h"
19#include "qgsauthmanager.h"
20#include "qgslogger.h"
23#include "qgsstaccatalog.h"
24#include "qgsstaccollection.h"
26#include "qgsstacitem.h"
28#include "qgsstacparser.h"
29
30#include <QFile>
31#include <QString>
32
33#include "moc_qgsstaccontroller.cpp"
34
35using namespace Qt::StringLiterals;
36
38{
39 qDeleteAll( mReplies );
40 qDeleteAll( mFetchedStacObjects );
41 qDeleteAll( mFetchedItemCollections );
42 qDeleteAll( mFetchedCollections );
43}
44
46{
47 QNetworkReply *reply = fetchAsync( url );
48 connect( reply, &QNetworkReply::finished, this, &QgsStacController::handleStacObjectReply );
49
50 return reply->property( "requestId" ).toInt();
51}
52
54{
55 QNetworkReply *reply = fetchAsync( url );
56 connect( reply, &QNetworkReply::finished, this, &QgsStacController::handleItemCollectionReply );
57
58 return reply->property( "requestId" ).toInt();
59}
60
62{
63 QNetworkReply *reply = fetchAsync( url );
64 connect( reply, &QNetworkReply::finished, this, &QgsStacController::handleCollectionsReply );
65
66 return reply->property( "requestId" ).toInt();
67}
68
70{
71 for ( QNetworkReply *reply : std::as_const( mReplies ) )
72 {
73 reply->abort();
74 reply->deleteLater();
75 }
76 mReplies.clear();
77}
78
79QNetworkReply *QgsStacController::fetchAsync( const QUrl &url )
80{
81 QNetworkRequest req( url );
82 mHeaders.updateNetworkRequest( req );
83 QgsSetRequestInitiatorClass( req, u"QgsStacController"_s );
84 req.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
85 req.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
86
87 if ( !mAuthCfg.isEmpty() )
88 {
90 }
91
92 QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
93
94 QNetworkReply *reply = nam->get( req );
95
96 if ( !mAuthCfg.isEmpty() )
97 {
99 }
100
101 mReplies.append( reply );
102
103 QgsDebugMsgLevel( u"Fired STAC request with id %1"_s.arg( reply->property( "requestId" ).toInt() ), 2 );
104
105 return reply;
106}
107
108void QgsStacController::handleStacObjectReply()
109{
110 QNetworkReply *reply = qobject_cast<QNetworkReply *>( QObject::sender() );
111 if ( !reply )
112 return;
113
114 const int requestId = reply->property( "requestId" ).toInt();
115 QgsDebugMsgLevel( u"Finished STAC request with id %1"_s.arg( requestId ), 2 );
116
117 if ( reply->error() != QNetworkReply::NoError )
118 {
119 const QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
120 const QString errorMessage = contentType.startsWith( "text/plain"_L1 ) ? reply->readAll() : reply->errorString();
121
122 emit finishedStacObjectRequest( requestId, errorMessage );
123 reply->deleteLater();
124 mReplies.removeOne( reply );
125 return;
126 }
127
128 const QByteArray data = reply->readAll();
129 QgsStacParser parser;
130 parser.setData( data );
131 parser.setBaseUrl( reply->url() );
132
133 QString error;
134 std::unique_ptr< QgsStacObject > object;
135 switch ( parser.type() )
136 {
138 object = parser.catalog();
139 break;
141 object = parser.collection();
142 break;
144 object = parser.item();
145 break;
147 object = nullptr;
148 error = parser.error().isEmpty() ? u"Parsed STAC data is not a Catalog, Collection or Item"_s : parser.error();
149 break;
150 }
151 mFetchedStacObjects.insert( requestId, object.release() );
152 emit finishedStacObjectRequest( requestId, error.isEmpty() ? parser.error() : error );
153 reply->deleteLater();
154 mReplies.removeOne( reply );
155}
156
157void QgsStacController::handleItemCollectionReply()
158{
159 QNetworkReply *reply = qobject_cast<QNetworkReply *>( QObject::sender() );
160 if ( !reply )
161 return;
162
163 const int requestId = reply->property( "requestId" ).toInt();
164 QgsDebugMsgLevel( u"Finished STAC request with id %1"_s.arg( requestId ), 2 );
165
166 if ( reply->error() != QNetworkReply::NoError )
167 {
168 const QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
169 const QString errorMessage = contentType.startsWith( "text/plain"_L1 ) ? reply->readAll() : reply->errorString();
170
171 emit finishedItemCollectionRequest( requestId, errorMessage );
172 reply->deleteLater();
173 mReplies.removeOne( reply );
174 return;
175 }
176
177 const QByteArray data = reply->readAll();
178 QgsStacParser parser;
179 parser.setData( data );
180 parser.setBaseUrl( reply->url() );
181
182 std::unique_ptr<QgsStacItemCollection> fc = parser.itemCollection();
183 mFetchedItemCollections.insert( requestId, fc.release() );
184 emit finishedItemCollectionRequest( requestId, parser.error() );
185 reply->deleteLater();
186 mReplies.removeOne( reply );
187}
188
189void QgsStacController::handleCollectionsReply()
190{
191 QNetworkReply *reply = qobject_cast<QNetworkReply *>( QObject::sender() );
192 if ( !reply )
193 return;
194
195 const int requestId = reply->property( "requestId" ).toInt();
196 QgsDebugMsgLevel( u"Finished STAC request with id %1"_s.arg( requestId ), 2 );
197
198 if ( reply->error() != QNetworkReply::NoError )
199 {
200 const QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
201 const QString errorMessage = contentType.startsWith( "text/plain"_L1 ) ? reply->readAll() : reply->errorString();
202
203 emit finishedCollectionsRequest( requestId, errorMessage );
204 reply->deleteLater();
205 mReplies.removeOne( reply );
206 return;
207 }
208
209 const QByteArray data = reply->readAll();
210 QgsStacParser parser;
211 parser.setData( data );
212 parser.setBaseUrl( reply->url() );
213
214 QgsStacCollectionList *cols = parser.collections();
215 mFetchedCollections.insert( requestId, cols );
216 emit finishedCollectionsRequest( requestId, parser.error() );
217 reply->deleteLater();
218 mReplies.removeOne( reply );
219}
220
221template<class T> std::unique_ptr<T> QgsStacController::takeStacObject( int requestId )
222{
223 std::unique_ptr< QgsStacObject > obj( mFetchedStacObjects.take( requestId ) );
224
225 if ( T *downCastObj = dynamic_cast< T * >( obj.get() ) )
226 {
227 ( void ) obj.release();
228 return std::unique_ptr< T >( downCastObj );
229 }
230
231 return nullptr;
232}
233template CORE_EXPORT std::unique_ptr< QgsStacItem > QgsStacController::takeStacObject<QgsStacItem>( int requestId );
234template CORE_EXPORT std::unique_ptr< QgsStacCatalog > QgsStacController::takeStacObject<QgsStacCatalog>( int requestId );
235template CORE_EXPORT std::unique_ptr< QgsStacObject > QgsStacController::takeStacObject<QgsStacObject>( int requestId );
236
237std::unique_ptr< QgsStacItemCollection > QgsStacController::takeItemCollection( int requestId )
238{
239 std::unique_ptr< QgsStacItemCollection > col( mFetchedItemCollections.take( requestId ) );
240 return col;
241}
242
243std::unique_ptr< QgsStacCollectionList > QgsStacController::takeCollections( int requestId )
244{
245 std::unique_ptr< QgsStacCollectionList > cols( mFetchedCollections.take( requestId ) );
246 return cols;
247}
248
249std::unique_ptr< QgsStacItemCollection > QgsStacController::fetchItemCollection( const QUrl &url, QString *error )
250{
251 QgsNetworkReplyContent content = fetchBlocking( url );
252
253 if ( content.error() != QNetworkReply::NoError )
254 {
255 if ( error )
256 *error = content.errorString();
257
258 return nullptr;
259 }
260
261 const QByteArray data = content.content();
262
263 QgsStacParser parser;
264 parser.setData( data );
265 parser.setBaseUrl( url );
266 std::unique_ptr< QgsStacItemCollection > ic( parser.itemCollection() );
267
268 if ( error )
269 *error = parser.error();
270
271 return ic;
272}
273
274std::unique_ptr< QgsStacCollectionList > QgsStacController::fetchCollections( const QUrl &url, QString *error )
275{
276 QgsNetworkReplyContent content = fetchBlocking( url );
277
278 if ( content.error() != QNetworkReply::NoError )
279 {
280 if ( error )
281 *error = content.errorString();
282
283 return nullptr;
284 }
285
286 const QByteArray data = content.content();
287
288 QgsStacParser parser;
289 parser.setData( data );
290 std::unique_ptr< QgsStacCollectionList > col( parser.collections() );
291
292 if ( error )
293 *error = parser.error();
294
295 return col;
296}
297
298QgsNetworkReplyContent QgsStacController::fetchBlocking( const QUrl &url )
299{
300 QNetworkRequest req( url );
301 mHeaders.updateNetworkRequest( req );
302 QgsSetRequestInitiatorClass( req, u"QgsStacController"_s );
303 req.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
304 req.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
305
307
308 return nam->blockingGet( req, mAuthCfg );
309}
310
312{
313 return mAuthCfg;
314}
315
317{
318 mAuthCfg = authCfg;
319}
320
321std::unique_ptr<QgsStacCatalog> QgsStacController::openLocalCatalog( const QString &fileName ) const
322{
323 QFile file( fileName );
324 const bool ok = file.open( QIODevice::ReadOnly );
325 if ( !ok )
326 {
327 QgsDebugError( u"Could not open file: "_s.arg( fileName ) );
328 return nullptr;
329 }
330
331 QgsStacParser parser;
332 parser.setData( file.readAll() );
333 parser.setBaseUrl( fileName );
334 return parser.catalog();
335}
336
337
338std::unique_ptr<QgsStacCollection> QgsStacController::openLocalCollection( const QString &fileName ) const
339{
340 QFile file( fileName );
341 const bool ok = file.open( QIODevice::ReadOnly );
342 if ( !ok )
343 {
344 QgsDebugError( u"Could not open file: "_s.arg( fileName ) );
345 return nullptr;
346 }
347
348 QgsStacParser parser;
349 parser.setData( file.readAll() );
350 parser.setBaseUrl( fileName );
351 return parser.collection();
352}
353
354std::unique_ptr<QgsStacItem> QgsStacController::openLocalItem( const QString &fileName ) const
355{
356 QFile file( fileName );
357 const bool ok = file.open( QIODevice::ReadOnly );
358 if ( !ok )
359 {
360 QgsDebugError( u"Could not open file: "_s.arg( fileName ) );
361 return nullptr;
362 }
363
364 QgsStacParser parser;
365 parser.setData( file.readAll() );
366 parser.setBaseUrl( fileName );
367 return parser.item();
368}
369
370template<class T> std::unique_ptr<T> QgsStacController::fetchStacObject( const QUrl &url, QString *error )
371{
372 QgsNetworkReplyContent content = fetchBlocking( url );
373
374 if ( content.error() != QNetworkReply::NoError )
375 {
376 if ( error )
377 *error = content.errorString();
378
379 return nullptr;
380 }
381
382 const QByteArray data = content.content();
383
384 QgsStacParser parser;
385 parser.setData( data );
386 parser.setBaseUrl( url );
387 std::unique_ptr< QgsStacObject > object;
388 switch ( parser.type() )
389 {
391 object = parser.catalog();
392 break;
394 object = parser.collection();
395 break;
397 object = parser.item();
398 break;
400 break;
401 }
402
403 std::unique_ptr< T > res;
404 if ( T *castObject = dynamic_cast< T * >( object.get() ) )
405 {
406 ( void ) object.release();
407 res.reset( castObject );
408 }
409 else
410 {
411 QgsDebugError( "Retrieved STAC object could not be cast to expected type" );
412 }
413
414 if ( error )
415 *error = parser.error();
416
417 return res;
418}
419
420template CORE_EXPORT std::unique_ptr< QgsStacItem > QgsStacController::fetchStacObject<QgsStacItem>( const QUrl &url, QString *error );
421template CORE_EXPORT std::unique_ptr< QgsStacCollection > QgsStacController::fetchStacObject<QgsStacCollection>( const QUrl &url, QString *error );
422template CORE_EXPORT std::unique_ptr< QgsStacCatalog > QgsStacController::fetchStacObject<QgsStacCatalog>( const QUrl &url, QString *error );
@ Item
STAC item.
Definition qgis.h:6889
@ Unknown
Type is not known.
Definition qgis.h:6886
@ Catalog
STAC catalog.
Definition qgis.h:6887
@ Collection
STAC collection.
Definition qgis.h:6888
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
bool updateNetworkReply(QNetworkReply *reply, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkReply with an authentication config (used to skip known SSL errors,...
bool updateNetworkRequest(QNetworkRequest &request) const
Updates a request by adding all the HTTP headers.
QNetworkAccessManager with additional QGIS specific logic.
static QgsNetworkReplyContent blockingGet(QNetworkRequest &request, const QString &authCfg=QString(), bool forceRefresh=false, QgsFeedback *feedback=nullptr, Qgis::NetworkRequestFlags flags=Qgis::NetworkRequestFlags())
Posts a GET request to obtain the contents of the target request and returns a new QgsNetworkReplyCon...
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QString errorString() const
Returns the error text for the reply, or an empty string if no error was encountered.
QByteArray content() const
Returns the reply content.
QNetworkReply::NetworkError error() const
Returns the reply's error message, or QNetworkReply::NoError if no error was encountered.
int fetchStacObjectAsync(const QUrl &url)
Initiates an asynchronous request for a STAC object using the url and returns an associated request i...
std::unique_ptr< QgsStacItemCollection > takeItemCollection(int requestId)
Returns the feature collection fetched with the specified requestId It should be used after the finis...
std::unique_ptr< QgsStacCollectionList > takeCollections(int requestId)
Returns the collections collection fetched with the specified requestId It should be used after the f...
std::unique_ptr< QgsStacCollection > openLocalCollection(const QString &fileName) const
Returns a STAC Collection by parsing a local file The caller takes ownership of the returned collecti...
int fetchCollectionsAsync(const QUrl &url)
Initiates an asynchronous request for a Collections collection using the url and returns an associate...
std::unique_ptr< QgsStacCatalog > openLocalCatalog(const QString &fileName) const
Returns a STAC Catalog by parsing a local file The caller takes ownership of the returned catalog.
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
std::unique_ptr< QgsStacItem > openLocalItem(const QString &fileName) const
Returns a STAC Item by parsing a local file The caller takes ownership of the returned item.
std::unique_ptr< QgsStacItemCollection > fetchItemCollection(const QUrl &url, QString *error=nullptr)
Fetches a feature collection from url using a blocking network request.
~QgsStacController() override
Default destructor.
int fetchItemCollectionAsync(const QUrl &url)
Initiates an asynchronous request for a feature collection using the url and returns an associated re...
QString authCfg() const
Returns the authentication config id which will be used during the request.
void finishedCollectionsRequest(int id, QString errorMessage)
This signal is fired when an async request initiated with fetchCollectionsAsync is finished.
std::unique_ptr< T > fetchStacObject(const QUrl &url, QString *error=nullptr)
Fetches a STAC object from url using a blocking network request.
void finishedItemCollectionRequest(int id, QString errorMessage)
This signal is fired when an async request initiated with fetchItemCollectionAsync is finished.
std::unique_ptr< T > takeStacObject(int requestId)
Returns the STAC object fetched with the specified requestId.
void cancelPendingAsyncRequests()
Cancels all pending async requests.
std::unique_ptr< QgsStacCollectionList > fetchCollections(const QUrl &url, QString *error=nullptr)
Fetches collections from url using a blocking network request.
void finishedStacObjectRequest(int id, QString errorMessage)
This signal is fired when an async request initiated with fetchStacObjectAsync is finished.
SpatioTemporal Asset Catalog JSON parser.
QString error() const
Returns the last parsing error.
QgsStacCollectionList * collections()
Returns the parsed STAC API Collections If parsing failed, nullptr is returned The caller takes owner...
void setBaseUrl(const QUrl &url)
Sets the base url that will be used to resolve relative links.
std::unique_ptr< QgsStacItem > item()
Returns the parsed STAC Item If parsing failed, nullptr is returned The caller takes ownership of the...
std::unique_ptr< QgsStacItemCollection > itemCollection()
Returns the parsed STAC API Item Collection If parsing failed, nullptr is returned The caller takes o...
std::unique_ptr< QgsStacCatalog > catalog()
Returns the parsed STAC Catalog If parsing failed, nullptr is returned The caller takes ownership of ...
Qgis::StacObjectType type() const
Returns the type of the parsed object.
std::unique_ptr< QgsStacCollection > collection()
Returns the parsed STAC Collection If parsing failed, nullptr is returned The caller takes ownership ...
void setData(const QByteArray &data)
Sets the JSON data to be parsed.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71
#define QgsSetRequestInitiatorClass(request, _class)