Skip to content

Commit

Permalink
Merge pull request #51144 from rouault/wfs_sublayers
Browse files Browse the repository at this point in the history
[WFS] Improve support of layers with unknown geometry type with Deegree servers
  • Loading branch information
rouault committed Jan 9, 2023
2 parents 3735fe8 + ada9621 commit bac117e
Show file tree
Hide file tree
Showing 32 changed files with 1,837 additions and 226 deletions.
17 changes: 17 additions & 0 deletions python/core/auto_generated/geometry/qgsgeometry.sip.in
Expand Up @@ -2307,6 +2307,23 @@ If it is already a multipart geometry, it will return ``True`` and
not change the geometry.

:return: ``True`` in case of success and ``False`` else
%End

bool convertToCurvedMultiType();
%Docstring
Converts a geometry into a multitype geometry of curve kind (when there
is a corresponding curve type).
e.g. a polygon into a multisurface geometry with one polygon,
a multipolygon into a multisurface, a linestring into a multicurve
geometry with one linestring, or a multilinestring into a multicurve.
If it is already a multipart curve geometry, it will return ``True`` and
not change the geometry. It will also return ``True`` and do nothing if
the current geometry is a multipoint or a geometry collection. A single
point will be transformed to a multipoint.

:return: ``True`` in case of success and ``False`` else

.. versionadded:: 3.30
%End

bool convertToSingleType();
Expand Down
10 changes: 10 additions & 0 deletions python/core/auto_generated/providers/qgsprovidermetadata.sip.in
Expand Up @@ -394,6 +394,16 @@ The optional ``feedback`` argument can be used to provide cancellation support f
different providers).

.. versionadded:: 3.22
%End

virtual QString suggestGroupNameForUri( const QString &uri ) const;
%Docstring
Returns a name that can be used as a group name for sublayers retrieved from
the specified ``uri``.

The default implementation returns an empty string.

.. versionadded:: 3.30
%End

virtual QgsDataProvider *createProvider( const QString &uri,
Expand Down
Expand Up @@ -39,6 +39,14 @@ possible, regardless of how expensive this may be.
%Docstring
Constructor for QgsProviderSublayerTask, which retrieves sublayer details for the
specified ``uri``.
%End

QgsProviderSublayerTask( const QString &uri, const QString &providerKey, bool includeSystemTables = false );
%Docstring
Constructor for QgsProviderSublayerTask, which retrieves sublayer details for the
specified ``uri``, restricted to a particular provider.

.. versionadded:: 3.30
%End

~QgsProviderSublayerTask();
Expand Down
6 changes: 6 additions & 0 deletions python/gui/auto_generated/qgsprovidersublayersdialog.sip.in
Expand Up @@ -56,6 +56,7 @@ Dialog for selecting provider sublayers.
public:

QgsProviderSublayersDialog( const QString &uri,
const QString &providerKey,
const QString &filePath,
const QList< QgsProviderSublayerDetails> initialDetails = QList< QgsProviderSublayerDetails>(),
const QList< QgsMapLayerType > &acceptableTypes = QList< QgsMapLayerType >(),
Expand Down Expand Up @@ -85,6 +86,11 @@ Returns the list of selected non-layer items (e.g. embedded QGIS project items).
QString groupName() const;
%Docstring
Returns an appropriate name for the layer group.
%End

void setGroupName( const QString &groupNameIn );
%Docstring
Sets an appropriate name for the layer group.
%End

signals:
Expand Down
25 changes: 16 additions & 9 deletions src/app/layers/qgsapplayerhandling.cpp
Expand Up @@ -324,7 +324,7 @@ QList< QgsMapLayer * > QgsAppLayerHandling::addOgrVectorLayers( const QStringLis
case SublayerHandling::AskUser:
{
// prompt user for sublayers
QgsProviderSublayersDialog dlg( uri, path, sublayers, {QgsMapLayerType::VectorLayer}, QgisApp::instance() );
QgsProviderSublayersDialog dlg( uri, QString(), path, sublayers, {QgsMapLayerType::VectorLayer}, QgisApp::instance() );

if ( dlg.exec() )
sublayers = dlg.selectedLayers();
Expand Down Expand Up @@ -534,7 +534,7 @@ bool QgsAppLayerHandling::askUserForZipItemLayers( const QString &path, const QL
case SublayerHandling::AskUser:
{
// prompt user for sublayers
QgsProviderSublayersDialog dlg( path, path, sublayers, acceptableTypes, QgisApp::instance() );
QgsProviderSublayersDialog dlg( path, QString(), path, sublayers, acceptableTypes, QgisApp::instance() );

if ( dlg.exec() )
sublayers = dlg.selectedLayers();
Expand Down Expand Up @@ -728,8 +728,11 @@ QList<QgsMapLayer *> QgsAppLayerHandling::addSublayers( const QList<QgsProviderS
}
else
{
if ( layerName != baseName && !layerName.isEmpty() && !baseName.isEmpty() )
if ( layerName != baseName && !layerName.isEmpty() && !baseName.isEmpty() &&
!layerName.startsWith( baseName ) )
{
layer->setName( QStringLiteral( "%1 — %2" ).arg( baseName, layerName ) );
}
else if ( !layerName.isEmpty() )
layer->setName( layerName );
else if ( !baseName.isEmpty() )
Expand Down Expand Up @@ -936,7 +939,7 @@ QList< QgsMapLayer * > QgsAppLayerHandling::openLayer( const QString &fileName,
case SublayerHandling::AskUser:
{
// prompt user for sublayers
QgsProviderSublayersDialog dlg( fileName, fileName, sublayers, {}, QgisApp::instance() );
QgsProviderSublayersDialog dlg( fileName, QString(), fileName, sublayers, {}, QgisApp::instance() );
dlg.setNonLayerItems( nonLayerItems );

if ( dlg.exec() )
Expand Down Expand Up @@ -1337,15 +1340,16 @@ T *QgsAppLayerHandling::addLayerPrivate( QgsMapLayerType type, const QString &ur
// Not all providers implement decodeUri(), so use original uri if uriElements is empty
const QString updatedUri = uriElements.isEmpty() ? uri : QgsProviderRegistry::instance()->encodeUri( providerKey, uriElements );

const bool canQuerySublayers = QgsProviderRegistry::instance()->providerMetadata( providerKey ) &&
( QgsProviderRegistry::instance()->providerMetadata( providerKey )->capabilities() & QgsProviderMetadata::QuerySublayers );
QgsProviderMetadata *providerMetadata = QgsProviderRegistry::instance()->providerMetadata( providerKey );
const bool canQuerySublayers = providerMetadata &&
( providerMetadata->capabilities() & QgsProviderMetadata::QuerySublayers );

T *result = nullptr;
if ( canQuerySublayers )
{
// query sublayers
QList< QgsProviderSublayerDetails > sublayers = QgsProviderRegistry::instance()->providerMetadata( providerKey ) ?
QgsProviderRegistry::instance()->providerMetadata( providerKey )->querySublayers( updatedUri, Qgis::SublayerQueryFlag::IncludeSystemTables )
QList< QgsProviderSublayerDetails > sublayers = providerMetadata ?
providerMetadata->querySublayers( updatedUri, Qgis::SublayerQueryFlag::IncludeSystemTables )
: QgsProviderRegistry::instance()->querySublayers( updatedUri );

// filter out non-matching sublayers
Expand All @@ -1372,7 +1376,10 @@ T *QgsAppLayerHandling::addLayerPrivate( QgsMapLayerType type, const QString &ur
{
case SublayerHandling::AskUser:
{
QgsProviderSublayersDialog dlg( updatedUri, path, sublayers, {type}, QgisApp::instance() );
QgsProviderSublayersDialog dlg( updatedUri, providerKey, path, sublayers, {type}, QgisApp::instance() );
QString groupName = providerMetadata->suggestGroupNameForUri( uri );
if ( !groupName.isEmpty() )
dlg.setGroupName( groupName );
if ( dlg.exec() )
{
const QList< QgsProviderSublayerDetails > selectedLayers = dlg.selectedLayers();
Expand Down
52 changes: 52 additions & 0 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -1523,6 +1523,12 @@ QVector<QgsGeometry> QgsGeometry::coerceToType( const QgsWkbTypes::Type type, do
newGeom.get()->addMValue( defaultM );
}

// Straight -> curve
if ( QgsWkbTypes::isCurvedType( type ) && !QgsWkbTypes::isCurvedType( newGeom.wkbType() ) )
{
newGeom.convertToCurvedMultiType();
}

// Multi -> single
if ( ! QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart( ) )
{
Expand Down Expand Up @@ -1590,6 +1596,52 @@ bool QgsGeometry::convertToMultiType()
return true;
}

bool QgsGeometry::convertToCurvedMultiType()
{
if ( !d->geometry )
{
return false;
}

switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
{
case QgsWkbTypes::MultiPoint:
case QgsWkbTypes::MultiCurve:
case QgsWkbTypes::MultiSurface:
case QgsWkbTypes::GeometryCollection:
{
return true;
}
default:
break;
}

std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
if ( !multiGeom )
{
return false;
}

QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
if ( sourceMultiGeom )
{
for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
{
if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
return false;
}
}
else
{
if ( !multiGeom->addGeometry( d->geometry->clone() ) )
return false;
}

reset( std::move( geom ) );
return true;
}

bool QgsGeometry::convertToSingleType()
{
if ( !d->geometry )
Expand Down
17 changes: 17 additions & 0 deletions src/core/geometry/qgsgeometry.h
Expand Up @@ -2376,6 +2376,23 @@ class CORE_EXPORT QgsGeometry
*/
bool convertToMultiType();

/**
* Converts a geometry into a multitype geometry of curve kind (when there
* is a corresponding curve type).
* e.g. a polygon into a multisurface geometry with one polygon,
* a multipolygon into a multisurface, a linestring into a multicurve
* geometry with one linestring, or a multilinestring into a multicurve.
* If it is already a multipart curve geometry, it will return TRUE and
* not change the geometry. It will also return TRUE and do nothing if
* the current geometry is a multipoint or a geometry collection. A single
* point will be transformed to a multipoint.
*
* \returns TRUE in case of success and FALSE else
*
* \since QGIS 3.30
*/
bool convertToCurvedMultiType();

/**
* Converts multi type geometry into single type geometry
* e.g. a multipolygon into a polygon geometry. Only the first part of the
Expand Down
5 changes: 5 additions & 0 deletions src/core/providers/qgsprovidermetadata.cpp
Expand Up @@ -77,6 +77,11 @@ QString QgsProviderMetadata::library() const
return mLibrary;
}

QString QgsProviderMetadata::suggestGroupNameForUri( const QString & /*uri*/ ) const
{
return QString();
}

QgsProviderMetadata::CreateDataProviderFunction QgsProviderMetadata::createFunction() const
{
return mCreateFunction;
Expand Down
10 changes: 10 additions & 0 deletions src/core/providers/qgsprovidermetadata.h
Expand Up @@ -457,6 +457,16 @@ class CORE_EXPORT QgsProviderMetadata : public QObject
*/
virtual QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = nullptr ) const;

/**
* Returns a name that can be used as a group name for sublayers retrieved from
* the specified \a uri.
*
* The default implementation returns an empty string.
*
* \since QGIS 3.30
*/
virtual QString suggestGroupNameForUri( const QString &uri ) const;

/**
* Class factory to return a pointer to a newly created QgsDataProvider object
*
Expand Down
19 changes: 18 additions & 1 deletion src/core/providers/qgsprovidersublayertask.cpp
Expand Up @@ -17,6 +17,7 @@

#include "qgsprovidersublayertask.h"
#include "qgsfeedback.h"
#include "qgsprovidermetadata.h"
#include "qgsproviderregistry.h"
#include "qgsprovidersublayerdetails.h"
#include "qgsreadwritelocker.h"
Expand All @@ -28,6 +29,14 @@ QgsProviderSublayerTask::QgsProviderSublayerTask( const QString &uri, bool inclu
{
}

QgsProviderSublayerTask::QgsProviderSublayerTask( const QString &uri, const QString &providerKey, bool includeSystemTables )
: QgsTask( tr( "Retrieving layers" ), QgsTask::CanCancel | QgsTask::CancelWithoutPrompt | QgsTask::Silent )
, mUri( uri )
, mProviderKey( providerKey )
, mIncludeSystemTables( includeSystemTables )
{
}

QList<QgsProviderSublayerDetails> QgsProviderSublayerTask::results() const
{
const QgsReadWriteLocker locker( mLock, QgsReadWriteLocker::Read );
Expand All @@ -44,7 +53,15 @@ bool QgsProviderSublayerTask::run()
if ( mIncludeSystemTables )
flags |= Qgis::SublayerQueryFlag::IncludeSystemTables;

const QList<QgsProviderSublayerDetails> res = QgsProviderRegistry::instance()->querySublayers( mUri, flags, mFeedback.get() );
QList<QgsProviderSublayerDetails> res;
if ( mProviderKey.isEmpty() )
res = QgsProviderRegistry::instance()->querySublayers( mUri, flags, mFeedback.get() );
else
{
QgsProviderMetadata *provider = QgsProviderRegistry::instance()->providerMetadata( mProviderKey );
if ( provider )
res = provider->querySublayers( mUri, flags, mFeedback.get() );
}

const QgsReadWriteLocker locker( mLock, QgsReadWriteLocker::Write );
mResults = res;
Expand Down
10 changes: 10 additions & 0 deletions src/core/providers/qgsprovidersublayertask.h
Expand Up @@ -55,6 +55,14 @@ class CORE_EXPORT QgsProviderSublayerTask : public QgsTask
*/
QgsProviderSublayerTask( const QString &uri, bool includeSystemTables = false );

/**
* Constructor for QgsProviderSublayerTask, which retrieves sublayer details for the
* specified \a uri, restricted to a particular provider.
*
* \since QGIS 3.30
*/
QgsProviderSublayerTask( const QString &uri, const QString &providerKey, bool includeSystemTables = false );

~QgsProviderSublayerTask() override;

/**
Expand All @@ -72,6 +80,8 @@ class CORE_EXPORT QgsProviderSublayerTask : public QgsTask

QString mUri;

QString mProviderKey;

bool mIncludeSystemTables = false;

std::unique_ptr< QgsFeedback > mFeedback;
Expand Down

0 comments on commit bac117e

Please sign in to comment.