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/" ) );
 
   58   QObject *obj = 
new QObject( 
this );
 
   61     if ( !mParsingLayers )
 
   63       mParsingLayers = 
true;
 
   64       QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
 
   65       if ( mError.isEmpty() )
 
   67         layers = parseLayers( lastResponse() );
 
   70       mParsingLayers = 
false;
 
   78   QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
 
   81   QObject *obj = 
new QObject( 
this );
 
   88   loop.exec( QEventLoop::ExcludeUserInputEvents );
 
   96   bool success = 
requestBlocking( QStringLiteral( 
"/api/layers?name=" )  + layerName );
 
  102   const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
 
  103   const QJsonObject jsonObject = jsonDocument.object();
 
  104   const QList<QVariant> layers = jsonObject.toVariantMap().value( QStringLiteral( 
"objects" ) ).toList();
 
  105   if ( layers.count() < 1 )
 
  109   QString defaultStyleUrl = layers.at( 0 ).toMap().value( QStringLiteral( 
"default_style" ) ).toString();
 
  111   defaultStyle = retrieveStyle( defaultStyleUrl );
 
  119   QList<QgsGeoNodeStyle> geoNodeStyles;
 
  120   bool success = 
requestBlocking( QStringLiteral( 
"/api/styles?layer__name=" ) + layerName );
 
  123     return geoNodeStyles;
 
  126   const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
 
  127   const QJsonObject jsobObject = jsonDocument.object();
 
  128   const QList<QVariant> styles = jsobObject.toVariantMap().value( QStringLiteral( 
"objects" ) ).toList();
 
  130   for ( 
const QVariant &style : styles )
 
  132     const QVariantMap styleMap = style.toMap();
 
  133     QString styleUrl = styleMap.value( QStringLiteral( 
"resource_uri" ) ).toString();
 
  135     if ( !geoNodeStyle.
name.isEmpty() )
 
  137       geoNodeStyles.append( geoNodeStyle );
 
  141   return geoNodeStyles;
 
  147   QString endPoint = QStringLiteral( 
"/api/styles/" ) + styleId;
 
  149   return retrieveStyle( endPoint );
 
  152 void QgsGeoNodeRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
 
  154   QString msg = tr( 
"%1 of %2 bytes of request downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral( 
"unknown number of" ) : QString::number( bytesTotal ) );
 
  169 void QgsGeoNodeRequest::replyFinished()
 
  172   if ( !mIsAborted && mGeoNodeReply )
 
  174     if ( mGeoNodeReply->error() == QNetworkReply::NoError )
 
  177       QVariant redirect = mGeoNodeReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
 
  178       if ( !redirect.isNull() )
 
  181         emit 
statusChanged( QStringLiteral( 
"GeoNode request redirected." ) );
 
  183         const QUrl &toUrl = redirect.toUrl();
 
  184         if ( toUrl == mGeoNodeReply->url() )
 
  186           mError = tr( 
"Redirect loop detected: %1" ).arg( toUrl.toString() );
 
  188           mHttpGeoNodeResponse.clear();
 
  192           QNetworkRequest 
request( toUrl );
 
  194           request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
 
  195           request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, 
true );
 
  197           mGeoNodeReply->deleteLater();
 
  198           mGeoNodeReply = 
nullptr;
 
  200           QgsDebugMsgLevel( QStringLiteral( 
"redirected getcapabilities: %1 forceRefresh=%2" ).arg( redirect.toString() ).arg( mForceRefresh ), 3 );
 
  203           connect( mGeoNodeReply, &QNetworkReply::finished, 
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
 
  204           connect( mGeoNodeReply, &QNetworkReply::downloadProgress, 
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
 
  214           QNetworkCacheMetaData cmd = nam->cache()->metaData( mGeoNodeReply->request().url() );
 
  216           QNetworkCacheMetaData::RawHeaderList hl;
 
  217           const QNetworkCacheMetaData::RawHeaderList cmdHeaders = cmd.rawHeaders();
 
  218           for ( 
const QNetworkCacheMetaData::RawHeader &h : cmdHeaders )
 
  220             if ( h.first != QStringLiteral( 
"Cache-Control" ) )
 
  223           cmd.setRawHeaders( hl );
 
  225           QgsDebugMsgLevel( QStringLiteral( 
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ), 2 );
 
  226           if ( cmd.expirationDate().isNull() )
 
  229             cmd.setExpirationDate( QDateTime::currentDateTime().addSecs( settings.
value( QStringLiteral( 
"qgis/defaultCapabilitiesExpiry" ), 
"24", 
QgsSettings::Providers ).toInt() * 60 * 60 ) );
 
  232           nam->cache()->updateMetaData( cmd );
 
  236           QgsDebugMsg( QStringLiteral( 
"No cache for capabilities!" ) );
 
  239         mHttpGeoNodeResponse = mGeoNodeReply->readAll();
 
  241         if ( mHttpGeoNodeResponse.isEmpty() )
 
  243           mError = tr( 
"Empty capabilities: %1" ).arg( mGeoNodeReply->errorString() );
 
  249       mError = tr( 
"Request failed: %1" ).arg( mGeoNodeReply->errorString() );
 
  251       mHttpGeoNodeResponse.clear();
 
  257     mGeoNodeReply->deleteLater();
 
  258     mGeoNodeReply = 
nullptr;
 
  264 QList<QgsGeoNodeRequest::ServiceLayerDetail> QgsGeoNodeRequest::parseLayers( 
const QByteArray &layerResponse )
 
  266   QList<QgsGeoNodeRequest::ServiceLayerDetail> layers;
 
  267   if ( layerResponse.isEmpty() )
 
  272   const QJsonDocument jsonDocument = QJsonDocument::fromJson( layerResponse );
 
  273   const QJsonObject jsonObject = jsonDocument.object();
 
  274   const QVariantMap jsonVariantMap = jsonObject.toVariantMap();
 
  275   const QVariantList layerList = jsonVariantMap.value( QStringLiteral( 
"objects" ) ).toList();
 
  277   QString wmsURLFormat, wfsURLFormat, wcsURLFormat, xyzURLFormat;
 
  279   for ( 
const QVariant &layer : std::as_const( layerList ) )
 
  282     const QVariantMap layerMap = layer.toMap();
 
  283     QVariantList layerLinks = layerMap.value( QStringLiteral( 
"links" ) ).toList();
 
  285     if ( layerMap.value( QStringLiteral( 
"typename" ) ).toString().isEmpty() )
 
  287       const QStringList splitUrl = layerMap.value( QStringLiteral( 
"detail_url" ) ).toString().split( 
'/' );
 
  288       layerStruct.
typeName = !splitUrl.isEmpty() ? splitUrl.last() : QString();
 
  290     layerStruct.
uuid = QUuid( layerMap.value( QStringLiteral( 
"uuid" ) ).toString() );
 
  291     layerStruct.
id = layerMap.value( QStringLiteral( 
"id" ) ).toString();
 
  292     layerStruct.
name = layerMap.value( QStringLiteral( 
"name" ) ).toString();
 
  293     layerStruct.
typeName = layerMap.value( QStringLiteral( 
"typename" ) ).toString();
 
  294     layerStruct.
title = layerMap.value( QStringLiteral( 
"title" ) ).toString();
 
  296     if ( ! layerMap.contains( QStringLiteral( 
"links" ) ) )
 
  298       if ( wmsURLFormat.isEmpty() && wfsURLFormat.isEmpty() && wcsURLFormat.isEmpty() && xyzURLFormat.isEmpty() )
 
  300         bool success = 
requestBlocking( QStringLiteral( 
"/api/layers/%1/" ).arg( layerStruct.
id ) );
 
  303           const QJsonDocument resourceUriDocument = QJsonDocument::fromJson( this->
lastResponse() );
 
  304           const QJsonObject resourceUriObject = resourceUriDocument.object();
 
  305           const QVariantMap resourceUriMap = resourceUriObject.toVariantMap();
 
  306           QVariantList resourceUriLinks = resourceUriMap.value( QStringLiteral( 
"links" ) ).toList();
 
  308           tempLayerStruct = 
parseOwsUrl( tempLayerStruct, resourceUriLinks );
 
  310           if ( tempLayerStruct.
wmsURL.isEmpty() && tempLayerStruct.
wfsURL.isEmpty() && tempLayerStruct.
wcsURL.isEmpty() && tempLayerStruct.
xyzURL.isEmpty() )
 
  315           switch ( tempLayerStruct.
server )
 
  319               wmsURLFormat = ! tempLayerStruct.
wmsURL.isEmpty() ? tempLayerStruct.
wmsURL.replace( layerStruct.
name, QStringLiteral( 
"%1" ) ) : QString();
 
  320               wfsURLFormat = ! tempLayerStruct.
wfsURL.isEmpty() ? tempLayerStruct.
wfsURL.replace( layerStruct.
name, QStringLiteral( 
"%1" ) ) : QString();
 
  321               wcsURLFormat = ! tempLayerStruct.
wcsURL.isEmpty() ? tempLayerStruct.
wcsURL.replace( layerStruct.
name, QStringLiteral( 
"%1" ) ) : QString();
 
  322               xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral( 
"%1" ) ) : QString();
 
  327               wmsURLFormat = ! tempLayerStruct.
wmsURL.isEmpty() ? tempLayerStruct.
wmsURL : QString();
 
  328               wfsURLFormat = ! tempLayerStruct.
wfsURL.isEmpty() ? tempLayerStruct.
wfsURL : QString();
 
  329               wcsURLFormat = ! tempLayerStruct.
wcsURL.isEmpty() ? tempLayerStruct.
wcsURL : QString();
 
  330               xyzURLFormat = ! tempLayerStruct.
xyzURL.isEmpty() ? tempLayerStruct.
xyzURL.replace( layerStruct.
name, QStringLiteral( 
"%1" ) ) : QString();
 
  343         layerStruct.
wmsURL = wmsURLFormat.contains( 
"%1" ) ? wmsURLFormat.arg( layerStruct.
name ) : wmsURLFormat;
 
  344         layerStruct.
wfsURL = wfsURLFormat.contains( 
"%1" ) ? wfsURLFormat.arg( layerStruct.
name ) : wfsURLFormat;
 
  345         layerStruct.
wcsURL = wcsURLFormat.contains( 
"%1" ) ? wcsURLFormat.arg( layerStruct.
name ) : wcsURLFormat;
 
  346         layerStruct.
xyzURL = xyzURLFormat.contains( 
"%1" ) ? xyzURLFormat.arg( layerStruct.
name ) : xyzURLFormat;
 
  350       layerStruct = 
parseOwsUrl( layerStruct, layerLinks );
 
  352     layers.append( layerStruct );
 
  361   for ( 
const QVariant &link : layerLinks )
 
  363     const QVariantMap linkMap = link.toMap();
 
  364     if ( linkMap.contains( QStringLiteral( 
"link_type" ) ) )
 
  366       if ( linkMap.value( QStringLiteral( 
"link_type" ) ) == QLatin1String( 
"OGC:WMS" ) )
 
  368         urlFound = layerStruct.
wmsURL = linkMap.value( QStringLiteral( 
"url" ) ).toString();
 
  370       else if ( linkMap.value( QStringLiteral( 
"link_type" ) ) == QLatin1String( 
"OGC:WFS" ) )
 
  372         urlFound = layerStruct.
wfsURL = linkMap.value( QStringLiteral( 
"url" ) ).toString();
 
  374       else if ( linkMap.value( QStringLiteral( 
"link_type" ) ) == QLatin1String( 
"OGC:WCS" ) )
 
  376         urlFound = layerStruct.
wcsURL = linkMap.value( QStringLiteral( 
"url" ) ).toString();
 
  378       else if ( linkMap.value( QStringLiteral( 
"link_type" ) ) == QLatin1String( 
"image" ) )
 
  380         if ( linkMap.contains( QStringLiteral( 
"name" ) ) && linkMap.value( QStringLiteral( 
"name" ) ) == QLatin1String( 
"Tiles" ) )
 
  382           urlFound = layerStruct.
xyzURL = linkMap.value( QStringLiteral( 
"url" ) ).toString();
 
  387     switch ( layerStruct.
server )
 
  404 QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle( 
const QString &styleUrl )
 
  413   const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
 
  414   const QJsonObject jsonObject = jsonDocument.object();
 
  416   const QVariantMap jsonMap = jsonObject.toVariantMap();
 
  417   geoNodeStyle.
id = jsonMap.value( QStringLiteral( 
"id" ) ).toString();
 
  418   geoNodeStyle.
name = jsonMap.value( QStringLiteral( 
"name" ) ).toString();
 
  419   geoNodeStyle.
title = jsonMap.value( QStringLiteral( 
"title" ) ).toString();
 
  420   geoNodeStyle.
styleUrl = jsonMap.value( QStringLiteral( 
"style_url" ) ).toString();
 
  443   if ( layers.empty() )
 
  451     if ( QString::compare( serviceType, QStringLiteral( 
"wms" ), Qt::CaseInsensitive ) == 0 )
 
  455     else if ( QString::compare( serviceType, QStringLiteral( 
"wfs" ), Qt::CaseInsensitive ) == 0 )
 
  459     else if ( QString::compare( serviceType, QStringLiteral( 
"wcs" ), Qt::CaseInsensitive ) == 0 )
 
  463     else if ( QString::compare( serviceType, QStringLiteral( 
"xyz" ), Qt::CaseInsensitive ) == 0 )
 
  471     if ( !url.contains( QLatin1String( 
"://" ) ) )
 
  475     if ( !urls.contains( url ) )
 
  490   if ( layers.empty() )
 
  499     if ( QString::compare( serviceType, QStringLiteral( 
"wms" ), Qt::CaseInsensitive ) == 0 )
 
  503     else if ( QString::compare( serviceType, QStringLiteral( 
"wfs" ), Qt::CaseInsensitive ) == 0 )
 
  507     else if ( QString::compare( serviceType, QStringLiteral( 
"wcs" ), Qt::CaseInsensitive ) == 0 )
 
  511     else if ( QString::compare( serviceType, QStringLiteral( 
"xyz" ), Qt::CaseInsensitive ) == 0 )
 
  519     QString layerName = layer.name;
 
  520     if ( !url.contains( QLatin1String( 
"://" ) ) )
 
  524     if ( !urls.contains( url ) )
 
  526       urls.insert( layerName, url );
 
  538   QString url = endPoint.startsWith( mBaseUrl ) ? endPoint : mBaseUrl + endPoint;
 
  540   setProtocol( url.split( QStringLiteral( 
"://" ) ).at( 0 ) );
 
  541   QUrl layerUrl( url );
 
  546   mGeoNodeReply = requestUrl( url );
 
  547   connect( mGeoNodeReply, &QNetworkReply::finished, 
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
 
  548   connect( mGeoNodeReply, &QNetworkReply::downloadProgress, 
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
 
  557   loop.exec( QEventLoop::ExcludeUserInputEvents );
 
  559   return mError.isEmpty();
 
  562 QNetworkReply *QgsGeoNodeRequest::requestUrl( 
const QString &url )
 
  564   QNetworkRequest 
request( url );
 
  565   request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, 1 );
 
  570   request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
 
  571   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.
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.