Skip to content

Commit

Permalink
[afs] Fix missing layers/services when connection url does
Browse files Browse the repository at this point in the history
not point at an individual service endpoint

Previously the provider would only show layers if a FeatureService
endpoint was used for the url, and would show nothing if
users enter the base server url.

Fix this by implementing a tree view for AFS items so that
if users enter a connection url at a higher level than we
show nested services/folders containing the layers.

(cherry picked from commit b2d2634)
  • Loading branch information
nyalldawson committed Nov 10, 2018
1 parent 3730579 commit 2ac6013
Show file tree
Hide file tree
Showing 9 changed files with 389 additions and 83 deletions.
2 changes: 1 addition & 1 deletion src/core/qgsstringutils.cpp
Expand Up @@ -431,7 +431,7 @@ QString QgsStringUtils::insertLinks( const QString &string, bool *foundLinks )
{
found = true;
QString email = emailRegEx.cap( 1 );
QString anchor = QStringLiteral( "<a href=\"mailto:%1\">%1</a>" ).arg( email.toHtmlEscaped(), email.toHtmlEscaped() );
QString anchor = QStringLiteral( "<a href=\"mailto:%1\">%1</a>" ).arg( email.toHtmlEscaped() );
converted.replace( emailRegEx.pos( 1 ), email.length(), anchor );
offset = emailRegEx.pos( 1 ) + anchor.length();
}
Expand Down
190 changes: 168 additions & 22 deletions src/providers/arcgisrest/qgsafsdataitems.cpp
Expand Up @@ -84,6 +84,65 @@ void QgsAfsRootItem::newConnection()

///////////////////////////////////////////////////////////////////////////////

void addFolderItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &baseUrl, const QString &authcfg, QgsDataItem *parent )
{
QgsArcGisRestUtils::visitFolderItems( [parent, &baseUrl, &items, authcfg]( const QString & name, const QString & url )
{
std::unique_ptr< QgsAfsFolderItem > folderItem = qgis::make_unique< QgsAfsFolderItem >( parent, name, url, baseUrl, authcfg );
items.append( folderItem.release() );
}, serviceData, baseUrl );
}

void addServiceItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &baseUrl, const QString &authcfg, QgsDataItem *parent,
const QString &parentName )
{
QgsArcGisRestUtils::visitServiceItems(
[&items, parent, authcfg]( const QString & name, const QString & url )
{
std::unique_ptr< QgsAfsServiceItem > serviceItem = qgis::make_unique< QgsAfsServiceItem >( parent, name, url, url, authcfg );
items.append( serviceItem.release() );
}, serviceData, baseUrl, parentName );
}

void addLayerItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &parentUrl, const QString &authcfg, QgsDataItem *parent )
{
QMap< QString, QgsDataItem * > layerItems;
QMap< QString, QString > parents;

QgsArcGisRestUtils::addLayerItems( [parent, &layerItems, &parents, authcfg]( const QString & parentLayerId, const QString & id, const QString & name, const QString & description, const QString & url, bool isParent, const QString & authid )
{
Q_UNUSED( description );

if ( !parentLayerId.isEmpty() )
parents.insert( id, parentLayerId );

if ( isParent )
{
std::unique_ptr< QgsAfsParentLayerItem > layerItem = qgis::make_unique< QgsAfsParentLayerItem >( parent, name, url, authcfg );
layerItems.insert( id, layerItem.release() );
}
else
{
std::unique_ptr< QgsAfsLayerItem > layerItem = qgis::make_unique< QgsAfsLayerItem >( parent, name, url, name, authid, authcfg );
layerItems.insert( id, layerItem.release() );
}

}, serviceData, parentUrl );

// create groups
for ( auto it = layerItems.constBegin(); it != layerItems.constEnd(); ++it )
{
const QString id = it.key();
QgsDataItem *item = it.value();
const QString parentId = parents.value( id );

if ( QgsDataItem *layerParent = parentId.isEmpty() ? nullptr : layerItems.value( parentId ) )
layerParent->addChildItem( item );
else
items.append( item );
}
}

QgsAfsConnectionItem::QgsAfsConnectionItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &connectionName )
: QgsDataCollectionItem( parent, name, path )
, mConnName( connectionName )
Expand All @@ -98,7 +157,7 @@ QVector<QgsDataItem *> QgsAfsConnectionItem::createChildren()
const QString url = connection.uri().param( QStringLiteral( "url" ) );
const QString authcfg = connection.uri().param( QStringLiteral( "authcfg" ) );

QVector<QgsDataItem *> layers;
QVector<QgsDataItem *> items;
QString errorTitle, errorMessage;
const QVariantMap serviceData = QgsArcGisRestUtils::getServiceInfo( url, authcfg, errorTitle, errorMessage );
if ( serviceData.isEmpty() )
Expand All @@ -107,29 +166,17 @@ QVector<QgsDataItem *> QgsAfsConnectionItem::createChildren()
{
std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
error->setToolTip( errorMessage );
layers.append( error.release() );
items.append( error.release() );
QgsDebugMsg( "Connection failed - " + errorMessage );
}
return layers;
return items;
}
const QString authid = QgsArcGisRestUtils::parseSpatialReference( serviceData.value( QStringLiteral( "spatialReference" ) ).toMap() ).authid();

const QVariantList layerInfoList = serviceData[QStringLiteral( "layers" )].toList();
for ( const QVariant &layerInfo : layerInfoList )
{
const QVariantMap layerInfoMap = layerInfo.toMap();
if ( !layerInfoMap.value( QStringLiteral( "subLayerIds" ) ).toList().empty() )
{
// group layer - do not show as it is not possible to load
// TODO - show nested groups
continue;
}
const QString id = layerInfoMap.value( QStringLiteral( "id" ) ).toString();
QgsAfsLayerItem *layer = new QgsAfsLayerItem( this, mName, url + '/' + id, layerInfoMap.value( QStringLiteral( "name" ) ).toString(), authid, authcfg );
layers.append( layer );
}
addFolderItems( items, serviceData, url, authcfg, this );
addServiceItems( items, serviceData, url, authcfg, this, QString() );
addLayerItems( items, serviceData, url, authcfg, this );

return layers;
return items;
}

bool QgsAfsConnectionItem::equal( const QgsDataItem *other )
Expand Down Expand Up @@ -199,18 +246,117 @@ void QgsAfsConnectionItem::refreshConnection()
}
#endif

///////////////////////////////////////////////////////////////////////////////

QgsAfsLayerItem::QgsAfsLayerItem( QgsDataItem *parent, const QString &name, const QString &url, const QString &title, const QString &authid, const QString &authcfg )
: QgsLayerItem( parent, title, parent->path() + "/" + name, QString(), QgsLayerItem::Vector, QStringLiteral( "arcgisfeatureserver" ) )
QgsAfsFolderItem::QgsAfsFolderItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg )
: QgsDataCollectionItem( parent, name, path )
, mBaseUrl( baseUrl )
, mAuthCfg( authcfg )
{
mIconName = QStringLiteral( "mIconDbSchema.svg" );
mCapabilities |= Collapse;
setToolTip( path );
}


QVector<QgsDataItem *> QgsAfsFolderItem::createChildren()
{
const QString url = mPath;

QVector<QgsDataItem *> items;
QString errorTitle, errorMessage;
const QVariantMap serviceData = QgsArcGisRestUtils::getServiceInfo( url, mAuthCfg, errorTitle, errorMessage );
if ( serviceData.isEmpty() )
{
if ( !errorMessage.isEmpty() )
{
std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
error->setToolTip( errorMessage );
items.append( error.release() );
QgsDebugMsg( "Connection failed - " + errorMessage );
}
return items;
}

addFolderItems( items, serviceData, mBaseUrl, mAuthCfg, this );
addServiceItems( items, serviceData, mBaseUrl, mAuthCfg, this, mName );
addLayerItems( items, serviceData, mPath, mAuthCfg, this );
return items;
}

bool QgsAfsFolderItem::equal( const QgsDataItem *other )
{
const QgsAfsFolderItem *o = qobject_cast<const QgsAfsFolderItem *>( other );
return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
}

QgsAfsServiceItem::QgsAfsServiceItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg )
: QgsDataCollectionItem( parent, name, path )
, mBaseUrl( baseUrl )
, mAuthCfg( authcfg )
{
mIconName = QStringLiteral( "mIconDbSchema.svg" );
mCapabilities |= Collapse;
setToolTip( path );
}

QVector<QgsDataItem *> QgsAfsServiceItem::createChildren()
{
const QString url = mPath;

QVector<QgsDataItem *> items;
QString errorTitle, errorMessage;
const QVariantMap serviceData = QgsArcGisRestUtils::getServiceInfo( url, mAuthCfg, errorTitle, errorMessage );
if ( serviceData.isEmpty() )
{
if ( !errorMessage.isEmpty() )
{
std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
error->setToolTip( errorMessage );
items.append( error.release() );
QgsDebugMsg( "Connection failed - " + errorMessage );
}
return items;
}

addFolderItems( items, serviceData, mBaseUrl, mAuthCfg, this );
addServiceItems( items, serviceData, mBaseUrl, mAuthCfg, this, mName );
addLayerItems( items, serviceData, mPath, mAuthCfg, this );
return items;
}

bool QgsAfsServiceItem::equal( const QgsDataItem *other )
{
const QgsAfsServiceItem *o = qobject_cast<const QgsAfsServiceItem *>( other );
return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
}

QgsAfsLayerItem::QgsAfsLayerItem( QgsDataItem *parent, const QString &, const QString &url, const QString &title, const QString &authid, const QString &authcfg )
: QgsLayerItem( parent, title, url, QString(), QgsLayerItem::Vector, QStringLiteral( "arcgisfeatureserver" ) )
{
mUri = QStringLiteral( "crs='%1' url='%2'" ).arg( authid, url );
if ( !authcfg.isEmpty() )
mUri += QStringLiteral( " authcfg='%1'" ).arg( authcfg );
setState( Populated );
mIconName = QStringLiteral( "mIconAfs.svg" );
setToolTip( url );
}

QgsAfsParentLayerItem::QgsAfsParentLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &authcfg )
: QgsDataItem( QgsDataItem::Collection, parent, name, path )
, mAuthCfg( authcfg )
{
mCapabilities |= Fast;
mIconName = QStringLiteral( "mIconDbSchema.svg" );
setToolTip( path );
}

bool QgsAfsParentLayerItem::equal( const QgsDataItem *other )
{
const QgsAfsParentLayerItem *o = qobject_cast<const QgsAfsParentLayerItem *>( other );
return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
}


//
// QgsAfsDataItemProvider
//
Expand Down
43 changes: 43 additions & 0 deletions src/providers/arcgisrest/qgsafsdataitems.h
Expand Up @@ -64,13 +64,56 @@ class QgsAfsConnectionItem : public QgsDataCollectionItem
QString mConnName;
};

class QgsAfsFolderItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsAfsFolderItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg );
QVector<QgsDataItem *> createChildren() override;
bool equal( const QgsDataItem *other ) override;

private:
QString mFolder;
QString mBaseUrl;
QString mAuthCfg;
};

class QgsAfsServiceItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsAfsServiceItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg );
QVector<QgsDataItem *> createChildren() override;
bool equal( const QgsDataItem *other ) override;

private:
QString mFolder;
QString mBaseUrl;
QString mAuthCfg;
};

class QgsAfsParentLayerItem : public QgsDataItem
{
Q_OBJECT
public:

QgsAfsParentLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &authcfg );
bool equal( const QgsDataItem *other ) override;

private:

QString mAuthCfg;

};

class QgsAfsLayerItem : public QgsLayerItem
{
Q_OBJECT

public:

QgsAfsLayerItem( QgsDataItem *parent, const QString &name, const QString &url, const QString &title, const QString &authid, const QString &authcfg );

};

//! Provider for afs root data item
Expand Down

0 comments on commit 2ac6013

Please sign in to comment.