Skip to content

Commit

Permalink
[feature][vectortiles] Add option to create connections for ArcGIS Ve…
Browse files Browse the repository at this point in the history
…ctor

Tile Services to easily load vector tile layers from these servers

Adds new options in the Vector Tiles browser tree context menu
and Data Source Manager to create a connection to an ESRI VectorTileServer.
When loading the layer from the connection, the tile server parameters
will be automatically setup to suit the layer source.
  • Loading branch information
nyalldawson committed Sep 8, 2020
1 parent b0dd568 commit 9114e91
Show file tree
Hide file tree
Showing 14 changed files with 499 additions and 50 deletions.
45 changes: 45 additions & 0 deletions src/core/vectortile/qgsvectortileconnection.cpp
Expand Up @@ -30,6 +30,17 @@ QString QgsVectorTileProviderConnection::encodedUri( const QgsVectorTileProvider
uri.setParam( QStringLiteral( "zmin" ), QString::number( conn.zMin ) );
if ( conn.zMax != -1 )
uri.setParam( QStringLiteral( "zmax" ), QString::number( conn.zMax ) );

switch ( conn.serviceType )
{
case Generic:
break;

case ArcgisVectorTileService:
uri.setParam( QStringLiteral( "serviceType" ), QStringLiteral( "arcgis" ) );
break;
}

return uri.encodedUri();
}

Expand All @@ -42,6 +53,12 @@ QgsVectorTileProviderConnection::Data QgsVectorTileProviderConnection::decodedUr
conn.url = dsUri.param( QStringLiteral( "url" ) );
conn.zMin = dsUri.hasParam( QStringLiteral( "zmin" ) ) ? dsUri.param( QStringLiteral( "zmin" ) ).toInt() : -1;
conn.zMax = dsUri.hasParam( QStringLiteral( "zmax" ) ) ? dsUri.param( QStringLiteral( "zmax" ) ).toInt() : -1;

if ( dsUri.hasParam( QStringLiteral( "serviceType" ) ) )
{
if ( dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
conn.serviceType = ArcgisVectorTileService;
}
return conn;
}

Expand All @@ -55,6 +72,17 @@ QString QgsVectorTileProviderConnection::encodedLayerUri( const QgsVectorTilePro
uri.setParam( QStringLiteral( "zmin" ), QString::number( conn.zMin ) );
if ( conn.zMax != -1 )
uri.setParam( QStringLiteral( "zmax" ), QString::number( conn.zMax ) );

switch ( conn.serviceType )
{
case Generic:
break;

case ArcgisVectorTileService:
uri.setParam( QStringLiteral( "serviceType" ), QStringLiteral( "arcgis" ) );
break;
}

return uri.encodedUri();
}

Expand All @@ -79,6 +107,13 @@ QgsVectorTileProviderConnection::Data QgsVectorTileProviderConnection::connectio
conn.url = settings.value( QStringLiteral( "url" ) ).toString();
conn.zMin = settings.value( QStringLiteral( "zmin" ), -1 ).toInt();
conn.zMax = settings.value( QStringLiteral( "zmax" ), -1 ).toInt();

if ( settings.contains( QStringLiteral( "serviceType" ) ) )
{
if ( settings.value( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
conn.serviceType = ArcgisVectorTileService;
}

return conn;
}

Expand All @@ -96,6 +131,16 @@ void QgsVectorTileProviderConnection::addConnection( const QString &name, QgsVec
settings.setValue( QStringLiteral( "url" ), conn.url );
settings.setValue( QStringLiteral( "zmin" ), conn.zMin );
settings.setValue( QStringLiteral( "zmax" ), conn.zMax );

switch ( conn.serviceType )
{
case Generic:
break;

case ArcgisVectorTileService:
settings.setValue( QStringLiteral( "serviceType" ), QStringLiteral( "arcgis" ) );
break;
}
}

QString QgsVectorTileProviderConnection::selectedConnection()
Expand Down
13 changes: 13 additions & 0 deletions src/core/vectortile/qgsvectortileconnection.h
Expand Up @@ -35,12 +35,25 @@ class CORE_EXPORT QgsVectorTileProviderConnection : public QgsAbstractProviderCo
virtual void store( const QString &name ) const override;
virtual void remove( const QString &name ) const override;

/**
* Vector tile service type.
*
* \since QGIS 3.16
*/
enum ServiceType
{
Generic, //!< Generic (XYZ) connection
ArcgisVectorTileService, //!< ArcGIS VectorTileServer connection
};

//! Represents decoded data of a connection
struct Data
{
QString url;
int zMin = -1;
int zMax = -1;

ServiceType serviceType = Generic;
};

//! Returns connection data encoded as a string
Expand Down
60 changes: 58 additions & 2 deletions src/core/vectortile/qgsvectortilelayer.cpp
Expand Up @@ -23,10 +23,12 @@
#include "qgsvectortilelabeling.h"
#include "qgsvectortileloader.h"
#include "qgsvectortileutils.h"
#include "qgsnetworkaccessmanager.h"

#include "qgsdatasourceuri.h"
#include "qgslayermetadataformatter.h"

#include "qgsblockingnetworkrequest.h"
#include "qgsmapboxglstyleconverter.h"

QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseName )
: QgsMapLayer( QgsMapLayerType::VectorTileLayer, baseName )
Expand All @@ -48,7 +50,12 @@ bool QgsVectorTileLayer::loadDataSource()

mSourceType = dsUri.param( QStringLiteral( "type" ) );
mSourcePath = dsUri.param( QStringLiteral( "url" ) );
if ( mSourceType == QStringLiteral( "xyz" ) )
if ( mSourceType == QStringLiteral( "xyz" ) && dsUri.param( QStringLiteral( "serviceType" ) ) == QLatin1String( "arcgis" ) )
{
if ( !setupArcgisVectorTileServiceConnection( mSourcePath ) )
return false;
}
else if ( mSourceType == QStringLiteral( "xyz" ) )
{
if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
{
Expand Down Expand Up @@ -109,6 +116,55 @@ bool QgsVectorTileLayer::loadDataSource()
return true;
}

bool QgsVectorTileLayer::setupArcgisVectorTileServiceConnection( const QString &uri )
{
QNetworkRequest request = QNetworkRequest( QUrl( uri ) );

QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) );

QgsBlockingNetworkRequest networkRequest;
switch ( networkRequest.get( request ) )
{
case QgsBlockingNetworkRequest::NoError:
break;

case QgsBlockingNetworkRequest::NetworkError:
case QgsBlockingNetworkRequest::TimeoutError:
case QgsBlockingNetworkRequest::ServerExceptionError:
return false;
}

const QgsNetworkReplyContent content = networkRequest.reply();
const QByteArray raw = content.content();

// Parse data
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson( raw, &err );
if ( doc.isNull() )
{
return false;
}
mArcgisLayerConfiguration = doc.object().toVariantMap();
if ( mArcgisLayerConfiguration.contains( QStringLiteral( "error" ) ) )
{
return false;
}

mArcgisLayerConfiguration.insert( QStringLiteral( "serviceUri" ), uri );
mSourcePath = uri + '/' + mArcgisLayerConfiguration.value( QStringLiteral( "tiles" ) ).toList().value( 0 ).toString();
if ( !QgsVectorTileUtils::checkXYZUrlTemplate( mSourcePath ) )
{
QgsDebugMsg( QStringLiteral( "Invalid format of URL for XYZ source: " ) + mSourcePath );
return false;
}

mSourceMinZoom = 0;
mSourceMaxZoom = mArcgisLayerConfiguration.value( QStringLiteral( "maxzoom" ) ).toInt();
setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) );

return true;
}

QgsVectorTileLayer::~QgsVectorTileLayer() = default;


Expand Down
4 changes: 4 additions & 0 deletions src/core/vectortile/qgsvectortilelayer.h
Expand Up @@ -172,6 +172,10 @@ class CORE_EXPORT QgsVectorTileLayer : public QgsMapLayer
std::unique_ptr<QgsVectorTileLabeling> mLabeling;
//! Whether we draw borders of tiles
bool mTileBorderRendering = false;

QVariantMap mArcgisLayerConfiguration;

bool setupArcgisVectorTileServiceConnection( const QString &uri );
};


Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -326,6 +326,7 @@ SET(QGIS_GUI_SRCS
tableeditor/qgstableeditorformattingwidget.cpp
tableeditor/qgstableeditorwidget.cpp

vectortile/qgsarcgisvectortileconnectiondialog.cpp
vectortile/qgsvectortilebasiclabelingwidget.cpp
vectortile/qgsvectortilebasicrendererwidget.cpp
vectortile/qgsvectortileconnectiondialog.cpp
Expand Down Expand Up @@ -1131,6 +1132,7 @@ SET(QGIS_GUI_HDRS
tableeditor/qgstableeditorformattingwidget.h
tableeditor/qgstableeditorwidget.h

vectortile/qgsarcgisvectortileconnectiondialog.h
vectortile/qgsvectortilebasiclabelingwidget.h
vectortile/qgsvectortilebasicrendererwidget.h
vectortile/qgsvectortileconnectiondialog.h
Expand Down
2 changes: 2 additions & 0 deletions src/gui/qgsmanageconnectionsdialog.cpp
Expand Up @@ -771,6 +771,7 @@ QDomDocument QgsManageConnectionsDialog::saveVectorTileConnections( const QStrin
el.setAttribute( QStringLiteral( "url" ), settings.value( path + "/url" ).toString() );
el.setAttribute( QStringLiteral( "zmin" ), settings.value( path + "/zmin", -1 ).toInt() );
el.setAttribute( QStringLiteral( "zmax" ), settings.value( path + "/zmax", -1 ).toInt() );
el.setAttribute( QStringLiteral( "serviceType" ), settings.value( path + "/serviceType", -1 ).toInt() );

root.appendChild( el );
}
Expand Down Expand Up @@ -1616,6 +1617,7 @@ void QgsManageConnectionsDialog::loadVectorTileConnections( const QDomDocument &
settings.setValue( QStringLiteral( "url" ), child.attribute( QStringLiteral( "url" ) ) );
settings.setValue( QStringLiteral( "zmin" ), child.attribute( QStringLiteral( "zmin" ) ) );
settings.setValue( QStringLiteral( "zmax" ), child.attribute( QStringLiteral( "zmax" ) ) );
settings.setValue( QStringLiteral( "serviceType" ), child.attribute( QStringLiteral( "serviceType" ) ) );
settings.endGroup();

child = child.nextSiblingElement();
Expand Down
63 changes: 63 additions & 0 deletions src/gui/vectortile/qgsarcgisvectortileconnectiondialog.cpp
@@ -0,0 +1,63 @@
/***************************************************************************
qgsarcgisvectortileconnectiondialog.cpp
---------------------
begin : September 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsarcgisvectortileconnectiondialog.h"
#include "qgsvectortileconnection.h"
#include "qgsgui.h"

#include <QMessageBox>
#include <QPushButton>

///@cond PRIVATE

QgsArcgisVectorTileConnectionDialog::QgsArcgisVectorTileConnectionDialog( QWidget *parent )
: QDialog( parent )
{
setupUi( this );
QgsGui::enableAutoGeometryRestore( this );

buttonBox->button( QDialogButtonBox::Ok )->setDisabled( true );
connect( mEditName, &QLineEdit::textChanged, this, &QgsArcgisVectorTileConnectionDialog::updateOkButtonState );
connect( mEditUrl, &QLineEdit::textChanged, this, &QgsArcgisVectorTileConnectionDialog::updateOkButtonState );
}

void QgsArcgisVectorTileConnectionDialog::setConnection( const QString &name, const QString &uri )
{
mEditName->setText( name );

QgsVectorTileProviderConnection::Data conn = QgsVectorTileProviderConnection::decodedUri( uri );
mEditUrl->setText( conn.url );
}

QString QgsArcgisVectorTileConnectionDialog::connectionUri() const
{
QgsVectorTileProviderConnection::Data conn;
conn.url = mEditUrl->text();
conn.serviceType = QgsVectorTileProviderConnection::ArcgisVectorTileService;
return QgsVectorTileProviderConnection::encodedUri( conn );
}

QString QgsArcgisVectorTileConnectionDialog::connectionName() const
{
return mEditName->text();
}

void QgsArcgisVectorTileConnectionDialog::updateOkButtonState()
{
bool enabled = !mEditName->text().isEmpty() && !mEditUrl->text().isEmpty();
buttonBox->button( QDialogButtonBox::Ok )->setEnabled( enabled );
}

///@endcond
48 changes: 48 additions & 0 deletions src/gui/vectortile/qgsarcgisvectortileconnectiondialog.h
@@ -0,0 +1,48 @@
/***************************************************************************
qgsarcgisvectortileconnectiondialog.h
---------------------
begin : September 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSARCGISVECTORTILECONNECTIONDIALOG_H
#define QGSARCGISVECTORTILECONNECTIONDIALOG_H

///@cond PRIVATE
#define SIP_NO_FILE

#include <QDialog>

#include "ui_qgsarcgisvectortileconnectiondialog.h"


class QgsArcgisVectorTileConnectionDialog : public QDialog, public Ui::QgsArcgisVectorTileConnectionDialog
{
Q_OBJECT
public:
explicit QgsArcgisVectorTileConnectionDialog( QWidget *parent = nullptr );

void setConnection( const QString &name, const QString &uri );

QString connectionUri() const;
QString connectionName() const;

private slots:
void updateOkButtonState();

private:
QString mBaseKey;
QString mCredentialsBaseKey;
};

///@endcond

#endif // QGSVECTORTILECONNECTIONDIALOG_H

0 comments on commit 9114e91

Please sign in to comment.