17 #include "qgssettings.h"
23 #include <QNetworkCacheMetaData>
25 #include <QJsonDocument>
26 #include <QJsonObject>
28 #include <QDomDocument>
29 #include <QRegularExpression>
34 , mForceRefresh( forceRefresh )
49 mGeoNodeReply->deleteLater();
50 mGeoNodeReply =
nullptr;
56 request( QStringLiteral(
"/api/layers/" ) );
57 QObject *obj =
new QObject(
this );
61 QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
62 if ( mError.isEmpty() )
64 layers = parseLayers( this->lastResponse() );
74 QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
78 QObject *obj =
new QObject(
this );
84 loop.exec( QEventLoop::ExcludeUserInputEvents );
92 bool success =
requestBlocking( QStringLiteral(
"/api/layers?name=" ) + layerName );
98 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
99 const QJsonObject jsonObject = jsonDocument.object();
100 const QList<QVariant> layers = jsonObject.toVariantMap().value( QStringLiteral(
"objects" ) ).toList();
101 if ( layers.count() < 1 )
105 QString defaultStyleUrl = layers.at( 0 ).toMap().value( QStringLiteral(
"default_style" ) ).toString();
107 defaultStyle = retrieveStyle( defaultStyleUrl );
115 QList<QgsGeoNodeStyle> geoNodeStyles;
116 bool success =
requestBlocking( QStringLiteral(
"/api/styles?layer__name=" ) + layerName );
119 return geoNodeStyles;
122 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
123 const QJsonObject jsobObject = jsonDocument.object();
124 const QList<QVariant> styles = jsobObject.toVariantMap().value( QStringLiteral(
"objects" ) ).toList();
126 for (
const QVariant &style : styles )
128 const QVariantMap styleMap = style.toMap();
129 QString styleUrl = styleMap.value( QStringLiteral(
"resource_uri" ) ).toString();
131 if ( !geoNodeStyle.
name.isEmpty() )
133 geoNodeStyles.append( geoNodeStyle );
137 return geoNodeStyles;
143 QString endPoint = QStringLiteral(
"/api/styles/" ) + styleId;
145 return retrieveStyle( endPoint );
148 void QgsGeoNodeRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
150 QString msg = tr(
"%1 of %2 bytes of request downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) );
165 void QgsGeoNodeRequest::replyFinished()
168 if ( !mIsAborted && mGeoNodeReply )
170 if ( mGeoNodeReply->error() == QNetworkReply::NoError )
173 QVariant redirect = mGeoNodeReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
174 if ( !redirect.isNull() )
177 emit
statusChanged( QStringLiteral(
"GeoNode request redirected." ) );
179 const QUrl &toUrl = redirect.toUrl();
180 if ( toUrl == mGeoNodeReply->url() )
182 mError = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
184 mHttpGeoNodeResponse.clear();
188 QNetworkRequest
request( toUrl );
190 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
191 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
193 mGeoNodeReply->deleteLater();
194 mGeoNodeReply =
nullptr;
196 QgsDebugMsgLevel( QStringLiteral(
"redirected getcapabilities: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 3 );
199 connect( mGeoNodeReply, &QNetworkReply::finished,
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
200 connect( mGeoNodeReply, &QNetworkReply::downloadProgress,
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
210 QNetworkCacheMetaData cmd = nam->cache()->metaData( mGeoNodeReply->request().url() );
212 QNetworkCacheMetaData::RawHeaderList hl;
213 const QNetworkCacheMetaData::RawHeaderList cmdHeaders = cmd.rawHeaders();
214 for (
const QNetworkCacheMetaData::RawHeader &h : cmdHeaders )
216 if ( h.first != QStringLiteral(
"Cache-Control" ) )
219 cmd.setRawHeaders( hl );
221 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
222 if ( cmd.expirationDate().isNull() )
224 QgsSettings settings;
225 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( settings.value( QStringLiteral(
"qgis/defaultCapabilitiesExpiry" ),
"24", QgsSettings::Providers ).toInt() * 60 * 60 ) );
228 nam->cache()->updateMetaData( cmd );
232 QgsDebugMsg( QStringLiteral(
"No cache for capabilities!" ) );
235 mHttpGeoNodeResponse = mGeoNodeReply->readAll();
237 if ( mHttpGeoNodeResponse.isEmpty() )
239 mError = tr(
"Empty capabilities: %1" ).arg( mGeoNodeReply->errorString() );
245 mError = tr(
"Request failed: %1" ).arg( mGeoNodeReply->errorString() );
247 mHttpGeoNodeResponse.clear();
253 mGeoNodeReply->deleteLater();
254 mGeoNodeReply =
nullptr;
260 QList<QgsGeoNodeRequest::ServiceLayerDetail> QgsGeoNodeRequest::parseLayers(
const QByteArray &layerResponse )
262 QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
263 if ( layerResponse.isEmpty() )
268 const QJsonDocument jsonDocument = QJsonDocument::fromJson( layerResponse );
269 const QJsonObject jsonObject = jsonDocument.object();
270 const QVariantMap jsonVariantMap = jsonObject.toVariantMap();
271 const QVariantList layerList = jsonVariantMap.value( QStringLiteral(
"objects" ) ).toList();
273 QString wmsURLFormat, wfsURLFormat, wcsURLFormat, xyzURLFormat;
275 for (
const QVariant &layer : std::as_const( layerList ) )
278 const QVariantMap layerMap = layer.toMap();
279 QVariantList layerLinks = layerMap.value( QStringLiteral(
"links" ) ).toList();
281 if ( layerMap.value( QStringLiteral(
"typename" ) ).toString().isEmpty() )
283 const QStringList splitUrl = layerMap.value( QStringLiteral(
"detail_url" ) ).toString().split(
'/' );
284 layerStruct.
typeName = !splitUrl.isEmpty() ? splitUrl.last() : QString();
286 layerStruct.
uuid = QUuid( layerMap.value( QStringLiteral(
"uuid" ) ).toString() );
287 layerStruct.
id = layerMap.value( QStringLiteral(
"id" ) ).toString();
288 layerStruct.
name = layerMap.value( QStringLiteral(
"name" ) ).toString();
289 layerStruct.
typeName = layerMap.value( QStringLiteral(
"typename" ) ).toString();
290 layerStruct.
title = layerMap.value( QStringLiteral(
"title" ) ).toString();
292 if ( ! layerMap.contains( QStringLiteral(
"links" ) ) )
294 if ( wmsURLFormat.isEmpty() && wfsURLFormat.isEmpty() && wcsURLFormat.isEmpty() && xyzURLFormat.isEmpty() )
296 bool success =
requestBlocking( QStringLiteral(
"/api/layers/" ) + layerStruct.
id );
299 const QJsonDocument resourceUriDocument = QJsonDocument::fromJson( this->
lastResponse() );
300 const QJsonObject resourceUriObject = resourceUriDocument.object();
301 const QVariantMap resourceUriMap = resourceUriObject.toVariantMap();
302 QVariantList resourceUriLinks = resourceUriMap.value( QStringLiteral(
"links" ) ).toList();
304 tempLayerStruct =
parseOwsUrl( tempLayerStruct, resourceUriLinks );
306 if ( tempLayerStruct.
wmsURL.isEmpty() && tempLayerStruct.
wfsURL.isEmpty() && tempLayerStruct.
wcsURL.isEmpty() && tempLayerStruct.
xyzURL.isEmpty() )
311 switch ( tempLayerStruct.
server )
315 wmsURLFormat = ! tempLayerStruct.
wmsURL.isEmpty() ? tempLayerStruct.
wmsURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
316 wfsURLFormat = ! tempLayerStruct.
wfsURL.isEmpty() ? tempLayerStruct.
wfsURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
317 wcsURLFormat = ! tempLayerStruct.
wcsURL.isEmpty() ? tempLayerStruct.
wcsURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
318 xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
323 wmsURLFormat = ! tempLayerStruct.
wmsURL.isEmpty() ? tempLayerStruct.
wmsURL : QString();
324 wfsURLFormat = ! tempLayerStruct.
wfsURL.isEmpty() ? tempLayerStruct.
wfsURL : QString();
325 wcsURLFormat = ! tempLayerStruct.
wcsURL.isEmpty() ? tempLayerStruct.
wcsURL : QString();
326 xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
339 layerStruct.
wmsURL = wmsURLFormat.contains(
"%1" ) ? wmsURLFormat.arg( layerStruct.
name ) : wmsURLFormat;
340 layerStruct.
wfsURL = wfsURLFormat.contains(
"%1" ) ? wfsURLFormat.arg( layerStruct.
name ) : wfsURLFormat;
341 layerStruct.
wcsURL = wcsURLFormat.contains(
"%1" ) ? wcsURLFormat.arg( layerStruct.
name ) : wcsURLFormat;
342 layerStruct.
xyzURL = xyzURLFormat.contains(
"%1" ) ? xyzURLFormat.arg( layerStruct.
name ) : xyzURLFormat;
346 layerStruct =
parseOwsUrl( layerStruct, layerLinks );
348 layers.append( layerStruct );
357 for (
const QVariant &link : layerLinks )
359 const QVariantMap linkMap = link.toMap();
360 if ( linkMap.contains( QStringLiteral(
"link_type" ) ) )
362 if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WMS" ) )
364 urlFound = layerStruct.
wmsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
366 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WFS" ) )
368 urlFound = layerStruct.
wfsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
370 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WCS" ) )
372 urlFound = layerStruct.
wcsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
374 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"image" ) )
376 if ( linkMap.contains( QStringLiteral(
"name" ) ) && linkMap.value( QStringLiteral(
"name" ) ) == QLatin1String(
"Tiles" ) )
378 urlFound = layerStruct.
xyzURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
383 switch ( layerStruct.
server )
400 QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle(
const QString &styleUrl )
409 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
410 const QJsonObject jsonObject = jsonDocument.object();
412 const QVariantMap jsonMap = jsonObject.toVariantMap();
413 geoNodeStyle.
id = jsonMap.value( QStringLiteral(
"id" ) ).toString();
414 geoNodeStyle.
name = jsonMap.value( QStringLiteral(
"name" ) ).toString();
415 geoNodeStyle.
title = jsonMap.value( QStringLiteral(
"title" ) ).toString();
416 geoNodeStyle.
styleUrl = jsonMap.value( QStringLiteral(
"style_url" ) ).toString();
439 if ( layers.empty() )
447 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
451 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
455 else if ( QString::compare( serviceType, QStringLiteral(
"wcs" ), Qt::CaseInsensitive ) == 0 )
459 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
467 if ( !url.contains( QLatin1String(
"://" ) ) )
471 if ( !urls.contains( url ) )
486 if ( layers.empty() )
495 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
499 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
503 else if ( QString::compare( serviceType, QStringLiteral(
"wcs" ), Qt::CaseInsensitive ) == 0 )
507 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
515 QString layerName = layer.name;
516 if ( !url.contains( QLatin1String(
"://" ) ) )
520 if ( !urls.contains( url ) )
522 urls.insert( layerName, url );
534 QString url = endPoint.startsWith( mBaseUrl ) ? endPoint : mBaseUrl + endPoint;
536 setProtocol( url.split( QStringLiteral(
"://" ) ).at( 0 ) );
537 QUrl layerUrl( url );
542 mGeoNodeReply = requestUrl( url );
543 connect( mGeoNodeReply, &QNetworkReply::finished,
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
544 connect( mGeoNodeReply, &QNetworkReply::downloadProgress,
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
553 loop.exec( QEventLoop::ExcludeUserInputEvents );
555 return mError.isEmpty();
558 QNetworkReply *QgsGeoNodeRequest::requestUrl(
const QString &url )
560 QNetworkRequest
request( url );
561 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, 1 );
566 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
567 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
void layersFetched(const QList< QgsGeoNodeRequest::ServiceLayerDetail > &layers)
Emitted when the result of a fetchLayers call has been received and processed.
bool requestBlocking(const QString &endPoint)
Triggers a new request to the GeoNode server, with the requested endPoint.
QgsGeoNodeRequest(const QString &baseUrl, bool forceRefresh, QObject *parent=nullptr)
Constructor for QgsGeoNodeRequest.
void requestFinished()
Emitted when the existing request has been completed.
QList< QgsGeoNodeStyle > fetchStylesBlocking(const QString &layerName)
Requests the list of available styles for the layer with matching layerName from the server.
@ Geoserver
Geoserver used as backend.
@ Unknown
Unknown backend.
@ QgisServer
QGIS server used as backend.
void fetchLayers()
Triggers a new request to fetch the list of available layers from the server.
QgsGeoNodeStyle fetchStyleBlocking(const QString &styleId)
Requests the details for the style with matching styleId from the server.
QList< QgsGeoNodeRequest::ServiceLayerDetail > fetchLayersBlocking()
Requests the list of available layers from the server.
QgsGeoNodeStyle fetchDefaultStyleBlocking(const QString &layerName)
Requests the default style for the layer with matching layerName from the server.
QStringList fetchServiceUrlsBlocking(const QString &serviceType)
Requests the list of unique URLs for available services with matching serviceType from the server.
QgsGeoNodeRequest::ServiceLayerDetail parseOwsUrl(QgsGeoNodeRequest::ServiceLayerDetail &layerStruct, const QVariantList &layerLinks)
Returns the updated ServiceLayerDetail struct with WMS/WFS/XYZ url.
QgsStringMap fetchServiceUrlDataBlocking(const QString &serviceType)
Obtains a map of layer name to URL for available services with matching serviceType from the server.
void statusChanged(const QString &statusQString)
Emitted when the status of an ongoing request is changed.
QString protocol() const
Returns the network protocol (e.g.
QByteArray lastResponse() const
Returns the most recent response obtained from the server.
void request(const QString &endPoint)
Triggers a new request to the GeoNode server, with the requested endPoint.
void abort()
Aborts any active network request immediately.
void setProtocol(const QString &protocol)
Sets the network protocol (e.g.
~QgsGeoNodeRequest() override
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
network access manager for QGIS
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
QMap< QString, QString > QgsStringMap
#define QgsDebugMsgLevel(str, level)
#define QgsSetRequestInitiatorClass(request, _class)
Service layer details for an individual layer from a GeoNode connection.
QString typeName
Layer type name.
QString wmsURL
WMS URL for layer.
QString title
Layer title.
QString wfsURL
WFS URL for layer.
QString xyzURL
XYZ tileserver URL for layer.
BackendServer server
Backend server (geoserver or qgis-server)
QUuid uuid
Unique identifier (generate on the client side, not at the GeoNode server)
QString wcsURL
WCS URL for layer.
Encapsulates information about a GeoNode layer style.
QString id
Unique style ID.
QString styleUrl
Associated URL.
QDomDocument body
DOM documenting containing style.
QString title
Style title.