diff --git a/python/core/auto_generated/qgsvectorfilewriter.sip.in b/python/core/auto_generated/qgsvectorfilewriter.sip.in index e11fc355776..ea0bc3a237d 100644 --- a/python/core/auto_generated/qgsvectorfilewriter.sip.in +++ b/python/core/auto_generated/qgsvectorfilewriter.sip.in @@ -143,6 +143,11 @@ Constructor for MetaData SymbolLayerSymbology }; + enum FieldNameSource + { + Original, + PreferAlias, + }; enum VectorFormatOption { @@ -384,6 +389,8 @@ Constructor QgsVectorFileWriter::FieldValueConverter *fieldValueConverter; QgsFeedback *feedback; + + FieldNameSource fieldNameSource; }; diff --git a/src/core/qgsvectorfilewriter.cpp b/src/core/qgsvectorfilewriter.cpp index 4bba0dfef1e..75fbbbecfea 100644 --- a/src/core/qgsvectorfilewriter.cpp +++ b/src/core/qgsvectorfilewriter.cpp @@ -107,7 +107,8 @@ QgsVectorFileWriter::QgsVectorFileWriter( SymbologyExport symbologyExport, QgsFeatureSink::SinkFlags sinkFlags, QString *newLayer, - QgsCoordinateTransformContext transformContext + QgsCoordinateTransformContext transformContext, + FieldNameSource fieldNameSouce ) : mError( NoError ) , mWkbType( geometryType ) @@ -116,7 +117,7 @@ QgsVectorFileWriter::QgsVectorFileWriter( { init( vectorFileName, fileEncoding, fields, geometryType, srs, driverName, datasourceOptions, layerOptions, newFilename, nullptr, - QString(), CreateOrOverwriteFile, newLayer, sinkFlags, transformContext ); + QString(), CreateOrOverwriteFile, newLayer, sinkFlags, transformContext, fieldNameSouce ); } QgsVectorFileWriter::QgsVectorFileWriter( @@ -135,7 +136,8 @@ QgsVectorFileWriter::QgsVectorFileWriter( ActionOnExistingFile action, QString *newLayer, QgsCoordinateTransformContext transformContext, - QgsFeatureSink::SinkFlags sinkFlags + QgsFeatureSink::SinkFlags sinkFlags, + FieldNameSource fieldNameSource ) : mError( NoError ) , mWkbType( geometryType ) @@ -144,7 +146,7 @@ QgsVectorFileWriter::QgsVectorFileWriter( { init( vectorFileName, fileEncoding, fields, geometryType, srs, driverName, datasourceOptions, layerOptions, newFilename, fieldValueConverter, - layerName, action, newLayer, sinkFlags, transformContext ); + layerName, action, newLayer, sinkFlags, transformContext, fieldNameSource ); } QgsVectorFileWriter *QgsVectorFileWriter::create( @@ -163,7 +165,7 @@ QgsVectorFileWriter *QgsVectorFileWriter::create( return new QgsVectorFileWriter( fileName, options.fileEncoding, fields, geometryType, srs, options.driverName, options.datasourceOptions, options.layerOptions, newFilename, options.symbologyExport, options.fieldValueConverter, options.layerName, - options.actionOnExistingFile, newLayer, transformContext, sinkFlags ); + options.actionOnExistingFile, newLayer, transformContext, sinkFlags, options.fieldNameSource ); Q_NOWARN_DEPRECATED_POP } @@ -201,7 +203,7 @@ void QgsVectorFileWriter::init( QString vectorFileName, const QString &layerNameIn, ActionOnExistingFile action, QString *newLayer, SinkFlags sinkFlags, - const QgsCoordinateTransformContext &transformContext ) + const QgsCoordinateTransformContext &transformContext, FieldNameSource fieldNameSource ) { mRenderContext.setRendererScale( mSymbologyScale ); @@ -604,10 +606,9 @@ void QgsVectorFileWriter::init( QString vectorFileName, attrField = fieldValueConverter->fieldDefinition( fields.at( fldIdx ) ); } - QString name( attrField.name() ); if ( action == AppendToLayerAddFields ) { - int ogrIdx = OGR_FD_GetFieldIndex( defn, mCodec->fromUnicode( name ) ); + int ogrIdx = OGR_FD_GetFieldIndex( defn, mCodec->fromUnicode( attrField.name() ) ); if ( ogrIdx >= 0 ) { mAttrIdxToOgrIdx.insert( fldIdx, ogrIdx ); @@ -615,6 +616,18 @@ void QgsVectorFileWriter::init( QString vectorFileName, } } + QString name; + switch ( fieldNameSource ) + { + case Original: + name = attrField.name(); + break; + + case PreferAlias: + name = !attrField.alias().isEmpty() ? attrField.alias() : attrField.name(); + break; + } + OGRFieldType ogrType = OFTString; //default to string int ogrWidth = attrField.length(); int ogrPrecision = attrField.precision(); diff --git a/src/core/qgsvectorfilewriter.h b/src/core/qgsvectorfilewriter.h index ca7f760730d..4c1e3909666 100644 --- a/src/core/qgsvectorfilewriter.h +++ b/src/core/qgsvectorfilewriter.h @@ -185,6 +185,16 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink SymbolLayerSymbology //Exports one feature per symbol layer (considering symbol levels) }; + /** + * Source for exported field names. + * + * \since QGIS 3.18 + */ + enum FieldNameSource + { + Original = 0, //!< Use original field names + PreferAlias, //!< Use the original field alias as the exported field name, wherever one is set. Otherwise use the original field names. + }; /** * Options for sorting and filtering vector formats. @@ -514,6 +524,13 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink //! Optional feedback object allowing cancellation of layer save QgsFeedback *feedback = nullptr; + + /** + * Source for exported field names. + * + * \since QGIS 3.18 + */ + FieldNameSource fieldNameSource = Original; }; #ifndef SIP_RUN @@ -570,7 +587,8 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() #ifndef SIP_RUN , QString *newLayer = nullptr, - QgsCoordinateTransformContext transformContext = QgsCoordinateTransformContext() + QgsCoordinateTransformContext transformContext = QgsCoordinateTransformContext(), + FieldNameSource fieldNameSource = Original #endif ) SIP_DEPRECATED; @@ -592,6 +610,7 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink * \param newLayer potentially modified layer name (output parameter) (added in QGIS 3.4) * \param transformContext transform context, needed if the output file srs is forced to specific crs (added in QGIS 3.10.3) * \param sinkFlags feature sink flags (added in QGIS 3.10.3) + * \param fieldNameSource source for field names (since QGIS 3.18) * \note not available in Python bindings * \deprecated Use create() instead. */ @@ -610,7 +629,8 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink QgsVectorFileWriter::ActionOnExistingFile action, QString *newLayer = nullptr, QgsCoordinateTransformContext transformContext = QgsCoordinateTransformContext(), - QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() + QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(), + FieldNameSource fieldNameSource = Original ) SIP_SKIP; //! QgsVectorFileWriter cannot be copied. @@ -967,7 +987,8 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink QgsVectorFileWriter::FieldValueConverter *fieldValueConverter, const QString &layerName, QgsVectorFileWriter::ActionOnExistingFile action, QString *newLayer, QgsFeatureSink::SinkFlags sinkFlags, - const QgsCoordinateTransformContext &transformContext ); + const QgsCoordinateTransformContext &transformContext, + FieldNameSource fieldNameSource ); void resetMap( const QgsAttributeList &attributes ); std::unique_ptr< QgsFeatureRenderer > mRenderer; diff --git a/tests/src/python/test_qgsvectorfilewriter.py b/tests/src/python/test_qgsvectorfilewriter.py index 0e4cba64e81..e361319d686 100644 --- a/tests/src/python/test_qgsvectorfilewriter.py +++ b/tests/src/python/test_qgsvectorfilewriter.py @@ -99,6 +99,56 @@ class TestQgsVectorFileWriter(unittest.TestCase): writeShape(self.mMemoryLayer, 'writetest.shp') + def testWritePreferAlias(self): + """Test prefering field alias.""" + layer = QgsVectorLayer( + ('Point?crs=epsg:4326&field=name:string(20)&' + 'field=age:integer&field=size:double&index=yes'), + 'test', + 'memory') + + self.assertTrue(layer.isValid()) + myProvider = layer.dataProvider() + + layer.setFieldAlias(0, 'My Name') + layer.setFieldAlias(2, 'My Size') + + ft = QgsFeature() + ft.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 10))) + ft.setAttributes(['Johny', 20, 0.3]) + myResult, myFeatures = myProvider.addFeatures([ft]) + self.assertTrue(myResult) + self.assertTrue(myFeatures) + + options = QgsVectorFileWriter.SaveVectorOptions() + options.driverName = 'ESRI Shapefile' + options.fieldNameSource = QgsVectorFileWriter.Original + + dest = os.path.join(str(QDir.tempPath()), 'alias.shp') + result, err = QgsVectorFileWriter.writeAsVectorFormatV2( + layer, + dest, + QgsProject.instance().transformContext(), + options) + self.assertEqual(result, QgsVectorFileWriter.NoError) + + res = QgsVectorLayer(dest, 'result') + self.assertTrue(res.isValid()) + self.assertEqual([f.name() for f in res.fields()], ['name', 'age', 'size']) + + options.fieldNameSource = QgsVectorFileWriter.PreferAlias + dest = os.path.join(str(QDir.tempPath()), 'alias2.shp') + result, err = QgsVectorFileWriter.writeAsVectorFormatV2( + layer, + dest, + QgsProject.instance().transformContext(), + options) + self.assertEqual(result, QgsVectorFileWriter.NoError) + + res = QgsVectorLayer(dest, 'result') + self.assertTrue(res.isValid()) + self.assertEqual([f.name() for f in res.fields()], ['My Name', 'age', 'My Size']) + def testWriteWithLongLongField(self): ml = QgsVectorLayer('NoGeometry?crs=epsg:4326&field=fldlonglong:long', 'test2', 'memory')