From 9db2d79244f0b5065564e5c1247864a06a033c3f Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 26 Oct 2020 15:34:21 +0100 Subject: [PATCH] fix duplication of feature being stopped at 1 level deep (#39550) --- .../auto_generated/qgsvectorlayerutils.sip.in | 7 +++++-- src/app/qgisapp.cpp | 4 ++-- src/core/qgsvectorlayerutils.cpp | 16 ++++++++++++---- src/core/qgsvectorlayerutils.h | 7 +++++-- src/gui/qgsrelationeditorwidget.cpp | 2 +- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/python/core/auto_generated/qgsvectorlayerutils.sip.in b/python/core/auto_generated/qgsvectorlayerutils.sip.in index f712c1c6fe53..0a2db8ef60b9 100644 --- a/python/core/auto_generated/qgsvectorlayerutils.sip.in +++ b/python/core/auto_generated/qgsvectorlayerutils.sip.in @@ -204,18 +204,21 @@ automatically inserted into the layer. .. versionadded:: 3.6 %End - static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext /Out/ ); + static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, QgsDuplicateFeatureContext &duplicateFeatureContext /Out/, const int maxDepth = 0 ); %Docstring Duplicates a feature and it's children (one level deep). It calls CreateFeature, so default values and constraints (e.g., unique constraints) will automatically be handled. The duplicated feature will be automatically inserted into the layer. -``depth`` the higher this number the deeper the level - With depth > 0 the children of the feature are not duplicated ``duplicateFeatureContext`` stores all the layers and the featureids of the duplicated features (incl. children) +``maxDepth`` the maximum depth to duplicate children in relations, 0 is unlimited depth (in any case, limited to 100) +``depth`` the current depth, not exposed in Python +``referencedLayersBranch`` the current branch of layers across the relations, not exposed in Python, taken by copy not reference, used to avoid infinite loop .. versionadded:: 3.0 %End + static void matchAttributesToFields( QgsFeature &feature, const QgsFields &fields ); %Docstring Matches the attributes in ``feature`` to the specified ``fields``. diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index eefa82f854ae..baacd3fd677e 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -16478,7 +16478,7 @@ QgsFeature QgisApp::duplicateFeatures( QgsMapLayer *mlayer, const QgsFeature &fe { QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicateFeatureContext; - QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), 0, duplicateFeatureContext ); + QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), duplicateFeatureContext ); featureCount += 1; const auto duplicatedFeatureContextLayers = duplicateFeatureContext.layers(); @@ -16529,7 +16529,7 @@ QgsFeature QgisApp::duplicateFeatureDigitized( QgsMapLayer *mlayer, const QgsFea QgsFeature newFeature = feature; newFeature.setGeometry( digitizedFeature.geometry() ); - QgsVectorLayerUtils::duplicateFeature( layer, newFeature, QgsProject::instance(), 0, duplicateFeatureContext ); + QgsVectorLayerUtils::duplicateFeature( layer, newFeature, QgsProject::instance(), duplicateFeatureContext ); QString childrenInfo; const auto duplicateFeatureContextLayers = duplicateFeatureContext.layers(); diff --git a/src/core/qgsvectorlayerutils.cpp b/src/core/qgsvectorlayerutils.cpp index 5e5afc457459..41787f222509 100644 --- a/src/core/qgsvectorlayerutils.cpp +++ b/src/core/qgsvectorlayerutils.cpp @@ -42,6 +42,7 @@ #include "qgsstyle.h" #include "qgsauxiliarystorage.h" + QgsFeatureIterator QgsVectorLayerUtils::getValuesIterator( const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly ) { std::unique_ptr expression; @@ -622,7 +623,7 @@ QgsFeatureList QgsVectorLayerUtils::createFeatures( const QgsVectorLayer *layer, return result; } -QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext ) +QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, QgsDuplicateFeatureContext &duplicateFeatureContext, const int maxDepth, int depth, QList referencedLayersBranch ) { if ( !layer ) return QgsFeature(); @@ -639,12 +640,16 @@ QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const Q const QList relations = project->relationManager()->referencedRelations( layer ); + const int effectiveMaxDepth = maxDepth > 0 ? maxDepth : 100; + for ( const QgsRelation &relation : relations ) { //check if composition (and not association) - if ( relation.strength() == QgsRelation::Composition && depth < 1 ) + if ( relation.strength() == QgsRelation::Composition && !referencedLayersBranch.contains( relation.referencedLayer() ) && depth < effectiveMaxDepth ) { depth++; + referencedLayersBranch << layer; + //get features connected over this relation QgsFeatureIterator relatedFeaturesIt = relation.getRelatedFeatures( feature ); QgsFeatureIds childFeatureIds; @@ -660,7 +665,7 @@ QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const Q childFeature.setAttribute( fieldPair.first, newFeature.attribute( fieldPair.second ) ); } //call the function for the child - childFeatureIds.insert( duplicateFeature( relation.referencingLayer(), childFeature, project, depth, duplicateFeatureContext ).id() ); + childFeatureIds.insert( duplicateFeature( relation.referencingLayer(), childFeature, project, duplicateFeatureContext, maxDepth, depth, referencedLayersBranch ).id() ); } //store for feedback @@ -827,7 +832,10 @@ QgsFeatureIds QgsVectorLayerUtils::QgsDuplicateFeatureContext::duplicatedFeature void QgsVectorLayerUtils::QgsDuplicateFeatureContext::setDuplicatedFeatures( QgsVectorLayer *layer, const QgsFeatureIds &ids ) { - mDuplicatedFeatures.insert( layer, ids ); + if ( mDuplicatedFeatures.contains( layer ) ) + mDuplicatedFeatures[layer] += ids; + else + mDuplicatedFeatures.insert( layer, ids ); } /* QMap QgsVectorLayerUtils::QgsDuplicateFeatureContext::duplicateFeatureContext() const diff --git a/src/core/qgsvectorlayerutils.h b/src/core/qgsvectorlayerutils.h index c78194ab16bc..43186711f835 100644 --- a/src/core/qgsvectorlayerutils.h +++ b/src/core/qgsvectorlayerutils.h @@ -205,11 +205,14 @@ class CORE_EXPORT QgsVectorLayerUtils * Duplicates a feature and it's children (one level deep). It calls CreateFeature, so * default values and constraints (e.g., unique constraints) will automatically be handled. * The duplicated feature will be automatically inserted into the layer. - * \a depth the higher this number the deeper the level - With depth > 0 the children of the feature are not duplicated * \a duplicateFeatureContext stores all the layers and the featureids of the duplicated features (incl. children) + * \a maxDepth the maximum depth to duplicate children in relations, 0 is unlimited depth (in any case, limited to 100) + * \a depth the current depth, not exposed in Python + * \a referencedLayersBranch the current branch of layers across the relations, not exposed in Python, taken by copy not reference, used to avoid infinite loop * \since QGIS 3.0 */ - static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext SIP_OUT ); + static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, QgsDuplicateFeatureContext &duplicateFeatureContext SIP_OUT, const int maxDepth = 0, int depth SIP_PYARGREMOVE = 0, QList referencedLayersBranch SIP_PYARGREMOVE = QList() ); + /** * Gets the feature source from a QgsVectorLayer pointer. diff --git a/src/gui/qgsrelationeditorwidget.cpp b/src/gui/qgsrelationeditorwidget.cpp index db1f53a82dc1..acc8743a7e52 100644 --- a/src/gui/qgsrelationeditorwidget.cpp +++ b/src/gui/qgsrelationeditorwidget.cpp @@ -637,7 +637,7 @@ void QgsRelationEditorWidget::duplicateFeature() while ( fit.nextFeature( f ) ) { QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicatedFeatureContext; - QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), 0, duplicatedFeatureContext ); + QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), duplicatedFeatureContext ); } }