mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-19 00:02:48 -04:00
[FEATURE] Duplicate features including children (one level deep) when relation strength is competition - configuration for the relationsstrength in the relation GUI [needs-docs]
This commit is contained in:
parent
6297d19b70
commit
f7073d0587
@ -19,6 +19,13 @@ class QgsRelation
|
|||||||
%End
|
%End
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum RelationStrength
|
||||||
|
{
|
||||||
|
Association,
|
||||||
|
Composition
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
QgsRelation();
|
QgsRelation();
|
||||||
%Docstring
|
%Docstring
|
||||||
@ -53,6 +60,12 @@ class QgsRelation
|
|||||||
Set a name for this relation
|
Set a name for this relation
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
void setStrength( const RelationStrength &strength );
|
||||||
|
%Docstring
|
||||||
|
Set a strength for this relation
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
%End
|
||||||
|
|
||||||
void setReferencingLayer( const QString &id );
|
void setReferencingLayer( const QString &id );
|
||||||
%Docstring
|
%Docstring
|
||||||
Set the referencing (child) layer id. This layer will be searched in the registry.
|
Set the referencing (child) layer id. This layer will be searched in the registry.
|
||||||
@ -158,6 +171,15 @@ class QgsRelation
|
|||||||
:rtype: str
|
:rtype: str
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
RelationStrength strength() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the relation strength as a string
|
||||||
|
|
||||||
|
:return: strength
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
:rtype: RelationStrength
|
||||||
|
%End
|
||||||
|
|
||||||
QString id() const;
|
QString id() const;
|
||||||
%Docstring
|
%Docstring
|
||||||
A (project-wide) unique id for this relation
|
A (project-wide) unique id for this relation
|
||||||
|
@ -22,6 +22,38 @@ class QgsVectorLayerUtils
|
|||||||
%End
|
%End
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
class QgsDuplicateFeatureContext
|
||||||
|
{
|
||||||
|
%Docstring
|
||||||
|
Contains mainly the QMap with QgsVectorLayer and QgsFeatureIds do list all the duplicated features
|
||||||
|
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsvectorlayerutils.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
|
||||||
|
QgsDuplicateFeatureContext();
|
||||||
|
|
||||||
|
QList<QgsVectorLayer *> layers() const;
|
||||||
|
%Docstring
|
||||||
|
Returns all the layers in the member QMap mDuplicatedFeatures
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
:rtype: list of QgsVectorLayer
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsFeatureIds duplicatedFeatures( QgsVectorLayer *layer ) const;
|
||||||
|
%Docstring
|
||||||
|
Returns the duplicated features in the given layer
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
:rtype: QgsFeatureIds
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
static bool valueExists( const QgsVectorLayer *layer, int fieldIndex, const QVariant &value, const QgsFeatureIds &ignoreIds = QgsFeatureIds() );
|
static bool valueExists( const QgsVectorLayer *layer, int fieldIndex, const QVariant &value, const QgsFeatureIds &ignoreIds = QgsFeatureIds() );
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns true if the specified value already exists within a field. This method can be used to test for uniqueness
|
Returns true if the specified value already exists within a field. This method can be used to test for uniqueness
|
||||||
@ -62,8 +94,18 @@ class QgsVectorLayerUtils
|
|||||||
:rtype: QgsFeature
|
:rtype: QgsFeature
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext /Out/ );
|
||||||
|
%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.
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
:rtype: QgsFeature
|
||||||
|
%End
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
* This file has been generated automatically from *
|
* This file has been generated automatically from *
|
||||||
* *
|
* *
|
||||||
|
@ -43,6 +43,17 @@ void QgsDiscoverRelationsDlg::addRelation( const QgsRelation &rel )
|
|||||||
mRelationsTable->setItem( row, 2, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencingField() ) );
|
mRelationsTable->setItem( row, 2, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencingField() ) );
|
||||||
mRelationsTable->setItem( row, 3, new QTableWidgetItem( rel.referencedLayer()->name() ) );
|
mRelationsTable->setItem( row, 3, new QTableWidgetItem( rel.referencedLayer()->name() ) );
|
||||||
mRelationsTable->setItem( row, 4, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencedField() ) );
|
mRelationsTable->setItem( row, 4, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencedField() ) );
|
||||||
|
if ( rel.strength() == QgsRelation::RelationStrength::Composition )
|
||||||
|
{
|
||||||
|
mRelationsTable->setItem( row, 5, new QTableWidgetItem( QStringLiteral( "Composition" ) ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRelationsTable->setItem( row, 5, new QTableWidgetItem( QStringLiteral( "Association" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
mRelationsTable->item( row, 5 )->setToolTip( QStringLiteral( "Composition (child features will be copied too) or Association" ) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QgsRelation> QgsDiscoverRelationsDlg::relations() const
|
QList<QgsRelation> QgsDiscoverRelationsDlg::relations() const
|
||||||
|
@ -33,6 +33,10 @@ QgsRelationAddDlg::QgsRelationAddDlg( QWidget *parent )
|
|||||||
mCbxReferencedLayer->setFilters( QgsMapLayerProxyModel::VectorLayer );
|
mCbxReferencedLayer->setFilters( QgsMapLayerProxyModel::VectorLayer );
|
||||||
mCbxReferencedField->setLayer( mCbxReferencedLayer->currentLayer() );
|
mCbxReferencedField->setLayer( mCbxReferencedLayer->currentLayer() );
|
||||||
|
|
||||||
|
mCbxRelationStrength->addItem( "Association", QVariant::fromValue( QgsRelation::RelationStrength::Association ) );
|
||||||
|
mCbxRelationStrength->addItem( "Composition", QVariant::fromValue( QgsRelation::RelationStrength::Composition ) );
|
||||||
|
mCbxRelationStrength->setToolTip( QStringLiteral( "Composition (child features will be copied too) or Association" ) );
|
||||||
|
|
||||||
mTxtRelationId->setPlaceholderText( tr( "[Generated automatically]" ) );
|
mTxtRelationId->setPlaceholderText( tr( "[Generated automatically]" ) );
|
||||||
checkDefinitionValid();
|
checkDefinitionValid();
|
||||||
|
|
||||||
@ -74,6 +78,10 @@ QString QgsRelationAddDlg::relationName()
|
|||||||
return mTxtRelationName->text();
|
return mTxtRelationName->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsRelation::RelationStrength QgsRelationAddDlg::relationStrength()
|
||||||
|
{
|
||||||
|
return mCbxRelationStrength->currentData().value<QgsRelation::RelationStrength>();
|
||||||
|
}
|
||||||
|
|
||||||
void QgsRelationAddDlg::checkDefinitionValid()
|
void QgsRelationAddDlg::checkDefinitionValid()
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "ui_qgsrelationadddlgbase.h"
|
#include "ui_qgsrelationadddlgbase.h"
|
||||||
#include "qgis_app.h"
|
#include "qgis_app.h"
|
||||||
|
#include "qgsrelation.h"
|
||||||
|
|
||||||
class QgsVectorLayer;
|
class QgsVectorLayer;
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ class APP_EXPORT QgsRelationAddDlg : public QDialog, private Ui::QgsRelationAddD
|
|||||||
QList< QPair< QString, QString > > references();
|
QList< QPair< QString, QString > > references();
|
||||||
QString relationId();
|
QString relationId();
|
||||||
QString relationName();
|
QString relationName();
|
||||||
|
QgsRelation::RelationStrength relationStrength();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
|
@ -77,6 +77,19 @@ void QgsRelationManagerDialog::addRelation( const QgsRelation &rel )
|
|||||||
item = new QTableWidgetItem( rel.id() );
|
item = new QTableWidgetItem( rel.id() );
|
||||||
item->setFlags( Qt::ItemIsEditable );
|
item->setFlags( Qt::ItemIsEditable );
|
||||||
mRelationsTable->setItem( row, 5, item );
|
mRelationsTable->setItem( row, 5, item );
|
||||||
|
|
||||||
|
|
||||||
|
if ( rel.strength() == QgsRelation::RelationStrength::Composition )
|
||||||
|
{
|
||||||
|
item = new QTableWidgetItem( QStringLiteral( "Composition" ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = new QTableWidgetItem( QStringLiteral( "Association" ) );
|
||||||
|
}
|
||||||
|
item->setFlags( Qt::ItemIsEditable );
|
||||||
|
mRelationsTable->setItem( row, 6, item );
|
||||||
|
|
||||||
mRelationsTable->setSortingEnabled( true );
|
mRelationsTable->setSortingEnabled( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +129,7 @@ void QgsRelationManagerDialog::mBtnAddRelation_clicked()
|
|||||||
relation.setId( relationId );
|
relation.setId( relationId );
|
||||||
relation.addFieldPair( addDlg.references().at( 0 ).first, addDlg.references().at( 0 ).second );
|
relation.addFieldPair( addDlg.references().at( 0 ).first, addDlg.references().at( 0 ).second );
|
||||||
relation.setName( addDlg.relationName() );
|
relation.setName( addDlg.relationName() );
|
||||||
|
relation.setStrength( addDlg.relationStrength() );
|
||||||
|
|
||||||
addRelation( relation );
|
addRelation( relation );
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ QgsRelation QgsRelation::createFromXml( const QDomNode &node )
|
|||||||
QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
|
QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
|
||||||
QString id = elem.attribute( QStringLiteral( "id" ) );
|
QString id = elem.attribute( QStringLiteral( "id" ) );
|
||||||
QString name = elem.attribute( QStringLiteral( "name" ) );
|
QString name = elem.attribute( QStringLiteral( "name" ) );
|
||||||
|
QString strength = elem.attribute( QStringLiteral( "strength" ) );
|
||||||
|
|
||||||
const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
|
const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
|
||||||
|
|
||||||
@ -66,6 +67,14 @@ QgsRelation QgsRelation::createFromXml( const QDomNode &node )
|
|||||||
relation.mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
|
relation.mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
|
||||||
relation.mRelationId = id;
|
relation.mRelationId = id;
|
||||||
relation.mRelationName = name;
|
relation.mRelationName = name;
|
||||||
|
if ( strength == "Composition" )
|
||||||
|
{
|
||||||
|
relation.mRelationStrength = RelationStrength::Composition;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
relation.mRelationStrength = RelationStrength::Association;
|
||||||
|
}
|
||||||
|
|
||||||
QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
|
QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
|
||||||
for ( int i = 0; i < references.size(); ++i )
|
for ( int i = 0; i < references.size(); ++i )
|
||||||
@ -90,6 +99,14 @@ void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
|
|||||||
elem.setAttribute( QStringLiteral( "name" ), mRelationName );
|
elem.setAttribute( QStringLiteral( "name" ), mRelationName );
|
||||||
elem.setAttribute( QStringLiteral( "referencingLayer" ), mReferencingLayerId );
|
elem.setAttribute( QStringLiteral( "referencingLayer" ), mReferencingLayerId );
|
||||||
elem.setAttribute( QStringLiteral( "referencedLayer" ), mReferencedLayerId );
|
elem.setAttribute( QStringLiteral( "referencedLayer" ), mReferencedLayerId );
|
||||||
|
if ( mRelationStrength == RelationStrength::Composition )
|
||||||
|
{
|
||||||
|
elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Composition" ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Association" ) );
|
||||||
|
}
|
||||||
|
|
||||||
Q_FOREACH ( const FieldPair &fields, mFieldPairs )
|
Q_FOREACH ( const FieldPair &fields, mFieldPairs )
|
||||||
{
|
{
|
||||||
@ -114,6 +131,12 @@ void QgsRelation::setName( const QString &name )
|
|||||||
mRelationName = name;
|
mRelationName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QgsRelation::setStrength( const RelationStrength &strength )
|
||||||
|
{
|
||||||
|
mRelationStrength = strength;
|
||||||
|
}
|
||||||
|
|
||||||
void QgsRelation::setReferencingLayer( const QString &id )
|
void QgsRelation::setReferencingLayer( const QString &id )
|
||||||
{
|
{
|
||||||
mReferencingLayerId = id;
|
mReferencingLayerId = id;
|
||||||
@ -206,6 +229,11 @@ QString QgsRelation::name() const
|
|||||||
return mRelationName;
|
return mRelationName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsRelation::RelationStrength QgsRelation::strength() const
|
||||||
|
{
|
||||||
|
return mRelationStrength;
|
||||||
|
}
|
||||||
|
|
||||||
QString QgsRelation::id() const
|
QString QgsRelation::id() const
|
||||||
{
|
{
|
||||||
return mRelationId;
|
return mRelationId;
|
||||||
|
@ -48,6 +48,17 @@ class CORE_EXPORT QgsRelation
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum for the relation strength
|
||||||
|
* Association, Composition
|
||||||
|
*/
|
||||||
|
enum RelationStrength
|
||||||
|
{
|
||||||
|
Association, //!< Loose relation, related elements are not part of the parent and a parent copy will not copy any children.
|
||||||
|
Composition //!< Fix relation, related elements are part of the parent and a parent copy will copy any children or delete of parent will delete children
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
#ifndef SIP_RUN
|
#ifndef SIP_RUN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,6 +120,12 @@ class CORE_EXPORT QgsRelation
|
|||||||
*/
|
*/
|
||||||
void setName( const QString &name );
|
void setName( const QString &name );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a strength for this relation
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
void setStrength( const RelationStrength &strength );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the referencing (child) layer id. This layer will be searched in the registry.
|
* Set the referencing (child) layer id. This layer will be searched in the registry.
|
||||||
*/
|
*/
|
||||||
@ -214,6 +231,14 @@ class CORE_EXPORT QgsRelation
|
|||||||
*/
|
*/
|
||||||
QString name() const;
|
QString name() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the relation strength as a string
|
||||||
|
*
|
||||||
|
* \returns strength
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
RelationStrength strength() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A (project-wide) unique id for this relation
|
* A (project-wide) unique id for this relation
|
||||||
*
|
*
|
||||||
@ -345,6 +370,8 @@ class CORE_EXPORT QgsRelation
|
|||||||
//! The parent layer
|
//! The parent layer
|
||||||
QgsVectorLayer *mReferencedLayer = nullptr;
|
QgsVectorLayer *mReferencedLayer = nullptr;
|
||||||
|
|
||||||
|
RelationStrength mRelationStrength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of fields which define the relation.
|
* A list of fields which define the relation.
|
||||||
* In most cases there will be only one value, but multiple values
|
* In most cases there will be only one value, but multiple values
|
||||||
@ -357,5 +384,6 @@ class CORE_EXPORT QgsRelation
|
|||||||
|
|
||||||
// Register QgsRelation for usage with QVariant
|
// Register QgsRelation for usage with QVariant
|
||||||
Q_DECLARE_METATYPE( QgsRelation )
|
Q_DECLARE_METATYPE( QgsRelation )
|
||||||
|
Q_DECLARE_METATYPE( QgsRelation::RelationStrength )
|
||||||
|
|
||||||
#endif // QGSRELATION_H
|
#endif // QGSRELATION_H
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
#include "qgsvectorlayerutils.h"
|
#include "qgsvectorlayerutils.h"
|
||||||
#include "qgsvectordataprovider.h"
|
#include "qgsvectordataprovider.h"
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include "qgsproject.h"
|
||||||
|
#include "qgsrelationmanager.h"
|
||||||
|
#include "qgslogger.h"
|
||||||
|
|
||||||
bool QgsVectorLayerUtils::valueExists( const QgsVectorLayer *layer, int fieldIndex, const QVariant &value, const QgsFeatureIds &ignoreIds )
|
bool QgsVectorLayerUtils::valueExists( const QgsVectorLayer *layer, int fieldIndex, const QVariant &value, const QgsFeatureIds &ignoreIds )
|
||||||
{
|
{
|
||||||
@ -320,3 +323,87 @@ QgsFeature QgsVectorLayerUtils::createFeature( QgsVectorLayer *layer, const QgsG
|
|||||||
return newFeature;
|
return newFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext )
|
||||||
|
{
|
||||||
|
if ( !layer )
|
||||||
|
return QgsFeature();
|
||||||
|
|
||||||
|
if ( !layer->isEditable() )
|
||||||
|
return QgsFeature();
|
||||||
|
|
||||||
|
//get context from layer
|
||||||
|
QgsExpressionContext context = layer->createExpressionContext();
|
||||||
|
context.setFeature( feature );
|
||||||
|
|
||||||
|
//create the attribute map
|
||||||
|
QgsAttributes srcAttr = feature.attributes();
|
||||||
|
QgsAttributeMap dstAttr;
|
||||||
|
for ( int src = 0; src < srcAttr.count(); ++src )
|
||||||
|
{
|
||||||
|
dstAttr[ src ] = srcAttr.at( src );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFeature newFeature = createFeature( layer, feature.geometry(), dstAttr, &context );
|
||||||
|
|
||||||
|
const QList<QgsRelation> relations = project->relationManager()->referencedRelations( layer );
|
||||||
|
|
||||||
|
for ( const QgsRelation &relation : relations )
|
||||||
|
{
|
||||||
|
//check if composition (and not association)
|
||||||
|
if ( relation.strength() == QgsRelation::Composition && depth < 1 )
|
||||||
|
{
|
||||||
|
depth++;
|
||||||
|
//get features connected over this relation
|
||||||
|
QgsFeatureIterator relatedFeaturesIt = relation.getRelatedFeatures( feature );
|
||||||
|
QgsFeatureIds childFeatureIds;
|
||||||
|
QgsFeature childFeature;
|
||||||
|
while ( relatedFeaturesIt.nextFeature( childFeature ) )
|
||||||
|
{
|
||||||
|
//set childlayer editable
|
||||||
|
relation.referencingLayer()->startEditing();
|
||||||
|
//change the fk of the child to the id of the new parent
|
||||||
|
for ( const QgsRelation::FieldPair &fieldPair : relation.fieldPairs() )
|
||||||
|
{
|
||||||
|
childFeature.setAttribute( fieldPair.first, newFeature.attribute( fieldPair.second ) );
|
||||||
|
}
|
||||||
|
//call the function for the child
|
||||||
|
duplicateFeature( relation.referencingLayer(), childFeature, project, depth, duplicateFeatureContext );
|
||||||
|
|
||||||
|
//add the new feature id for feedback
|
||||||
|
childFeatureIds.insert( childFeature.id() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//store for feedback
|
||||||
|
duplicateFeatureContext.setDuplicatedFeatures( relation.referencingLayer(), childFeatureIds );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layer->addFeature( newFeature );
|
||||||
|
|
||||||
|
return newFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QgsVectorLayer *> QgsVectorLayerUtils::QgsDuplicateFeatureContext::layers() const
|
||||||
|
{
|
||||||
|
QList<QgsVectorLayer *> layers;
|
||||||
|
QMap<QgsVectorLayer *, QgsFeatureIds>::const_iterator i;
|
||||||
|
for ( i = mDuplicatedFeatures.begin(); i != mDuplicatedFeatures.end(); ++i )
|
||||||
|
layers.append( i.key() );
|
||||||
|
return layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFeatureIds QgsVectorLayerUtils::QgsDuplicateFeatureContext::duplicatedFeatures( QgsVectorLayer *layer ) const
|
||||||
|
{
|
||||||
|
return mDuplicatedFeatures[layer];
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsVectorLayerUtils::QgsDuplicateFeatureContext::setDuplicatedFeatures( QgsVectorLayer *layer, QgsFeatureIds ids )
|
||||||
|
{
|
||||||
|
mDuplicatedFeatures.insert( layer, ids );
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
QMap<QgsVectorLayer *, QgsFeatureIds> QgsVectorLayerUtils::QgsDuplicateFeatureContext::duplicateFeatureContext() const
|
||||||
|
{
|
||||||
|
return mDuplicatedFeatures;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
@ -32,6 +32,43 @@ class CORE_EXPORT QgsVectorLayerUtils
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
* \class QgsDuplicateFeatureContext
|
||||||
|
* \brief Contains mainly the QMap with QgsVectorLayer and QgsFeatureIds do list all the duplicated features
|
||||||
|
*
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
class QgsDuplicateFeatureContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
QgsDuplicateFeatureContext() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the layers in the member QMap mDuplicatedFeatures
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
QList<QgsVectorLayer *> layers() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the duplicated features in the given layer
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
QgsFeatureIds duplicatedFeatures( QgsVectorLayer *layer ) const;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QgsVectorLayer *, QgsFeatureIds> mDuplicatedFeatures;
|
||||||
|
friend class QgsVectorLayerUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To set an entry to the member QMap mDuplicatedFeatures
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
void setDuplicatedFeatures( QgsVectorLayer *layer, QgsFeatureIds ids );
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the specified value already exists within a field. This method can be used to test for uniqueness
|
* Returns true if the specified value already exists within a field. This method can be used to test for uniqueness
|
||||||
* of values inside a layer's attributes. An optional list of ignored feature IDs can be provided, if so, any features
|
* of values inside a layer's attributes. An optional list of ignored feature IDs can be provided, if so, any features
|
||||||
@ -68,6 +105,15 @@ class CORE_EXPORT QgsVectorLayerUtils
|
|||||||
const QgsAttributeMap &attributes = QgsAttributeMap(),
|
const QgsAttributeMap &attributes = QgsAttributeMap(),
|
||||||
QgsExpressionContext *context = nullptr );
|
QgsExpressionContext *context = nullptr );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext SIP_OUT );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // QGSVECTORLAYERUTILS_H
|
#endif // QGSVECTORLAYERUTILS_H
|
||||||
|
@ -62,6 +62,11 @@
|
|||||||
<string>Referenced Field</string>
|
<string>Referenced Field</string>
|
||||||
</property>
|
</property>
|
||||||
</column>
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Strength</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -7,31 +7,60 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>581</width>
|
<width>581</width>
|
||||||
<height>267</height>
|
<height>342</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Add Relation</string>
|
<string>Add Relation</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="1" column="0">
|
<item row="1" column="2">
|
||||||
|
<widget class="QgsMapLayerComboBox" name="mCbxReferencedLayer"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Referenced Field</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="2">
|
||||||
|
<widget class="QgsFieldComboBox" name="mCbxReferencingField"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QgsFieldComboBox" name="mCbxReferencedField"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Referencing Layer (Child)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QgsMapLayerComboBox" name="mCbxReferencingLayer"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_7">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Name</string>
|
<string>Name</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1" colspan="2">
|
<item row="0" column="2">
|
||||||
<widget class="QgsMapLayerComboBox" name="mCbxReferencingLayer"/>
|
<widget class="QLineEdit" name="mTxtRelationName"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0">
|
<item row="1" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Id</string>
|
<string>Referenced Layer (Parent)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="15" column="0" colspan="3">
|
<item row="5" column="2" rowspan="2">
|
||||||
|
<widget class="QLineEdit" name="mTxtRelationId"/>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="2">
|
||||||
<widget class="QDialogButtonBox" name="mButtonBox">
|
<widget class="QDialogButtonBox" name="mButtonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
@ -41,48 +70,29 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="4" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Referencing Layer (Child)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1" colspan="2">
|
|
||||||
<widget class="QgsFieldComboBox" name="mCbxReferencingField"/>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Referencing Field</string>
|
<string>Referencing Field</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="7" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Referenced Layer (Parent)</string>
|
<string>Relationship Strength</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="5" column="0" rowspan="2" colspan="2">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Referenced Field</string>
|
<string>Id</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1" colspan="2">
|
<item row="7" column="2">
|
||||||
<widget class="QgsFieldComboBox" name="mCbxReferencedField"/>
|
<widget class="QComboBox" name="mCbxRelationStrength"/>
|
||||||
</item>
|
|
||||||
<item row="9" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="mTxtRelationId"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1" colspan="2">
|
|
||||||
<widget class="QgsMapLayerComboBox" name="mCbxReferencedLayer"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="mTxtRelationName"/>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -43,21 +43,26 @@
|
|||||||
<string>Referencing Field</string>
|
<string>Referencing Field</string>
|
||||||
</property>
|
</property>
|
||||||
</column>
|
</column>
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>Referenced Layer</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<column>
|
<column>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Referenced Field</string>
|
<string>Referenced Field</string>
|
||||||
</property>
|
</property>
|
||||||
</column>
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Referenced Layer</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
<column>
|
<column>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Id</string>
|
<string>Id</string>
|
||||||
</property>
|
</property>
|
||||||
</column>
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Strength</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -14,13 +14,16 @@ __revision__ = '$Format:%H$'
|
|||||||
|
|
||||||
import qgis # NOQA
|
import qgis # NOQA
|
||||||
|
|
||||||
from qgis.core import (QgsVectorLayer,
|
from qgis.core import (QgsProject,
|
||||||
|
QgsVectorLayer,
|
||||||
QgsVectorLayerUtils,
|
QgsVectorLayerUtils,
|
||||||
QgsFieldConstraints,
|
QgsFieldConstraints,
|
||||||
QgsFeature,
|
QgsFeature,
|
||||||
|
QgsFeatureIterator,
|
||||||
QgsGeometry,
|
QgsGeometry,
|
||||||
QgsPointXY,
|
QgsPointXY,
|
||||||
QgsDefaultValue,
|
QgsDefaultValue,
|
||||||
|
QgsRelation,
|
||||||
NULL
|
NULL
|
||||||
)
|
)
|
||||||
from qgis.testing import start_app, unittest
|
from qgis.testing import start_app, unittest
|
||||||
@ -267,6 +270,159 @@ class TestQgsVectorLayerUtils(unittest.TestCase):
|
|||||||
f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
|
f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
|
||||||
self.assertEqual(f.attributes(), ['test_4', 128, NULL])
|
self.assertEqual(f.attributes(), ['test_4', 128, NULL])
|
||||||
|
|
||||||
|
def testDuplicateFeature(self):
|
||||||
|
""" test duplicating a feature """
|
||||||
|
|
||||||
|
project = QgsProject().instance()
|
||||||
|
|
||||||
|
# LAYERS
|
||||||
|
# - add first layer (parent)
|
||||||
|
layer1 = QgsVectorLayer("Point?field=fldtxt:string&field=pkid:integer",
|
||||||
|
"parentlayer", "memory")
|
||||||
|
# > check first layer (parent)
|
||||||
|
self.assertTrue(layer1.isValid())
|
||||||
|
# - set the value for the copy
|
||||||
|
layer1.setDefaultValueDefinition(1, QgsDefaultValue("rand(1000,2000)"))
|
||||||
|
# > check first layer (parent)
|
||||||
|
self.assertTrue(layer1.isValid())
|
||||||
|
# - add second layer (child)
|
||||||
|
layer2 = QgsVectorLayer("Point?field=fldtxt:string&field=id:integer&field=foreign_key:integer",
|
||||||
|
"childlayer", "memory")
|
||||||
|
# > check second layer (child)
|
||||||
|
self.assertTrue(layer2.isValid())
|
||||||
|
# - add layers
|
||||||
|
project.addMapLayers([layer1, layer2])
|
||||||
|
|
||||||
|
# FEATURES
|
||||||
|
# - add 2 features on layer1 (parent)
|
||||||
|
l1f1orig = QgsFeature()
|
||||||
|
l1f1orig.setFields(layer1.fields())
|
||||||
|
l1f1orig.setAttributes(["F_l1f1", 100])
|
||||||
|
l1f2orig = QgsFeature()
|
||||||
|
l1f2orig.setFields(layer1.fields())
|
||||||
|
l1f2orig.setAttributes(["F_l1f2", 101])
|
||||||
|
# > check by adding features
|
||||||
|
self.assertTrue(layer1.dataProvider().addFeatures([l1f1orig, l1f2orig]))
|
||||||
|
# add 4 features on layer2 (child)
|
||||||
|
l2f1orig = QgsFeature()
|
||||||
|
l2f1orig.setFields(layer2.fields())
|
||||||
|
l2f1orig.setAttributes(["F_l2f1", 201, 100])
|
||||||
|
l2f2orig = QgsFeature()
|
||||||
|
l2f2orig.setFields(layer2.fields())
|
||||||
|
l2f2orig.setAttributes(["F_l2f2", 202, 100])
|
||||||
|
l2f3orig = QgsFeature()
|
||||||
|
l2f3orig.setFields(layer2.fields())
|
||||||
|
l2f3orig.setAttributes(["F_l2f3", 203, 100])
|
||||||
|
l2f4orig = QgsFeature()
|
||||||
|
l2f4orig.setFields(layer2.fields())
|
||||||
|
l2f4orig.setAttributes(["F_l2f4", 204, 101])
|
||||||
|
# > check by adding features
|
||||||
|
self.assertTrue(layer2.dataProvider().addFeatures([l2f1orig, l2f2orig, l2f3orig, l2f4orig]))
|
||||||
|
|
||||||
|
# RELATION
|
||||||
|
# - create the relationmanager
|
||||||
|
relMgr = project.relationManager()
|
||||||
|
# - create the relation
|
||||||
|
rel = QgsRelation()
|
||||||
|
rel.setId('rel1')
|
||||||
|
rel.setName('childrel')
|
||||||
|
rel.setReferencingLayer(layer2.id())
|
||||||
|
rel.setReferencedLayer(layer1.id())
|
||||||
|
rel.addFieldPair('foreign_key', 'pkid')
|
||||||
|
rel.setStrength(QgsRelation.Composition)
|
||||||
|
# > check relation
|
||||||
|
self.assertTrue(rel.isValid())
|
||||||
|
# - add relation
|
||||||
|
relMgr.addRelation(rel)
|
||||||
|
# > check if referencedLayer is layer1
|
||||||
|
self.assertEqual(rel.referencedLayer(), layer1)
|
||||||
|
# > check if referencingLayer is layer2
|
||||||
|
self.assertEqual(rel.referencingLayer(), layer2)
|
||||||
|
# > check if the layers are correct in relation when loading from relationManager
|
||||||
|
relations = project.relationManager().relations()
|
||||||
|
relation = relations[list(relations.keys())[0]]
|
||||||
|
# > check if referencedLayer is layer1
|
||||||
|
self.assertEqual(relation.referencedLayer(), layer1)
|
||||||
|
# > check if referencingLayer is layer2
|
||||||
|
self.assertEqual(relation.referencingLayer(), layer2)
|
||||||
|
# > check the relatedfeatures
|
||||||
|
|
||||||
|
'''
|
||||||
|
# testoutput 1
|
||||||
|
print( "\nAll Features and relations")
|
||||||
|
featit=layer1.getFeatures()
|
||||||
|
f=QgsFeature()
|
||||||
|
while featit.nextFeature(f):
|
||||||
|
print( f.attributes())
|
||||||
|
childFeature = QgsFeature()
|
||||||
|
relfeatit=rel.getRelatedFeatures(f)
|
||||||
|
while relfeatit.nextFeature(childFeature):
|
||||||
|
print( childFeature.attributes() )
|
||||||
|
print( "\n--------------------------")
|
||||||
|
|
||||||
|
print( "\nFeatures on layer1")
|
||||||
|
for f in layer1.getFeatures():
|
||||||
|
print( f.attributes() )
|
||||||
|
|
||||||
|
print( "\nFeatures on layer2")
|
||||||
|
for f in layer2.getFeatures():
|
||||||
|
print( f.attributes() )
|
||||||
|
'''
|
||||||
|
|
||||||
|
# DUPLICATION
|
||||||
|
# - duplicate feature l1f1orig with children
|
||||||
|
layer1.startEditing()
|
||||||
|
results = QgsVectorLayerUtils.duplicateFeature(layer1, l1f1orig, project, 0)
|
||||||
|
|
||||||
|
# > check if name is name of duplicated (pk is different)
|
||||||
|
result_feature = results[0]
|
||||||
|
self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt'))
|
||||||
|
# > check duplicated child layer
|
||||||
|
result_layer = results[1].layers()[0]
|
||||||
|
self.assertEqual(result_layer, layer2)
|
||||||
|
# > check duplicated child features
|
||||||
|
self.assertTrue(results[1].duplicatedFeatures(result_layer))
|
||||||
|
|
||||||
|
'''
|
||||||
|
# testoutput 2
|
||||||
|
print( "\nFeatures on layer1 (after duplication)")
|
||||||
|
for f in layer1.getFeatures():
|
||||||
|
print( f.attributes() )
|
||||||
|
|
||||||
|
print( "\nFeatures on layer2 (after duplication)")
|
||||||
|
for f in layer2.getFeatures():
|
||||||
|
print( f.attributes() )
|
||||||
|
|
||||||
|
print( "\nAll Features and relations")
|
||||||
|
featit=layer1.getFeatures()
|
||||||
|
f=QgsFeature()
|
||||||
|
while featit.nextFeature(f):
|
||||||
|
print( f.attributes())
|
||||||
|
childFeature = QgsFeature()
|
||||||
|
relfeatit=rel.getRelatedFeatures(f)
|
||||||
|
while relfeatit.nextFeature(childFeature):
|
||||||
|
print( childFeature.attributes() )
|
||||||
|
'''
|
||||||
|
|
||||||
|
# > compare text of parent feature
|
||||||
|
self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt'))
|
||||||
|
|
||||||
|
# - create copyValueList
|
||||||
|
childFeature = QgsFeature()
|
||||||
|
relfeatit = rel.getRelatedFeatures(result_feature)
|
||||||
|
copyValueList = []
|
||||||
|
while relfeatit.nextFeature(childFeature):
|
||||||
|
copyValueList.append(childFeature.attribute('fldtxt'))
|
||||||
|
# - create origValueList
|
||||||
|
childFeature = QgsFeature()
|
||||||
|
relfeatit = rel.getRelatedFeatures(l1f1orig)
|
||||||
|
origValueList = []
|
||||||
|
while relfeatit.nextFeature(childFeature):
|
||||||
|
origValueList.append(childFeature.attribute('fldtxt'))
|
||||||
|
|
||||||
|
# - check if the ids are still the same
|
||||||
|
self.assertEqual(copyValueList, origValueList)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user