From 93b7a0c3d5042d58cd545a075c29ad4dc7df8094 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 5 May 2021 10:27:25 +1000 Subject: [PATCH] [api] Add option to QgsVectorFileWriter to store layer metadata for created layer --- .../auto_generated/qgsvectorfilewriter.sip.in | 5 ++ src/core/qgsvectorfilewriter.cpp | 50 ++++++++++++++++++- src/core/qgsvectorfilewriter.h | 17 +++++++ tests/src/python/test_qgsvectorfilewriter.py | 47 ++++++++++++++++- 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/python/core/auto_generated/qgsvectorfilewriter.sip.in b/python/core/auto_generated/qgsvectorfilewriter.sip.in index 548e18a8721..73744e3111a 100644 --- a/python/core/auto_generated/qgsvectorfilewriter.sip.in +++ b/python/core/auto_generated/qgsvectorfilewriter.sip.in @@ -135,6 +135,7 @@ Constructor for MetaData ErrProjection, ErrFeatureWriteFailed, ErrInvalidLayer, + ErrSavingMetadata, Canceled, }; @@ -393,6 +394,10 @@ Constructor QgsFeedback *feedback; FieldNameSource fieldNameSource; + + bool saveMetadata; + + QgsLayerMetadata layerMetadata; }; diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 843e961e505..ef6a39d69aa 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -3192,9 +3192,18 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV2( Pre } } - std::unique_ptr< QgsVectorFileWriter > writer( create( fileName, details.outputFields, destWkbType, details.outputCrs, transformContext, options, QgsFeatureSink::SinkFlags(), newFilename, newLayer ) ); + QString tempNewFilename; + QString tempNewLayer; + + std::unique_ptr< QgsVectorFileWriter > writer( create( fileName, details.outputFields, destWkbType, details.outputCrs, transformContext, options, QgsFeatureSink::SinkFlags(), &tempNewFilename, &tempNewLayer ) ); writer->setSymbologyScale( options.symbologyScale ); + if ( newFilename ) + *newFilename = tempNewFilename; + + if ( newLayer ) + *newLayer = tempNewLayer; + if ( newFilename ) { QgsDebugMsgLevel( "newFilename = " + *newFilename, 2 ); @@ -3335,7 +3344,44 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV2( Pre *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n ); } - return errors == 0 ? NoError : ErrFeatureWriteFailed; + writer.reset(); + + bool metadataFailure = false; + if ( options.saveMetadata ) + { + QString uri = QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), QVariantMap + { + {QStringLiteral( "path" ), tempNewFilename }, + {QStringLiteral( "layerName" ), tempNewLayer } + } ); + + try + { + QString error; + if ( !QgsProviderRegistry::instance()->saveLayerMetadata( QStringLiteral( "ogr" ), uri, options.layerMetadata, error ) ) + { + if ( errorMessage ) + { + if ( !errorMessage->isEmpty() ) + *errorMessage += '\n'; + *errorMessage += error; + } + metadataFailure = true; + } + } + catch ( QgsNotSupportedException &e ) + { + if ( errorMessage ) + { + if ( !errorMessage->isEmpty() ) + *errorMessage += '\n'; + *errorMessage += e.what(); + } + metadataFailure = true; + } + } + + return errors == 0 ? ( !metadataFailure ? NoError : ErrSavingMetadata ) : ErrFeatureWriteFailed; } QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer *layer, diff --git a/src/core/qgsvectorfilewriter.h b/src/core/qgsvectorfilewriter.h index df09fa399b0..e0e7f0b8f08 100644 --- a/src/core/qgsvectorfilewriter.h +++ b/src/core/qgsvectorfilewriter.h @@ -177,6 +177,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink ErrProjection, ErrFeatureWriteFailed, ErrInvalidLayer, + ErrSavingMetadata, //!< Metadata saving failed Canceled, //!< Writing was interrupted by manual cancellation }; @@ -533,6 +534,22 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink * \since QGIS 3.18 */ FieldNameSource fieldNameSource = Original; + + /** + * Set to TRUE to save layer metadata for the exported vector file. + * + * \see layerMetadata + * \since QGIS 3.20 + */ + bool saveMetadata = false; + + /** + * Layer metadata to save for the exported vector file. This will only be used if saveMetadata is TRUE. + * + * \see saveMetadata + * \since QGIS 3.20 + */ + QgsLayerMetadata layerMetadata; }; #ifndef SIP_RUN diff --git a/tests/src/python/test_qgsvectorfilewriter.py b/tests/src/python/test_qgsvectorfilewriter.py index 2d22b898b71..35b0b02ffe8 100644 --- a/tests/src/python/test_qgsvectorfilewriter.py +++ b/tests/src/python/test_qgsvectorfilewriter.py @@ -33,7 +33,8 @@ from qgis.core import (QgsVectorLayer, QgsFields, QgsCoordinateTransformContext, QgsFeatureSink, - QgsMemoryProviderUtils + QgsMemoryProviderUtils, + QgsLayerMetadata ) from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir, QByteArray import os @@ -1410,6 +1411,50 @@ class TestQgsVectorFileWriter(unittest.TestCase): vl = QgsVectorLayer(dest_file_name) self.assertTrue(vl.isValid()) + def testPersistMetadata(self): + """ + Test that metadata from the source layer is saved as default for the destination if the + persist metadat option is enabled + """ + vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') + self.assertTrue(vl.startEditing()) + f = QgsFeature(vl.fields()) + f.setGeometry(QgsGeometry.fromWkt('point(9 45)')) + f.setAttribute(0, 'QGIS Rocks!') # not valid! + self.assertTrue(vl.addFeatures([f])) + f.setAttribute(0, 12345) # valid! + self.assertTrue(vl.addFeatures([f])) + + # set some metadata on the source layer + metadata = QgsLayerMetadata() + metadata.setTitle('my title') + metadata.setAbstract('my abstract') + metadata.setLicenses(['l1', 'l2']) + + dest_file_name = os.path.join(str(QDir.tempPath()), 'save_metadata.gpkg') + + options = QgsVectorFileWriter.SaveVectorOptions() + options.driverName = 'GPKG' + options.layerName = 'test' + options.saveMetadata = True + options.layerMetadata = metadata + + write_result, error_message, new_file, new_layer = QgsVectorFileWriter.writeAsVectorFormatV3( + vl, + dest_file_name, + QgsProject.instance().transformContext(), + options) + self.assertEqual(write_result, QgsVectorFileWriter.ErrFeatureWriteFailed, error_message) + + # Open result and check + created_layer = QgsVectorLayer(f'{new_file}|layerName={new_layer}', 'test', 'ogr') + self.assertTrue(created_layer.isValid()) + + # layer should have metadata stored + self.assertEqual(created_layer.metadata().title(), 'my title') + self.assertEqual(created_layer.metadata().abstract(), 'my abstract') + self.assertEqual(created_layer.metadata().licenses(), ['l1', 'l2']) + if __name__ == '__main__': unittest.main()