Skip to content

Commit

Permalink
Add option to regenerate primary key to QgsVectorLayerUtils.makeFeatu…
Browse files Browse the repository at this point in the history
…resCompatible

Allows us to optionally reset the fid field value when required
  • Loading branch information
nyalldawson committed Oct 15, 2020
1 parent a53bb3d commit 4b3703d
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 8 deletions.
8 changes: 6 additions & 2 deletions python/core/auto_generated/qgsvectorlayerutils.sip.in
Expand Up @@ -237,7 +237,7 @@ Finally, the feature's fields are set to ``fields``.
.. versionadded:: 3.4
%End

static QgsFeatureList makeFeatureCompatible( const QgsFeature &feature, const QgsVectorLayer *layer );
static QgsFeatureList makeFeatureCompatible( const QgsFeature &feature, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() );
%Docstring
Converts input ``feature`` to be compatible with the given ``layer``.

Expand All @@ -255,10 +255,12 @@ The following operations will be performed to convert the input features:
- drop Z/M
- convert multi part geometries to single part

Optionally, ``sinkFlags`` can be specified to further refine the compatibility logic.

.. versionadded:: 3.4
%End

static QgsFeatureList makeFeaturesCompatible( const QgsFeatureList &features, const QgsVectorLayer *layer );
static QgsFeatureList makeFeaturesCompatible( const QgsFeatureList &features, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() );
%Docstring
Converts input ``features`` to be compatible with the given ``layer``.

Expand All @@ -276,6 +278,8 @@ The following operations will be performed to convert the input features:
- drop Z/M
- convert multi part geometries to single part

Optionally, ``sinkFlags`` can be specified to further refine the compatibility logic.

.. versionadded:: 3.4
%End

Expand Down
15 changes: 12 additions & 3 deletions src/core/qgsvectorlayerutils.cpp
Expand Up @@ -735,13 +735,22 @@ void QgsVectorLayerUtils::matchAttributesToFields( QgsFeature &feature, const Qg
feature.setFields( fields );
}

QgsFeatureList QgsVectorLayerUtils::makeFeatureCompatible( const QgsFeature &feature, const QgsVectorLayer *layer )
QgsFeatureList QgsVectorLayerUtils::makeFeatureCompatible( const QgsFeature &feature, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags )
{
QgsWkbTypes::Type inputWkbType( layer->wkbType( ) );
QgsFeatureList resultFeatures;
QgsFeature newF( feature );
// Fix attributes
QgsVectorLayerUtils::matchAttributesToFields( newF, layer->fields( ) );

if ( sinkFlags & QgsFeatureSink::RegeneratePrimaryKey && layer->storageType() == QLatin1String( "GPKG" ) )
{
// drop incoming fid value, let it be regenerated
const int fidIndex = layer->fields().lookupField( QStringLiteral( "fid" ) );
if ( fidIndex >= 0 )
newF.setAttribute( fidIndex, QVariant() );
}

// Does geometry need transformations?
QgsWkbTypes::GeometryType newFGeomType( QgsWkbTypes::geometryType( newF.geometry().wkbType() ) );
bool newFHasGeom = newFGeomType !=
Expand Down Expand Up @@ -785,12 +794,12 @@ QgsFeatureList QgsVectorLayerUtils::makeFeatureCompatible( const QgsFeature &fea
return resultFeatures;
}

QgsFeatureList QgsVectorLayerUtils::makeFeaturesCompatible( const QgsFeatureList &features, const QgsVectorLayer *layer )
QgsFeatureList QgsVectorLayerUtils::makeFeaturesCompatible( const QgsFeatureList &features, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags )
{
QgsFeatureList resultFeatures;
for ( const QgsFeature &f : features )
{
const QgsFeatureList features( makeFeatureCompatible( f, layer ) );
const QgsFeatureList features( makeFeatureCompatible( f, layer, sinkFlags ) );
for ( const auto &_f : features )
{
resultFeatures.append( _f );
Expand Down
9 changes: 7 additions & 2 deletions src/core/qgsvectorlayerutils.h
Expand Up @@ -20,6 +20,7 @@
#include "qgsgeometry.h"
#include "qgsvectorlayerfeatureiterator.h"
#include "qgssymbollayerreference.h"
#include "qgsfeaturesink.h"

class QgsFeatureRenderer;
class QgsSymbolLayer;
Expand Down Expand Up @@ -264,9 +265,11 @@ class CORE_EXPORT QgsVectorLayerUtils
* - drop Z/M
* - convert multi part geometries to single part
*
* Optionally, \a sinkFlags can be specified to further refine the compatibility logic.
*
* \since QGIS 3.4
*/
static QgsFeatureList makeFeatureCompatible( const QgsFeature &feature, const QgsVectorLayer *layer );
static QgsFeatureList makeFeatureCompatible( const QgsFeature &feature, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() );

/**
* Converts input \a features to be compatible with the given \a layer.
Expand All @@ -285,9 +288,11 @@ class CORE_EXPORT QgsVectorLayerUtils
* - drop Z/M
* - convert multi part geometries to single part
*
* Optionally, \a sinkFlags can be specified to further refine the compatibility logic.
*
* \since QGIS 3.4
*/
static QgsFeatureList makeFeaturesCompatible( const QgsFeatureList &features, const QgsVectorLayer *layer );
static QgsFeatureList makeFeaturesCompatible( const QgsFeatureList &features, const QgsVectorLayer *layer, QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags() );

/**
* \return true if the \param feature field at index \param fieldIndex from \param layer
Expand Down
23 changes: 22 additions & 1 deletion tests/src/python/test_qgsprocessinginplace.py
Expand Up @@ -28,7 +28,8 @@
QgsCoordinateReferenceSystem,
QgsProject,
QgsProcessingException,
QgsVectorLayer
QgsVectorLayer,
QgsFeatureSink
)
from processing.core.Processing import Processing
from processing.core.ProcessingConfig import ProcessingConfig
Expand Down Expand Up @@ -912,6 +913,26 @@ def test_unique_constraints(self):

self.assertTrue(gpkg_layer.commitChanges())

def test_regenerate_fid(self):
"""Test RegeneratePrimaryKey flag"""
temp_dir = QTemporaryDir()
temp_path = temp_dir.path()
gpkg_name = 'bug_31634_Multi_to_Singleparts_FID.gpkg'
gpkg_path = os.path.join(temp_path, gpkg_name)
shutil.copyfile(os.path.join(unitTestDataPath(), gpkg_name), gpkg_path)

gpkg_layer = QgsVectorLayer(gpkg_path + '|layername=Multi_to_Singleparts_FID_bug', 'lyr', 'ogr')
self.assertTrue(gpkg_layer.isValid())

f = next(gpkg_layer.getFeatures())
self.assertEqual(f['fid'], 1)
res = QgsVectorLayerUtils.makeFeatureCompatible(f, gpkg_layer)
self.assertEqual([ff['fid'] for ff in res], [1])

# if RegeneratePrimaryKey set then we should discard fid field
res = QgsVectorLayerUtils.makeFeatureCompatible(f, gpkg_layer, QgsFeatureSink.RegeneratePrimaryKey)
self.assertEqual([ff['fid'] for ff in res], [None])


if __name__ == '__main__':
unittest.main()

0 comments on commit 4b3703d

Please sign in to comment.