Merge pull request #62539 from nyalldawson/package_dest_crs

Add optional destination crs for Package Layers algorithm
This commit is contained in:
Alexander Bruy 2025-07-09 10:13:38 +01:00 committed by GitHub
commit d22e0d2eb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 152 additions and 38 deletions

View File

@ -49,7 +49,7 @@ try:
except (NameError, AttributeError):
pass
try:
QgsVectorFileWriter.SaveVectorOptions.__attribute_docs__ = {'driverName': 'OGR driver to use', 'layerName': 'Layer name. If let empty, it will be derived from the filename', 'actionOnExistingFile': 'Action on existing file', 'fileEncoding': 'Encoding to use', 'ct': 'Transform to reproject exported geometries with, or invalid transform\nfor no transformation', 'onlySelectedFeatures': 'Write only selected features of layer', 'datasourceOptions': 'List of OGR data source creation options', 'layerOptions': 'List of OGR layer creation options', 'skipAttributeCreation': 'Only write geometries', 'attributes': 'Attributes to export (empty means all unless skipAttributeCreation is set)', 'attributesExportNames': 'Attributes export names', 'symbologyExport': 'Symbology to export', 'symbologyScale': 'Scale of symbology', 'filterExtent': 'If not empty, only features intersecting the extent will be saved', 'overrideGeometryType': 'Set to a valid geometry type to override the default geometry type for the layer. This parameter\nallows for conversion of geometryless tables to null geometries, etc.', 'forceMulti': 'Sets to ``True`` to force creation of multipart geometries', 'includeZ': 'Sets to ``True`` to include z dimension in output. This option is only valid if overrideGeometryType is set', 'fieldValueConverter': 'Field value converter.\n\nOwnership is not transferred and callers must ensure that the lifetime of fieldValueConverter\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.', 'feedback': 'Optional feedback object allowing cancellation of layer save', 'fieldNameSource': 'Source for exported field names.\n\n.. versionadded:: 3.18', 'saveMetadata': 'Set to ``True`` to save layer metadata for the exported vector file.\n\n.. seealso:: :py:func:`layerMetadata`\n\n.. versionadded:: 3.20', 'layerMetadata': 'Layer metadata to save for the exported vector file. This will only be used if saveMetadata is ``True``.\n\n.. seealso:: :py:func:`saveMetadata`\n\n.. versionadded:: 3.20', 'includeConstraints': 'Set to ``True`` to transfer field constraints to the exported vector file.\n\nSupport for field constraints depends on the output file format.\n\n.. versionadded:: 3.34', 'setFieldDomains': 'Set to ``True`` to transfer field domains to the exported vector file.\n\nSupport for field domains depends on the output file format.\n\n.. note::\n\n Only available in builds based on GDAL 3.5 or later\n\n.. versionadded:: 3.36', 'sourceDatabaseProviderConnection': 'Source database provider connection, for field domains.\n\nOwnership is not transferred and callers must ensure that the lifetime of sourceDatabaseProviderConnection\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.\n\n.. versionadded:: 3.36'}
QgsVectorFileWriter.SaveVectorOptions.__attribute_docs__ = {'driverName': 'OGR driver to use', 'layerName': 'Layer name. If let empty, it will be derived from the filename', 'actionOnExistingFile': 'Action on existing file', 'fileEncoding': 'Encoding to use', 'ct': 'Transform to reproject exported geometries with, or invalid transform\nfor no transformation', 'onlySelectedFeatures': 'Write only selected features of layer', 'datasourceOptions': 'List of OGR data source creation options', 'layerOptions': 'List of OGR layer creation options', 'skipAttributeCreation': 'Only write geometries', 'attributes': 'Attributes to export (empty means all unless skipAttributeCreation is set)', 'attributesExportNames': 'Attributes export names', 'symbologyExport': 'Symbology to export', 'symbologyScale': 'Scale of symbology', 'filterExtent': "If not empty, only features intersecting the extent will be saved.\n\nThe filter extent should be in the destination CRS (if transforming), or the layer's\nCRS if no valid transform is set.", 'overrideGeometryType': 'Set to a valid geometry type to override the default geometry type for the layer. This parameter\nallows for conversion of geometryless tables to null geometries, etc.', 'forceMulti': 'Sets to ``True`` to force creation of multipart geometries', 'includeZ': 'Sets to ``True`` to include z dimension in output. This option is only valid if overrideGeometryType is set', 'fieldValueConverter': 'Field value converter.\n\nOwnership is not transferred and callers must ensure that the lifetime of fieldValueConverter\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.', 'feedback': 'Optional feedback object allowing cancellation of layer save', 'fieldNameSource': 'Source for exported field names.\n\n.. versionadded:: 3.18', 'saveMetadata': 'Set to ``True`` to save layer metadata for the exported vector file.\n\n.. seealso:: :py:func:`layerMetadata`\n\n.. versionadded:: 3.20', 'layerMetadata': 'Layer metadata to save for the exported vector file. This will only be used if saveMetadata is ``True``.\n\n.. seealso:: :py:func:`saveMetadata`\n\n.. versionadded:: 3.20', 'includeConstraints': 'Set to ``True`` to transfer field constraints to the exported vector file.\n\nSupport for field constraints depends on the output file format.\n\n.. versionadded:: 3.34', 'setFieldDomains': 'Set to ``True`` to transfer field domains to the exported vector file.\n\nSupport for field domains depends on the output file format.\n\n.. note::\n\n Only available in builds based on GDAL 3.5 or later\n\n.. versionadded:: 3.36', 'sourceDatabaseProviderConnection': 'Source database provider connection, for field domains.\n\nOwnership is not transferred and callers must ensure that the lifetime of sourceDatabaseProviderConnection\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.\n\n.. versionadded:: 3.36'}
QgsVectorFileWriter.SaveVectorOptions.__annotations__ = {'driverName': str, 'layerName': str, 'actionOnExistingFile': 'QgsVectorFileWriter.ActionOnExistingFile', 'fileEncoding': str, 'ct': 'QgsCoordinateTransform', 'onlySelectedFeatures': bool, 'datasourceOptions': 'List[str]', 'layerOptions': 'List[str]', 'skipAttributeCreation': bool, 'attributes': 'QgsAttributeList', 'attributesExportNames': 'List[str]', 'symbologyExport': 'Qgis.FeatureSymbologyExport', 'symbologyScale': float, 'filterExtent': 'QgsRectangle', 'overrideGeometryType': 'Qgis.WkbType', 'forceMulti': bool, 'includeZ': bool, 'fieldValueConverter': 'QgsVectorFileWriter.FieldValueConverter', 'feedback': 'QgsFeedback', 'fieldNameSource': 'QgsVectorFileWriter.FieldNameSource', 'saveMetadata': bool, 'layerMetadata': 'QgsLayerMetadata', 'includeConstraints': bool, 'setFieldDomains': bool, 'sourceDatabaseProviderConnection': 'QgsAbstractDatabaseProviderConnection'}
except (NameError, AttributeError):
pass

View File

@ -5,7 +5,7 @@ try:
except (NameError, AttributeError):
pass
try:
QgsVectorFileWriter.SaveVectorOptions.__attribute_docs__ = {'driverName': 'OGR driver to use', 'layerName': 'Layer name. If let empty, it will be derived from the filename', 'actionOnExistingFile': 'Action on existing file', 'fileEncoding': 'Encoding to use', 'ct': 'Transform to reproject exported geometries with, or invalid transform\nfor no transformation', 'onlySelectedFeatures': 'Write only selected features of layer', 'datasourceOptions': 'List of OGR data source creation options', 'layerOptions': 'List of OGR layer creation options', 'skipAttributeCreation': 'Only write geometries', 'attributes': 'Attributes to export (empty means all unless skipAttributeCreation is set)', 'attributesExportNames': 'Attributes export names', 'symbologyExport': 'Symbology to export', 'symbologyScale': 'Scale of symbology', 'filterExtent': 'If not empty, only features intersecting the extent will be saved', 'overrideGeometryType': 'Set to a valid geometry type to override the default geometry type for the layer. This parameter\nallows for conversion of geometryless tables to null geometries, etc.', 'forceMulti': 'Sets to ``True`` to force creation of multipart geometries', 'includeZ': 'Sets to ``True`` to include z dimension in output. This option is only valid if overrideGeometryType is set', 'fieldValueConverter': 'Field value converter.\n\nOwnership is not transferred and callers must ensure that the lifetime of fieldValueConverter\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.', 'feedback': 'Optional feedback object allowing cancellation of layer save', 'fieldNameSource': 'Source for exported field names.\n\n.. versionadded:: 3.18', 'saveMetadata': 'Set to ``True`` to save layer metadata for the exported vector file.\n\n.. seealso:: :py:func:`layerMetadata`\n\n.. versionadded:: 3.20', 'layerMetadata': 'Layer metadata to save for the exported vector file. This will only be used if saveMetadata is ``True``.\n\n.. seealso:: :py:func:`saveMetadata`\n\n.. versionadded:: 3.20', 'includeConstraints': 'Set to ``True`` to transfer field constraints to the exported vector file.\n\nSupport for field constraints depends on the output file format.\n\n.. versionadded:: 3.34', 'setFieldDomains': 'Set to ``True`` to transfer field domains to the exported vector file.\n\nSupport for field domains depends on the output file format.\n\n.. note::\n\n Only available in builds based on GDAL 3.5 or later\n\n.. versionadded:: 3.36', 'sourceDatabaseProviderConnection': 'Source database provider connection, for field domains.\n\nOwnership is not transferred and callers must ensure that the lifetime of sourceDatabaseProviderConnection\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.\n\n.. versionadded:: 3.36'}
QgsVectorFileWriter.SaveVectorOptions.__attribute_docs__ = {'driverName': 'OGR driver to use', 'layerName': 'Layer name. If let empty, it will be derived from the filename', 'actionOnExistingFile': 'Action on existing file', 'fileEncoding': 'Encoding to use', 'ct': 'Transform to reproject exported geometries with, or invalid transform\nfor no transformation', 'onlySelectedFeatures': 'Write only selected features of layer', 'datasourceOptions': 'List of OGR data source creation options', 'layerOptions': 'List of OGR layer creation options', 'skipAttributeCreation': 'Only write geometries', 'attributes': 'Attributes to export (empty means all unless skipAttributeCreation is set)', 'attributesExportNames': 'Attributes export names', 'symbologyExport': 'Symbology to export', 'symbologyScale': 'Scale of symbology', 'filterExtent': "If not empty, only features intersecting the extent will be saved.\n\nThe filter extent should be in the destination CRS (if transforming), or the layer's\nCRS if no valid transform is set.", 'overrideGeometryType': 'Set to a valid geometry type to override the default geometry type for the layer. This parameter\nallows for conversion of geometryless tables to null geometries, etc.', 'forceMulti': 'Sets to ``True`` to force creation of multipart geometries', 'includeZ': 'Sets to ``True`` to include z dimension in output. This option is only valid if overrideGeometryType is set', 'fieldValueConverter': 'Field value converter.\n\nOwnership is not transferred and callers must ensure that the lifetime of fieldValueConverter\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.', 'feedback': 'Optional feedback object allowing cancellation of layer save', 'fieldNameSource': 'Source for exported field names.\n\n.. versionadded:: 3.18', 'saveMetadata': 'Set to ``True`` to save layer metadata for the exported vector file.\n\n.. seealso:: :py:func:`layerMetadata`\n\n.. versionadded:: 3.20', 'layerMetadata': 'Layer metadata to save for the exported vector file. This will only be used if saveMetadata is ``True``.\n\n.. seealso:: :py:func:`saveMetadata`\n\n.. versionadded:: 3.20', 'includeConstraints': 'Set to ``True`` to transfer field constraints to the exported vector file.\n\nSupport for field constraints depends on the output file format.\n\n.. versionadded:: 3.34', 'setFieldDomains': 'Set to ``True`` to transfer field domains to the exported vector file.\n\nSupport for field domains depends on the output file format.\n\n.. note::\n\n Only available in builds based on GDAL 3.5 or later\n\n.. versionadded:: 3.36', 'sourceDatabaseProviderConnection': 'Source database provider connection, for field domains.\n\nOwnership is not transferred and callers must ensure that the lifetime of sourceDatabaseProviderConnection\nexceeds the lifetime of the :py:class:`QgsVectorFileWriter` object.\n\n.. versionadded:: 3.36'}
QgsVectorFileWriter.SaveVectorOptions.__annotations__ = {'driverName': str, 'layerName': str, 'actionOnExistingFile': 'QgsVectorFileWriter.ActionOnExistingFile', 'fileEncoding': str, 'ct': 'QgsCoordinateTransform', 'onlySelectedFeatures': bool, 'datasourceOptions': 'List[str]', 'layerOptions': 'List[str]', 'skipAttributeCreation': bool, 'attributes': 'QgsAttributeList', 'attributesExportNames': 'List[str]', 'symbologyExport': 'Qgis.FeatureSymbologyExport', 'symbologyScale': float, 'filterExtent': 'QgsRectangle', 'overrideGeometryType': 'Qgis.WkbType', 'forceMulti': bool, 'includeZ': bool, 'fieldValueConverter': 'QgsVectorFileWriter.FieldValueConverter', 'feedback': 'QgsFeedback', 'fieldNameSource': 'QgsVectorFileWriter.FieldNameSource', 'saveMetadata': bool, 'layerMetadata': 'QgsLayerMetadata', 'includeConstraints': bool, 'setFieldDomains': bool, 'sourceDatabaseProviderConnection': 'QgsAbstractDatabaseProviderConnection'}
except (NameError, AttributeError):
pass

View File

@ -64,6 +64,12 @@ void QgsPackageAlgorithm::initAlgorithm( const QVariantMap & )
auto extentParam = std::make_unique<QgsProcessingParameterExtent>( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ), QVariant(), true );
extentParam->setHelp( QObject::tr( "Limit exported features to those with geometries intersecting the provided extent" ) );
addParameter( extentParam.release() );
auto crsParam = std::make_unique< QgsProcessingParameterCrs >( QStringLiteral( "CRS" ), QObject::tr( "Destination CRS" ), QVariant(), true );
crsParam->setFlags( crsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
crsParam->setHelp( QObject::tr( "If set, all layers will be transformed to the destination CRS during packaging." ) );
addParameter( std::move( crsParam ) );
addOutput( new QgsProcessingOutputMultipleLayers( QStringLiteral( "OUTPUT_LAYERS" ), QObject::tr( "Layers within new package" ) ) );
}
@ -98,6 +104,8 @@ bool QgsPackageAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsPr
feedback->reportError( QObject::tr( "No layers selected, geopackage will be empty" ), false );
}
mDestinationCrs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
return true;
}
@ -354,7 +362,7 @@ QVariantMap QgsPackageAlgorithm::processAlgorithm( const QVariantMap &parameters
feedback->pushWarning( QObject::tr( "No spatial index exists for layer %1, performance will be severely degraded" ).arg( vectorLayer->name() ) );
}
extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, layer->crs() );
extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, mDestinationCrs.isValid() ? mDestinationCrs : layer->crs() );
}
if ( !packageVectorLayer( vectorLayer, packagePath, context, &multiStepFeedback, saveStyles, saveMetadata, selectedFeaturesOnly, extent ) )
@ -440,6 +448,11 @@ bool QgsPackageAlgorithm::packageVectorLayer( QgsVectorLayer *layer, const QStri
options.saveMetadata = true;
}
if ( mDestinationCrs.isValid() )
{
options.ct = QgsCoordinateTransform( layer->crs(), mDestinationCrs, context.transformContext() );
}
// Check FID compatibility with GPKG and remove any existing FID field if not compatible,
// let this be completely recreated since many layer sources have fid fields which are
// not compatible with gpkg requirements

View File

@ -53,6 +53,7 @@ class QgsPackageAlgorithm : public QgsProcessingAlgorithm
std::vector<std::unique_ptr<QgsMapLayer>> mLayers;
QMap<QString, QString> mClonedLayerIds;
QgsCoordinateReferenceSystem mDestinationCrs;
};
///@endcond PRIVATE

View File

@ -501,7 +501,12 @@ class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
//! Scale of symbology
double symbologyScale = 1.0;
//! If not empty, only features intersecting the extent will be saved
/**
* If not empty, only features intersecting the extent will be saved.
*
* The filter extent should be in the destination CRS (if transforming), or the layer's
* CRS if no valid transform is set.
*/
QgsRectangle filterExtent;
/**

View File

@ -25,6 +25,9 @@ from qgis.core import (
QgsRelation,
QgsSettings,
QgsVectorLayer,
QgsReferencedRectangle,
QgsCoordinateReferenceSystem,
QgsRectangle,
)
import unittest
from qgis.testing import start_app, QgisTestCase
@ -110,11 +113,11 @@ class TestPackageLayers(QgisTestCase):
f = ogr.Feature(lyr.GetLayerDefn())
f["name"] = "region one"
outring = ogr.Geometry(ogr.wkbLinearRing)
outring.AddPoint(2580000, 1220500)
outring.AddPoint(2581000, 1220500)
outring.AddPoint(2581000, 1221500)
outring.AddPoint(2580000, 1221500)
outring.AddPoint(2580000, 1220500)
outring.AddPoint_2D(2580000, 1220500)
outring.AddPoint_2D(2581000, 1220500)
outring.AddPoint_2D(2581000, 1221500)
outring.AddPoint_2D(2580000, 1221500)
outring.AddPoint_2D(2580000, 1220500)
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(outring)
f.SetGeometry(polygon)
@ -122,11 +125,11 @@ class TestPackageLayers(QgisTestCase):
f = ogr.Feature(lyr.GetLayerDefn())
f["name"] = "region two"
outring = ogr.Geometry(ogr.wkbLinearRing)
outring.AddPoint(2581000, 1220000)
outring.AddPoint(2582000, 1220000)
outring.AddPoint(2582000, 1221000)
outring.AddPoint(2581000, 1221000)
outring.AddPoint(2581000, 1220000)
outring.AddPoint_2D(2581000, 1220000)
outring.AddPoint_2D(2582000, 1220000)
outring.AddPoint_2D(2582000, 1221000)
outring.AddPoint_2D(2581000, 1221000)
outring.AddPoint_2D(2581000, 1220000)
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(outring)
f.SetGeometry(polygon)
@ -139,11 +142,11 @@ class TestPackageLayers(QgisTestCase):
f["name"] = "province one"
f["region"] = 1
outring = ogr.Geometry(ogr.wkbLinearRing)
outring.AddPoint(2580000, 1220500)
outring.AddPoint(2580500, 1220500)
outring.AddPoint(2580500, 1221500)
outring.AddPoint(2580000, 1221500)
outring.AddPoint(2580000, 1220500)
outring.AddPoint_2D(2580000, 1220500)
outring.AddPoint_2D(2580500, 1220500)
outring.AddPoint_2D(2580500, 1221500)
outring.AddPoint_2D(2580000, 1221500)
outring.AddPoint_2D(2580000, 1220500)
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(outring)
f.SetGeometry(polygon)
@ -152,11 +155,11 @@ class TestPackageLayers(QgisTestCase):
f["name"] = "province two"
f["region"] = 1
outring = ogr.Geometry(ogr.wkbLinearRing)
outring.AddPoint(2580500, 1220500)
outring.AddPoint(2581000, 1220500)
outring.AddPoint(2581000, 1221500)
outring.AddPoint(2580500, 1221500)
outring.AddPoint(2580500, 1220500)
outring.AddPoint_2D(2580500, 1220500)
outring.AddPoint_2D(2581000, 1220500)
outring.AddPoint_2D(2581000, 1221500)
outring.AddPoint_2D(2580500, 1221500)
outring.AddPoint_2D(2580500, 1220500)
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(outring)
f.SetGeometry(polygon)
@ -165,11 +168,11 @@ class TestPackageLayers(QgisTestCase):
f["name"] = "province three"
f["region"] = 2
outring = ogr.Geometry(ogr.wkbLinearRing)
outring.AddPoint(2581000, 1220000)
outring.AddPoint(2582000, 1220000)
outring.AddPoint(2582000, 1220500)
outring.AddPoint(2581000, 1220500)
outring.AddPoint(2581000, 1220000)
outring.AddPoint_2D(2581000, 1220000)
outring.AddPoint_2D(2582000, 1220000)
outring.AddPoint_2D(2582000, 1220500)
outring.AddPoint_2D(2581000, 1220500)
outring.AddPoint_2D(2581000, 1220000)
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(outring)
f.SetGeometry(polygon)
@ -178,11 +181,11 @@ class TestPackageLayers(QgisTestCase):
f["name"] = "province four"
f["region"] = 2
outring = ogr.Geometry(ogr.wkbLinearRing)
outring.AddPoint(2581000, 1220500)
outring.AddPoint(2582000, 1220500)
outring.AddPoint(2582000, 1221000)
outring.AddPoint(2581000, 1221000)
outring.AddPoint(2581000, 1220500)
outring.AddPoint_2D(2581000, 1220500)
outring.AddPoint_2D(2582000, 1220500)
outring.AddPoint_2D(2582000, 1221000)
outring.AddPoint_2D(2581000, 1221000)
outring.AddPoint_2D(2581000, 1220500)
polygon = ogr.Geometry(ogr.wkbPolygon)
polygon.AddGeometry(outring)
f.SetGeometry(polygon)
@ -195,28 +198,28 @@ class TestPackageLayers(QgisTestCase):
f["name"] = "city one"
f["province"] = 1
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(2580200, 1221200)
point.AddPoint_2D(2580200, 1221200)
f.SetGeometry(point)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["name"] = "city two"
f["province"] = 1
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(2580400, 1220700)
point.AddPoint_2D(2580400, 1220700)
f.SetGeometry(point)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["name"] = "city three"
f["province"] = 2
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(2580900, 1220900)
point.AddPoint_2D(2580900, 1220900)
f.SetGeometry(point)
lyr.CreateFeature(f)
f = ogr.Feature(lyr.GetLayerDefn())
f["name"] = "city four"
f["province"] = 4
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(2581200, 1220300)
point.AddPoint_2D(2581200, 1220300)
f.SetGeometry(point)
lyr.CreateFeature(f)
@ -466,6 +469,98 @@ class TestPackageLayers(QgisTestCase):
province.selectByIds([])
city.selectByIds([])
def test_crs(self):
"""Test export with transformation"""
alg = self.registry.createAlgorithmById("qgis:package")
self.assertIsNotNone(alg)
def _test(parameters, expected_wkts):
feedback = ConsoleFeedBack()
context = QgsProcessingContext()
context.setProject(QgsProject.instance())
# Note: the following returns true also in case of errors ...
self.assertTrue(execute(alg, parameters, context, feedback))
# ... so we check the log
self.assertEqual(feedback._errors, [])
# Check export
for layer_name, wkts in expected_wkts.items():
l = QgsVectorLayer(
self.temp_export_path + f"|layername={layer_name}", layer_name
)
self.assertTrue(l.isValid())
features = {f.id(): f for f in l.getFeatures()}
self.assertCountEqual(
list(features.keys()),
list(wkts.keys()),
layer_name + str(features.keys()),
)
for id, wkt in wkts.items():
self.assertEqual(
features[id].geometry().asWkt(3), wkt, f"{layer_name}: {id}"
)
region = QgsProject.instance().mapLayersByName("region")[0]
province = QgsProject.instance().mapLayersByName("province")[0]
city = QgsProject.instance().mapLayersByName("city")[0]
parameters = {
"EXPORT_RELATED_LAYERS": False,
"LAYERS": [province, region, city],
"OUTPUT": self.temp_export_path,
"OVERWRITE": True,
"SELECTED_FEATURES_ONLY": False,
"EXTENT": None,
"CRS": "EPSG:4326",
}
# Test with no extent given
_test(
parameters,
{
"region": {
1: "Polygon ((7.175 47.135, 7.188 47.135, 7.188 47.144, 7.175 47.144, 7.175 47.135))",
2: "Polygon ((7.188 47.131, 7.201 47.131, 7.201 47.14, 7.188 47.14, 7.188 47.131))",
},
"province": {
1: "Polygon ((7.175 47.135, 7.182 47.135, 7.182 47.144, 7.175 47.144, 7.175 47.135))",
2: "Polygon ((7.182 47.135, 7.188 47.135, 7.188 47.144, 7.182 47.144, 7.182 47.135))",
3: "Polygon ((7.188 47.131, 7.201 47.131, 7.201 47.135, 7.188 47.135, 7.188 47.131))",
4: "Polygon ((7.188 47.135, 7.201 47.135, 7.201 47.14, 7.188 47.14, 7.188 47.135))",
},
"city": {
1: "Point (7.178 47.141)",
2: "Point (7.18 47.137)",
3: "Point (7.187 47.139)",
4: "Point (7.191 47.133)",
},
},
)
# Test with extent
# Test more interesting extent
parameters["EXTENT"] = QgsReferencedRectangle(
QgsRectangle(2580700, 1220000, 2581500, 1222000),
QgsCoordinateReferenceSystem("EPSG:2056"),
)
_test(
parameters,
{
"region": {
1: "Polygon ((7.175 47.135, 7.188 47.135, 7.188 47.144, 7.175 47.144, 7.175 47.135))",
2: "Polygon ((7.188 47.131, 7.201 47.131, 7.201 47.14, 7.188 47.14, 7.188 47.131))",
},
"province": {
2: "Polygon ((7.182 47.135, 7.188 47.135, 7.188 47.144, 7.182 47.144, 7.182 47.135))",
3: "Polygon ((7.188 47.131, 7.201 47.131, 7.201 47.135, 7.188 47.135, 7.188 47.131))",
4: "Polygon ((7.188 47.135, 7.201 47.135, 7.201 47.14, 7.188 47.14, 7.188 47.135))",
},
"city": {3: "Point (7.187 47.139)", 4: "Point (7.191 47.133)"},
},
)
if __name__ == "__main__":
unittest.main()