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() )
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 QgsDebugMsg( QStringLiteral(
"expirationDate:%1" ).arg( cmd.expirationDate().toString() ) );
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();
274 if ( jsonVariantMap.contains( QStringLiteral(
"geonode_version" ) ) )
276 QRegularExpression re(
"((\\d+)(\\.\\d+))" );
277 QRegularExpressionMatch match = re.match( jsonVariantMap.value( QStringLiteral(
"geonode_version" ) ).toString() );
278 if ( match.hasMatch() )
280 const QStringList geonodeVersionSplit = match.captured( 0 ).split(
'.' );
281 majorVersion = geonodeVersionSplit.at( 0 ).toInt();
282 minorVersion = geonodeVersionSplit.at( 1 ).toInt();
295 if ( majorVersion == 2 && minorVersion == 6 )
297 for (
const QVariant &layer : qgis::as_const( layerList ) )
300 const QVariantMap layerMap = layer.toMap();
303 QString
layerTypeName = layerMap.value( QStringLiteral(
"detail_url" ) ).toString().split(
'/' ).last();
304 if ( layerTypeName.isEmpty() )
306 layerTypeName = layerMap.value( QStringLiteral(
"distribution_url" ) ).toString().split(
'/' ).last();
309 if ( layerTypeName.contains( QStringLiteral(
"%3A" ) ) )
311 layerTypeName.replace( QStringLiteral(
"%3A" ), QStringLiteral(
":" ) );
314 const QStringList splitURL = layerTypeName.split(
':' );
315 QString layerWorkspace = splitURL.at( 0 );
316 QString layerName = splitURL.at( 1 );
318 layerStruct.
name = layerName;
320 layerStruct.
uuid = layerMap.value( QStringLiteral(
"uuid" ) ).toString();
321 layerStruct.
title = layerMap.value( QStringLiteral(
"title" ) ).toString();
324 layerStruct.
wmsURL = mBaseUrl + QStringLiteral(
"/geoserver/" ) + layerWorkspace + QStringLiteral(
"/wms" );
326 layerStruct.
wfsURL = mBaseUrl + QStringLiteral(
"/geoserver/" ) + layerWorkspace + QStringLiteral(
"/wfs" );
328 layerStruct.
xyzURL.clear();
330 layers.append( layerStruct );
334 else if ( ( majorVersion == 2 && minorVersion >= 7 ) || ( majorVersion >= 3 ) )
336 for (
const QVariant &layer : qgis::as_const( layerList ) )
339 const QVariantMap layerMap = layer.toMap();
341 const QVariantList layerLinks = layerMap.value( QStringLiteral(
"links" ) ).toList();
342 for (
const QVariant &link : layerLinks )
344 const QVariantMap linkMap = link.toMap();
345 if ( linkMap.contains( QStringLiteral(
"link_type" ) ) )
347 if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QStringLiteral(
"OGC:WMS" ) )
349 layerStruct.
wmsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
351 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QStringLiteral(
"OGC:WFS" ) )
353 layerStruct.
wfsURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
355 else if ( linkMap.value( QStringLiteral(
"link_type" ) ) == QStringLiteral(
"image" ) )
357 if ( linkMap.contains( QStringLiteral(
"name" ) ) && linkMap.value( QStringLiteral(
"name" ) ) == QStringLiteral(
"Tiles" ) )
359 layerStruct.
xyzURL = linkMap.value( QStringLiteral(
"url" ) ).toString();
364 if ( layerMap.value( QStringLiteral(
"typename" ) ).toString().isEmpty() )
366 const QStringList splitURL = layerMap.value( QStringLiteral(
"detail_url" ) ).toString().split(
'/' );
367 layerStruct.
typeName = splitURL.at( splitURL.length() - 1 );
369 layerStruct.
uuid = layerMap.value( QStringLiteral(
"uuid" ) ).toString();
370 layerStruct.
name = layerMap.value( QStringLiteral(
"name" ) ).toString();
371 layerStruct.
typeName = layerMap.value( QStringLiteral(
"typename" ) ).toString();
372 layerStruct.
title = layerMap.value( QStringLiteral(
"title" ) ).toString();
373 layers.append( layerStruct );
379 QgsGeoNodeStyle QgsGeoNodeRequest::retrieveStyle(
const QString &styleUrl )
388 const QJsonDocument jsonDocument = QJsonDocument::fromJson( this->
lastResponse() );
389 const QJsonObject jsonObject = jsonDocument.object();
391 const QVariantMap jsonMap = jsonObject.toVariantMap();
392 geoNodeStyle.
id = jsonMap.value( QStringLiteral(
"id" ) ).toString();
393 geoNodeStyle.
name = jsonMap.value( QStringLiteral(
"name" ) ).toString();
394 geoNodeStyle.
title = jsonMap.value( QStringLiteral(
"title" ) ).toString();
395 geoNodeStyle.
styleUrl = jsonMap.value( QStringLiteral(
"style_url" ) ).toString();
418 if ( layers.empty() )
426 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
430 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
434 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
442 if ( !url.contains( QLatin1String(
"://" ) ) )
446 if ( !urls.contains( url ) )
461 if ( layers.empty() )
470 if ( QString::compare( serviceType, QStringLiteral(
"wms" ), Qt::CaseInsensitive ) == 0 )
474 else if ( QString::compare( serviceType, QStringLiteral(
"wfs" ), Qt::CaseInsensitive ) == 0 )
478 else if ( QString::compare( serviceType, QStringLiteral(
"xyz" ), Qt::CaseInsensitive ) == 0 )
486 QString layerName = layer.name;
487 if ( !url.contains( QLatin1String(
"://" ) ) )
491 if ( !urls.contains( url ) )
493 urls.insert( layerName, url );
505 QString url = endPoint.startsWith( mBaseUrl ) ? endPoint : mBaseUrl + endPoint;
507 setProtocol( url.split( QStringLiteral(
"://" ) ).at( 0 ) );
508 QUrl layerUrl( url );
513 mGeoNodeReply = requestUrl( url );
514 connect( mGeoNodeReply, &QNetworkReply::finished,
this, &QgsGeoNodeRequest::replyFinished, Qt::DirectConnection );
515 connect( mGeoNodeReply, &QNetworkReply::downloadProgress,
this, &QgsGeoNodeRequest::replyProgress, Qt::DirectConnection );
524 loop.exec( QEventLoop::ExcludeUserInputEvents );
526 return mError.isEmpty();
529 QNetworkReply *QgsGeoNodeRequest::requestUrl(
const QString &url )
531 QNetworkRequest
request( url );
535 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, mForceRefresh ? QNetworkRequest::AlwaysNetwork : QNetworkRequest::PreferCache );
536 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
void fetchLayers()
Triggers a new request to fetch the list of available layers from the server.
#define QgsSetRequestInitiatorClass(request, _class)
void layersFetched(const QList< QgsGeoNodeRequest::ServiceLayerDetail > &layers)
Emitted when the result of a fetchLayers call has been received and processed.
Encapsulates information about a GeoNode layer style.
void setProtocol(const QString &protocol)
Sets the network protocol (e.g.
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.
~QgsGeoNodeRequest() override
QString styleUrl
Associated URL.
Service layer details for an individual layer from a GeoNode connection.
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.
QString wmsURL
WMS URL for layer.
QgsStringMap fetchServiceUrlDataBlocking(const QString &serviceType)
Obtains a map of layer name to URL for available services with matching serviceType from the server...
QList< QgsGeoNodeStyle > fetchStylesBlocking(const QString &layerName)
Requests the list of available styles for the layer with matching layerName from the server...
QMap< QString, QString > QgsStringMap
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
QString typeName
Layer type name.
QDomDocument body
DOM documenting containing style.
void statusChanged(const QString &statusQString)
Emitted when the status of an ongoing request is changed.
QgsGeoNodeStyle fetchDefaultStyleBlocking(const QString &layerName)
Requests the default style for the layer with matching layerName from the server. ...
#define QgsDebugMsgLevel(str, level)
QString title
Layer title.
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).
QString xyzURL
XYZ tileserver URL for layer.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
QList< QgsGeoNodeRequest::ServiceLayerDetail > fetchLayersBlocking()
Requests the list of available layers from the server.
QString wfsURL
WFS URL for layer.
QgsGeoNodeRequest(const QString &baseUrl, bool forceRefresh, QObject *parent=nullptr)
Constructor for QgsGeoNodeRequest.
void requestFinished()
Emitted when the existing request has been completed.
QString id
Unique style ID.
QString protocol() const
Returns the network protocol (e.g.
bool requestBlocking(const QString &endPoint)
Triggers a new request to the GeoNode server, with the requested endPoint.
QUuid uuid
Unique identifier (generate on the client side, not at the GeoNode server)
void abort()
Aborts any active network request immediately.
network access manager for QGISThis class implements the QGIS network access manager.
QgsGeoNodeStyle fetchStyleBlocking(const QString &styleId)
Requests the details for the style with matching styleId from the server.
QStringList fetchServiceUrlsBlocking(const QString &serviceType)
Requests the list of unique URLs for available services with matching serviceType from the server...
QString title
Style title.