Add expression based filter for QgsVectorLayerExporter

This commit is contained in:
Nyall Dawson 2025-03-10 10:56:31 +10:00
parent 32373e1999
commit f22baa38f1
No known key found for this signature in database
GPG Key ID: 4C61673F0BF197FC
5 changed files with 248 additions and 1 deletions

View File

@ -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
};

View File

@ -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
};

View File

@ -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() );
}

View File

@ -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;
};
/**

View File

@ -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",