24#include <QNetworkCacheMetaData>
26#include <QJsonDocument>
29#include <QDomDocument>
30#include <QRegularExpression>
35 , mForceRefresh( forceRefresh )
50 mGeoNodeReply->deleteLater();
51 mGeoNodeReply =
nullptr;
57 request( QStringLiteral(
"/api/layers/" ) );
59 QObject *obj =
new QObject(
this );
62 if ( !mParsingLayers )
64 mParsingLayers =
true;
65 QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
66 if ( mError.isEmpty() )
68 layers = parseLayers( lastResponse() );
71 mParsingLayers =
false;
79 QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
82 QObject *obj =
new QObject(
this );
89 loop.exec( QEventLoop::ExcludeUserInputEvents );
97 bool success =
requestBlocking( QStringLiteral(
"/api/layers?name=" ) + layerName );
103 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
104 const QJsonObject jsonObject = jsonDocument.object();
105 const QList<QVariant> layers = jsonObject.toVariantMap().value( QStringLiteral(
"objects" ) ).toList();
106 if ( layers.count() < 1 )
110 QString defaultStyleUrl = layers.at( 0 ).toMap().value( QStringLiteral(
"default_style" ) ).toString();
112 defaultStyle = retrieveStyle( defaultStyleUrl );
120 QList<QgsGeoNodeStyle> geoNodeStyles;
121 bool success =
requestBlocking( QStringLiteral(
"/api/styles?layer__name=" ) + layerName );
124 return geoNodeStyles;
127 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
128 const QJsonObject jsobObject = jsonDocument.object();
129 const QList<QVariant> styles = jsobObject.toVariantMap().value( QStringLiteral(
"objects" ) ).toList();
131 for (
const QVariant &style : styles )
133 const QVariantMap styleMap = style.toMap();
134 QString styleUrl = styleMap.value( QStringLiteral(
"resource_uri" ) ).toString();
136 if ( !geoNodeStyle.
name.isEmpty() )
138 geoNodeStyles.append( geoNodeStyle );
142 return geoNodeStyles;
148 QString endPoint = QStringLiteral(
"/api/styles/" ) + styleId;
150 return retrieveStyle( endPoint );
153void QgsGeoNodeRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
155 QString msg = tr(
"%1 of %2 bytes of request downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) );
170void QgsGeoNodeRequest::replyFinished()
173 if ( !mIsAborted && mGeoNodeReply )
175 if ( mGeoNodeReply->error() == QNetworkReply::NoError )
178 QVariant redirect = mGeoNodeReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
182 emit
statusChanged( QStringLiteral(
"GeoNode request redirected." ) );
184 const QUrl &toUrl = redirect.toUrl();
185 if ( toUrl == mGeoNodeReply->url() )
187 mError = tr(
"Redirect loop detected: %1" ).arg( toUrl.toString() );
189 mHttpGeoNodeResponse.clear();
193 QNetworkRequest
request( toUrl );
195 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
196 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
198 mGeoNodeReply->deleteLater();
199 mGeoNodeReply =
nullptr;
201 QgsDebugMsgLevel( QStringLiteral(
"redirected getcapabilities: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 3 );
204 connect( mGeoNodeReply, &QNetworkReply::finished,
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
205 connect( mGeoNodeReply, &QNetworkReply::downloadProgress,
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
215 QNetworkCacheMetaData cmd = nam->cache()->metaData( mGeoNodeReply->request().url() );
217 QNetworkCacheMetaData::RawHeaderList hl;
218 const QNetworkCacheMetaData::RawHeaderList cmdHeaders = cmd.rawHeaders();
219 for (
const QNetworkCacheMetaData::RawHeader &h : cmdHeaders )
221 if ( h.first != QStringLiteral(
"Cache-Control" ) )
224 cmd.setRawHeaders( hl );
226 QgsDebugMsgLevel( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
227 if ( cmd.expirationDate().isNull() )
230 cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( settings.
value( QStringLiteral(
"qgis/defaultCapabilitiesExpiry" ),
"24",
QgsSettings::Providers ).toInt() * 60 * 60 ) );
233 nam->cache()->updateMetaData( cmd );
237 QgsDebugMsg( QStringLiteral(
"No cache for capabilities!" ) );
240 mHttpGeoNodeResponse = mGeoNodeReply->readAll();
242 if ( mHttpGeoNodeResponse.isEmpty() )
244 mError = tr(
"Empty capabilities: %1" ).arg( mGeoNodeReply->errorString() );
250 mError = tr(
"Request failed: %1" ).arg( mGeoNodeReply->errorString() );
252 mHttpGeoNodeResponse.clear();
258 mGeoNodeReply->deleteLater();
259 mGeoNodeReply =
nullptr;
265QList<QgsGeoNodeRequest::ServiceLayerDetail> QgsGeoNodeRequest::parseLayers(
const QByteArray &layerResponse )
267 QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
268 if ( layerResponse.isEmpty() )
273 const QJsonDocument jsonDocument = QJsonDocument::fromJson( layerResponse );
274 const QJsonObject jsonObject = jsonDocument.object();
275 const QVariantMap jsonVariantMap = jsonObject.toVariantMap();
276 const QVariantList layerList = jsonVariantMap.value( QStringLiteral(
"objects" ) ).toList();
278 QString wmsURLFormat, wfsURLFormat, wcsURLFormat, xyzURLFormat;
280 for (
const QVariant &layer : std::as_const( layerList ) )
283 const QVariantMap layerMap = layer.toMap();
284 QVariantList layerLinks = layerMap.value( QStringLiteral(
"links" ) ).toList();
286 if ( layerMap.value( QStringLiteral(
"typename" ) ).toString().isEmpty() )
288 const QStringList splitUrl = layerMap.value( QStringLiteral(
"detail_url" ) ).toString().split(
'/' );
289 layerStruct.
typeName = !splitUrl.isEmpty() ? splitUrl.last() : QString();
291 layerStruct.
uuid = QUuid( layerMap.value( QStringLiteral(
"uuid" ) ).toString() );
292 layerStruct.
id = layerMap.value( QStringLiteral(
"id" ) ).toString();
293 layerStruct.
name = layerMap.value( QStringLiteral(
"name" ) ).toString();
294 layerStruct.
typeName = layerMap.value( QStringLiteral(
"typename" ) ).toString();
295 layerStruct.
title = layerMap.value( QStringLiteral(
"title" ) ).toString();
297 if ( ! layerMap.contains( QStringLiteral(
"links" ) ) )
299 if ( wmsURLFormat.isEmpty() && wfsURLFormat.isEmpty() && wcsURLFormat.isEmpty() && xyzURLFormat.isEmpty() )
301 bool success =
requestBlocking( QStringLiteral(
"/api/layers/%1/" ).arg( layerStruct.
id ) );
304 const QJsonDocument resourceUriDocument = QJsonDocument::fromJson( this->
lastResponse() );
305 const QJsonObject resourceUriObject = resourceUriDocument.object();
306 const QVariantMap resourceUriMap = resourceUriObject.toVariantMap();
307 QVariantList resourceUriLinks = resourceUriMap.value( QStringLiteral(
"links" ) ).toList();
309 tempLayerStruct =
parseOwsUrl( tempLayerStruct, resourceUriLinks );
311 if ( tempLayerStruct.
wmsURL.isEmpty() && tempLayerStruct.
wfsURL.isEmpty() && tempLayerStruct.
wcsURL.isEmpty() && tempLayerStruct.
xyzURL.isEmpty() )
316 switch ( tempLayerStruct.
server )
320 wmsURLFormat = ! tempLayerStruct.
wmsURL.isEmpty() ? tempLayerStruct.
wmsURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
321 wfsURLFormat = ! tempLayerStruct.
wfsURL.isEmpty() ? tempLayerStruct.
wfsURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
322 wcsURLFormat = ! tempLayerStruct.
wcsURL.isEmpty() ? tempLayerStruct.
wcsURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
323 xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
328 wmsURLFormat = ! tempLayerStruct.
wmsURL.isEmpty() ? tempLayerStruct.
wmsURL : QString();
329 wfsURLFormat = ! tempLayerStruct.
wfsURL.isEmpty() ? tempLayerStruct.
wfsURL : QString();
330 wcsURLFormat = ! tempLayerStruct.
wcsURL.isEmpty() ? tempLayerStruct.
wcsURL : QString();
331 xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral(
"%1" ) ) : QString();
344 layerStruct.
wmsURL = wmsURLFormat.contains(
"%1" ) ? wmsURLFormat.arg( layerStruct.
name ) : wmsURLFormat;
345 layerStruct.
wfsURL = wfsURLFormat.contains(
"%1" ) ? wfsURLFormat.arg( layerStruct.
name ) : wfsURLFormat;
346 layerStruct.
wcsURL = wcsURLFormat.contains(
"%1" ) ? wcsURLFormat.arg( layerStruct.
name ) : wcsURLFormat;
347 layerStruct.
xyzURL = xyzURLFormat.contains(
"%1" ) ? xyzURLFormat.arg( layerStruct.
name ) : xyzURLFormat;
351 layerStruct =
parseOwsUrl( layerStruct, layerLinks );
353 layers.append( layerStruct );
362 for (
const QVariant &link : layerLinks )
364 const QVariantMap linkMap = link.toMap();
365 if ( linkMap.contains( QStringLiteral(
"link_type" ) ) )
367 if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WMS" ) )
369 urlFound = layerStruct.
wmsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
371 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WFS" ) )
373 urlFound = layerStruct.
wfsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
375 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"OGC:WCS" ) )
377 urlFound = layerStruct.
wcsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
379 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QLatin1String(
"image" ) )
381 if ( linkMap.contains( QStringLiteral(
"name" ) ) && linkMap.value( QStringLiteral(
"name" ) ) == QLatin1String(
"Tiles" ) )
383 urlFound = layerStruct.
xyzURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
388 switch ( layerStruct.
server )
405QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle(
const QString &styleUrl )
414 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
415 const QJsonObject jsonObject = jsonDocument.object();
417 const QVariantMap jsonMap = jsonObject.toVariantMap();
418 geoNodeStyle.
id = jsonMap.value( QStringLiteral(
"id" ) ).toString();
419 geoNodeStyle.
name = jsonMap.value( QStringLiteral(
"name" ) ).toString();
420 geoNodeStyle.
title = jsonMap.value( QStringLiteral(
"title" ) ).toString();
421 geoNodeStyle.
styleUrl = jsonMap.value( QStringLiteral(
"style_url" ) ).toString();
444 if ( layers.empty() )
452 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
456 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
460 else if ( QString::compare( serviceType, QStringLiteral(
"wcs" ), Qt::CaseInsensitive ) == 0 )
464 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
472 if ( !url.contains( QLatin1String(
"://" ) ) )
476 if ( !urls.contains( url ) )
491 if ( layers.empty() )
500 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
504 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
508 else if ( QString::compare( serviceType, QStringLiteral(
"wcs" ), Qt::CaseInsensitive ) == 0 )
512 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
520 QString layerName = layer.name;
521 if ( !url.contains( QLatin1String(
"://" ) ) )
525 if ( !urls.contains( url ) )
527 urls.insert( layerName, url );
539 QString url = endPoint.startsWith( mBaseUrl ) ? endPoint : mBaseUrl + endPoint;
541 setProtocol( url.split( QStringLiteral(
"://" ) ).at( 0 ) );
542 QUrl layerUrl( url );
547 mGeoNodeReply = requestUrl( url );
548 connect( mGeoNodeReply, &QNetworkReply::finished,
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
549 connect( mGeoNodeReply, &QNetworkReply::downloadProgress,
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
558 loop.exec( QEventLoop::ExcludeUserInputEvents );
560 return mError.isEmpty();
563QNetworkReply *QgsGeoNodeRequest::requestUrl(
const QString &url )
565 QNetworkRequest
request( url );
566 request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, 1 );
571 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
572 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.
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.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
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.