From a865a762a437ed804592d6c18dacdface0865b5f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 25 May 2023 11:45:17 +1000 Subject: [PATCH] Add driver filter option for formats capable of multi-layer storage --- .../auto_generated/qgsvectorfilewriter.sip.in | 1 + src/core/qgsgdalutils.cpp | 1 + src/core/qgsvectorfilewriter.cpp | 48 ++++++++++++++++++- src/core/qgsvectorfilewriter.h | 1 + tests/src/python/test_qgsvectorfilewriter.py | 22 +++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/python/core/auto_generated/qgsvectorfilewriter.sip.in b/python/core/auto_generated/qgsvectorfilewriter.sip.in index 262f4c5ced8..bbb902cda54 100644 --- a/python/core/auto_generated/qgsvectorfilewriter.sip.in +++ b/python/core/auto_generated/qgsvectorfilewriter.sip.in @@ -149,6 +149,7 @@ Constructor for MetaData { SortRecommended, SkipNonSpatialFormats, + SupportsMultipleLayers, }; typedef QFlags VectorFormatOptions; diff --git a/src/core/qgsgdalutils.cpp b/src/core/qgsgdalutils.cpp index 202f51e6178..eed4a2aa758 100644 --- a/src/core/qgsgdalutils.cpp +++ b/src/core/qgsgdalutils.cpp @@ -702,6 +702,7 @@ QStringList QgsGdalUtils::multiLayerFileExtensions() QStringLiteral( "pbf" ), QStringLiteral( "vrt" ), QStringLiteral( "nc" ), + QStringLiteral( "dxf" ), QStringLiteral( "shp.zip" ) }; return SUPPORTED_DB_LAYERS_EXTENSIONS; #endif diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index ff4e1da88f2..bedc902f4f3 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -19,6 +19,7 @@ #include "qgsapplication.h" #include "qgsfields.h" +#include "qgsgdalutils.h" #include "qgslogger.h" #include "qgsmessagelog.h" #include "qgscoordinatereferencesystem.h" @@ -3785,6 +3786,33 @@ void QgsVectorFileWriter::setSymbologyScale( double d ) mRenderContext.setRendererScale( mSymbologyScale ); } +QStringList multiLayerFormats() +{ + QStringList driverNames; + const QSet< QString > multiLayerExtensions = qgis::listToSet( QgsGdalUtils::multiLayerFileExtensions() ); + + for ( int i = 0; i < GDALGetDriverCount(); ++i ) + { + GDALDriverH driver = GDALGetDriver( i ); + if ( !driver ) + { + QgsLogger::warning( "unable to get driver " + QString::number( i ) ); + continue; + } + + const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" ); + if ( driverExtensions.isEmpty() ) + continue; + + const QSet< QString > splitExtensions = qgis::listToSet( driverExtensions.split( ' ', Qt::SkipEmptyParts ) ); + if ( splitExtensions.intersects( multiLayerExtensions ) ) + { + driverNames << OGR_Dr_GetName( driver ); + } + } + return driverNames; +} + QList< QgsVectorFileWriter::FilterFormatDetails > QgsVectorFileWriter::supportedFiltersAndFormats( const VectorFormatOptions options ) { static QReadWriteLock sFilterLock; @@ -3802,12 +3830,20 @@ QList< QgsVectorFileWriter::FilterFormatDetails > QgsVectorFileWriter::supported QgsApplication::registerOgrDrivers(); int const drvCount = OGRGetDriverCount(); + const QStringList multiLayerDrivers = multiLayerFormats(); + for ( int i = 0; i < drvCount; ++i ) { OGRSFDriverH drv = OGRGetDriver( i ); if ( drv ) { - QString drvName = OGR_Dr_GetName( drv ); + const QString drvName = OGR_Dr_GetName( drv ); + + if ( options & SupportsMultipleLayers ) + { + if ( !multiLayerDrivers.contains( drvName ) ) + continue; + } GDALDriverH gdalDriver = GDALGetDriverByName( drvName.toLocal8Bit().constData() ); char **metadata = nullptr; @@ -3918,13 +3954,21 @@ QList< QgsVectorFileWriter::DriverDetails > QgsVectorFileWriter::ogrDriverList( QgsApplication::registerOgrDrivers(); const int drvCount = OGRGetDriverCount(); + const QStringList multiLayerDrivers = multiLayerFormats(); + QStringList writableDrivers; for ( int i = 0; i < drvCount; ++i ) { OGRSFDriverH drv = OGRGetDriver( i ); if ( drv ) { - QString drvName = OGR_Dr_GetName( drv ); + const QString drvName = OGR_Dr_GetName( drv ); + + if ( options & SupportsMultipleLayers ) + { + if ( !multiLayerDrivers.contains( drvName ) ) + continue; + } if ( options & SkipNonSpatialFormats ) { diff --git a/src/core/qgsvectorfilewriter.h b/src/core/qgsvectorfilewriter.h index 38cfcfb604f..f371814ce1e 100644 --- a/src/core/qgsvectorfilewriter.h +++ b/src/core/qgsvectorfilewriter.h @@ -199,6 +199,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink { SortRecommended = 1 << 1, //!< Use recommended sort order, with extremely commonly used formats listed first SkipNonSpatialFormats = 1 << 2, //!< Filter out any formats which do not have spatial support (e.g. those which cannot save geometries) + SupportsMultipleLayers = 1 << 3, //!< Filter to only formats which support multiple layers (since QGIS 3.32) }; Q_DECLARE_FLAGS( VectorFormatOptions, VectorFormatOption ) diff --git a/tests/src/python/test_qgsvectorfilewriter.py b/tests/src/python/test_qgsvectorfilewriter.py index 151bfa14fc3..7b4d9801091 100644 --- a/tests/src/python/test_qgsvectorfilewriter.py +++ b/tests/src/python/test_qgsvectorfilewriter.py @@ -834,6 +834,17 @@ class TestQgsVectorFileWriter(unittest.TestCase): formats = QgsVectorFileWriter.supportedFiltersAndFormats(QgsVectorFileWriter.SkipNonSpatialFormats) self.assertFalse('ODS' in [f.driverName for f in formats]) + # multilayer formats + formats = QgsVectorFileWriter.supportedFiltersAndFormats(QgsVectorFileWriter.SupportsMultipleLayers) + self.assertTrue('DXF' in [f.driverName for f in formats]) + self.assertFalse('ESRI Shapefile' in [f.driverName for f in formats]) + self.assertTrue('XLSX' in [f.driverName for f in formats]) + + formats = QgsVectorFileWriter.supportedFiltersAndFormats(QgsVectorFileWriter.SupportsMultipleLayers | QgsVectorFileWriter.SkipNonSpatialFormats) + self.assertTrue('DXF' in [f.driverName for f in formats]) + self.assertFalse('ESRI Shapefile' in [f.driverName for f in formats]) + self.assertFalse('XLSX' in [f.driverName for f in formats]) + def testOgrDriverList(self): # test with drivers in recommended order drivers = QgsVectorFileWriter.ogrDriverList(QgsVectorFileWriter.SortRecommended) @@ -860,6 +871,17 @@ class TestQgsVectorFileWriter(unittest.TestCase): formats = QgsVectorFileWriter.ogrDriverList(QgsVectorFileWriter.SkipNonSpatialFormats) self.assertFalse('ODS' in [f.driverName for f in formats]) + # multilayer formats + formats = QgsVectorFileWriter.ogrDriverList(QgsVectorFileWriter.SupportsMultipleLayers) + self.assertTrue('DXF' in [f.driverName for f in formats]) + self.assertFalse('ESRI Shapefile' in [f.driverName for f in formats]) + self.assertTrue('XLSX' in [f.driverName for f in formats]) + + formats = QgsVectorFileWriter.ogrDriverList(QgsVectorFileWriter.SupportsMultipleLayers | QgsVectorFileWriter.SkipNonSpatialFormats) + self.assertTrue('DXF' in [f.driverName for f in formats]) + self.assertFalse('ESRI Shapefile' in [f.driverName for f in formats]) + self.assertFalse('XLSX' in [f.driverName for f in formats]) + def testSupportedFormatExtensions(self): formats = QgsVectorFileWriter.supportedFormatExtensions() self.assertTrue('gpkg' in formats)