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.