diff --git a/python/PyQt6/core/auto_generated/vector/qgsvectorlayerexporter.sip.in b/python/PyQt6/core/auto_generated/vector/qgsvectorlayerexporter.sip.in index 04d9f70e75f..dd97eb4742e 100644 --- a/python/PyQt6/core/auto_generated/vector/qgsvectorlayerexporter.sip.in +++ b/python/PyQt6/core/auto_generated/vector/qgsvectorlayerexporter.sip.in @@ -70,6 +70,10 @@ Encapsulates options for use with QgsVectorLayerExporter. %Docstring Sets whether the export should only include selected features. +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.setFilterExpression` + .. note:: This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. @@ -81,6 +85,10 @@ Sets whether the export should only include selected features. %Docstring Returns whether the export will only include selected features. +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.filterExpression` + .. note:: This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. @@ -140,6 +148,58 @@ Only features with a bounding box intersecting this extent will be exported. This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. .. seealso:: :py:func:`setExtent` +%End + + void setFilterExpression( const QString &expression ); +%Docstring +Set the filter ``expression`` for the features to export. + +Only features matching this expression will be exported. + +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.setSelectedOnly` + +.. note:: + + This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. + +.. seealso:: :py:func:`setExpressionContext` + +.. seealso:: :py:func:`filterExpression` +%End + + QString filterExpression() const; +%Docstring +Returns the filter expression for features to export. + +Only features matching this expression will be exported. + +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.selectedOnly` + +.. note:: + + This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. + +.. seealso:: :py:func:`expressionContext` + +.. seealso:: :py:func:`setFilterExpression` +%End + + void setExpressionContext( const QgsExpressionContext &context ); +%Docstring +Sets the expression ``context`` to use when a :py:func:`~ExportOptions.filterExpression` is set. + +.. seealso:: :py:func:`expressionContext` +%End + + const QgsExpressionContext &expressionContext() const; +%Docstring +Returns the expression context used when a :py:func:`~ExportOptions.filterExpression` is set. + +.. seealso:: :py:func:`setExpressionContext` %End }; diff --git a/python/core/auto_generated/vector/qgsvectorlayerexporter.sip.in b/python/core/auto_generated/vector/qgsvectorlayerexporter.sip.in index 04d9f70e75f..dd97eb4742e 100644 --- a/python/core/auto_generated/vector/qgsvectorlayerexporter.sip.in +++ b/python/core/auto_generated/vector/qgsvectorlayerexporter.sip.in @@ -70,6 +70,10 @@ Encapsulates options for use with QgsVectorLayerExporter. %Docstring Sets whether the export should only include selected features. +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.setFilterExpression` + .. note:: This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. @@ -81,6 +85,10 @@ Sets whether the export should only include selected features. %Docstring Returns whether the export will only include selected features. +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.filterExpression` + .. note:: This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. @@ -140,6 +148,58 @@ Only features with a bounding box intersecting this extent will be exported. This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. .. seealso:: :py:func:`setExtent` +%End + + void setFilterExpression( const QString &expression ); +%Docstring +Set the filter ``expression`` for the features to export. + +Only features matching this expression will be exported. + +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.setSelectedOnly` + +.. note:: + + This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. + +.. seealso:: :py:func:`setExpressionContext` + +.. seealso:: :py:func:`filterExpression` +%End + + QString filterExpression() const; +%Docstring +Returns the filter expression for features to export. + +Only features matching this expression will be exported. + +.. warning:: + + This setting is incompatible with :py:func:`~ExportOptions.selectedOnly` + +.. note:: + + This option only applies when the :py:func:`QgsVectorLayerExporter.exportLayer()` method is used. + +.. seealso:: :py:func:`expressionContext` + +.. seealso:: :py:func:`setFilterExpression` +%End + + void setExpressionContext( const QgsExpressionContext &context ); +%Docstring +Sets the expression ``context`` to use when a :py:func:`~ExportOptions.filterExpression` is set. + +.. seealso:: :py:func:`expressionContext` +%End + + const QgsExpressionContext &expressionContext() const; +%Docstring +Returns the expression context used when a :py:func:`~ExportOptions.filterExpression` is set. + +.. seealso:: :py:func:`setExpressionContext` %End }; diff --git a/src/core/vector/qgsvectorlayerexporter.cpp b/src/core/vector/qgsvectorlayerexporter.cpp index cf1eb27e14e..174a2d890f7 100644 --- a/src/core/vector/qgsvectorlayerexporter.cpp +++ b/src/core/vector/qgsvectorlayerexporter.cpp @@ -81,6 +81,26 @@ QgsReferencedRectangle QgsVectorLayerExporter::ExportOptions::extent() const return mExtent; } +void QgsVectorLayerExporter::ExportOptions::setFilterExpression( const QString &expression ) +{ + mFilterExpression = expression; +} + +QString QgsVectorLayerExporter::ExportOptions::filterExpression() const +{ + return mFilterExpression; +} + +void QgsVectorLayerExporter::ExportOptions::setExpressionContext( const QgsExpressionContext &context ) +{ + mExpressionContext = context; +} + +const QgsExpressionContext &QgsVectorLayerExporter::ExportOptions::expressionContext() const +{ + return mExpressionContext; +} + // // QgsVectorLayerExporter @@ -423,7 +443,12 @@ Qgis::VectorExportResult QgsVectorLayerExporter::exportLayer( QgsVectorLayer *la } } - if ( exportOptions.selectedOnly() ) + if ( !exportOptions.filterExpression().isEmpty() ) + { + req.setFilterExpression( exportOptions.filterExpression() ); + req.setExpressionContext( exportOptions.expressionContext() ); + } + else if ( exportOptions.selectedOnly() ) { req.setFilterFids( layer->selectedFeatureIds() ); } diff --git a/src/core/vector/qgsvectorlayerexporter.h b/src/core/vector/qgsvectorlayerexporter.h index 2626406940e..d4e74385d01 100644 --- a/src/core/vector/qgsvectorlayerexporter.h +++ b/src/core/vector/qgsvectorlayerexporter.h @@ -86,6 +86,8 @@ class CORE_EXPORT QgsVectorLayerExporter : public QgsFeatureSink /** * Sets whether the export should only include selected features. * + * \warning This setting is incompatible with setFilterExpression() + * * \note This option only applies when the QgsVectorLayerExporter::exportLayer() method is used. * * \seee selectedOnly() @@ -95,6 +97,8 @@ class CORE_EXPORT QgsVectorLayerExporter : public QgsFeatureSink /** * Returns whether the export will only include selected features. * + * \warning This setting is incompatible with filterExpression() + * * \note This option only applies when the QgsVectorLayerExporter::exportLayer() method is used. * * \seee setSelectedOnly() @@ -151,6 +155,48 @@ class CORE_EXPORT QgsVectorLayerExporter : public QgsFeatureSink */ QgsReferencedRectangle extent() const; + /** + * Set the filter \a expression for the features to export. + * + * Only features matching this expression will be exported. + * + * \warning This setting is incompatible with setSelectedOnly() + * + * \note This option only applies when the QgsVectorLayerExporter::exportLayer() method is used. + * + * \see setExpressionContext() + * \see filterExpression() + */ + void setFilterExpression( const QString &expression ); + + /** + * Returns the filter expression for features to export. + * + * Only features matching this expression will be exported. + * + * \warning This setting is incompatible with selectedOnly() + * + * \note This option only applies when the QgsVectorLayerExporter::exportLayer() method is used. + * + * \see expressionContext() + * \see setFilterExpression() + */ + QString filterExpression() const; + + /** + * Sets the expression \a context to use when a filterExpression() is set. + * + * \see expressionContext() + */ + void setExpressionContext( const QgsExpressionContext &context ); + + /** + * Returns the expression context used when a filterExpression() is set. + * + * \see setExpressionContext() + */ + const QgsExpressionContext &expressionContext() const; + private: bool mSelectedOnly = false; @@ -161,6 +207,9 @@ class CORE_EXPORT QgsVectorLayerExporter : public QgsFeatureSink QgsReferencedRectangle mExtent; + QString mFilterExpression; + QgsExpressionContext mExpressionContext; + }; /** diff --git a/tests/src/python/test_qgsvectorlayerexporter.py b/tests/src/python/test_qgsvectorlayerexporter.py index baac11e0fb5..fbba0230918 100644 --- a/tests/src/python/test_qgsvectorlayerexporter.py +++ b/tests/src/python/test_qgsvectorlayerexporter.py @@ -196,6 +196,59 @@ class TestQgsVectorLayerExporter(QgisTestCase): [f["fldtxt"] for f in layer.getFeatures()], ["test2", "test3"] ) + def test_expression_filter(self): + with tempfile.TemporaryDirectory() as temp_dir: + dest_file_name = Path(temp_dir) / "test_expression.gpkg" + + # create a layer to export + layer = QgsVectorLayer( + "Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory" + ) + self.assertTrue(layer.isValid()) + pr = layer.dataProvider() + f = QgsFeature() + f.setAttributes(["test", 123]) + f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 2))) + f2 = QgsFeature() + f2.setAttributes(["abc", 457]) + f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(3, 4))) + f3 = QgsFeature() + f3.setAttributes(["def", 4573]) + f3.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(5, 6))) + f4 = QgsFeature() + f4.setAttributes(["a feature", 4574]) + f4.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(7, 8))) + self.assertTrue(pr.addFeature(f)) + self.assertTrue(pr.addFeature(f2)) + self.assertTrue(pr.addFeature(f3)) + self.assertTrue(pr.addFeature(f4)) + layer.setCrs(QgsCoordinateReferenceSystem("EPSG:4326")) + + export_options = QgsVectorLayerExporter.ExportOptions() + export_options.setFilterExpression("left(\"fldtxt\", 1) = 'a'") + + res, error = QgsVectorLayerExporter.exportLayer( + layer, + dest_file_name.as_posix(), + "ogr", + export_options, + providerOptions={"layerName": "expression", "update": True}, + ) + self.assertEqual(res, Qgis.VectorExportResult.Success) + + # true to read result + layer = QgsVectorLayer( + dest_file_name.as_posix() + "|layername=expression", + "test", + "ogr", + ) + self.assertTrue(layer.isValid()) + self.assertEqual(layer.featureCount(), 2) + + self.assertCountEqual( + [f["fldtxt"] for f in layer.getFeatures()], ["abc", "a feature"] + ) + @unittest.skipIf( int(gdal.VersionInfo("VERSION_NUM")) < GDAL_COMPUTE_VERSION(3, 8, 0), "GDAL 3.8 required",