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() )
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, xyzURLFormat;
275 for (
const QVariant &layer : qgis::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 = 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() && 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.
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 xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
322 wmsURLFormat = ! tempLayerStruct.
wmsURL.isEmpty() ? tempLayerStruct.
wmsURL : QString();
323 wfsURLFormat = ! tempLayerStruct.
wfsURL.isEmpty() ? tempLayerStruct.
wfsURL : QString();
324 xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
337 layerStruct.
wmsURL = wmsURLFormat.contains(
"%1" ) ? wmsURLFormat.arg( layerStruct.
name ) : wmsURLFormat;
338 layerStruct.
wfsURL = wfsURLFormat.contains(
"%1" ) ? wfsURLFormat.arg( layerStruct.
name ) : wfsURLFormat;
339 layerStruct.
xyzURL = xyzURLFormat.contains(
"%1" ) ? xyzURLFormat.arg( layerStruct.
name ) : xyzURLFormat;
343 layerStruct =
parseOwsUrl( layerStruct, layerLinks );
345 layers.append( layerStruct );
354 for (
const QVariant &link : layerLinks )
356 const QVariantMap linkMap = link.toMap();
357 if ( linkMap.contains( QStringLiteral(
"link_type" ) ) )
359 if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WMS" ) )
361 urlFound = layerStruct.
wmsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
363 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WFS" ) )
365 urlFound = layerStruct.
wfsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
367 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"image" ) )
369 if ( linkMap.contains( QStringLiteral(
"name" ) ) && linkMap.value( QStringLiteral(
"name" ) ) == QLatin1String(
"Tiles" ) )
371 urlFound = layerStruct.
xyzURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
376 switch ( layerStruct.
server )
393 QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle(
const QString &styleUrl )
402 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
403 const QJsonObject jsonObject = jsonDocument.object();
405 const QVariantMap jsonMap = jsonObject.toVariantMap();
406 geoNodeStyle.
id = jsonMap.value( QStringLiteral(
"id" ) ).toString();
407 geoNodeStyle.
name = jsonMap.value( QStringLiteral(
"name" ) ).toString();
408 geoNodeStyle.
title = jsonMap.value( QStringLiteral(
"title" ) ).toString();
409 geoNodeStyle.
styleUrl = jsonMap.value( QStringLiteral(
"style_url" ) ).toString();
432 if ( layers.empty() )
440 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
444 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
448 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
456 if ( !url.contains( QLatin1String(
"://" ) ) )
460 if ( !urls.contains( url ) )
475 if ( layers.empty() )
484 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
488 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
492 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
500 QString layerName = layer.name;
501 if ( !url.contains( QLatin1String(
"://" ) ) )
505 if ( !urls.contains( url ) )
507 urls.insert( layerName, url );
519 QString url = endPoint.startsWith( mBaseUrl ) ? endPoint : mBaseUrl + endPoint;
521 setProtocol( url.split( QStringLiteral(
"://" ) ).at( 0 ) );
522 QUrl layerUrl( url );
527 mGeoNodeReply = requestUrl( url );
528 connect( mGeoNodeReply, &QNetworkReply::finished,
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
529 connect( mGeoNodeReply, &QNetworkReply::downloadProgress,
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
538 loop.exec( QEventLoop::ExcludeUserInputEvents );
540 return mError.isEmpty();
543 QNetworkReply *QgsGeoNodeRequest::requestUrl(
const QString &url )
545 QNetworkRequest
request( url );
546 request.setAttribute( QNetworkRequest::FollowRedirectsAttribute,
true );
551 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
552 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::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.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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)
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.