mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -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
|
||||
public:
|
||||
|
||||
enum RelationStrength
|
||||
{
|
||||
Association,
|
||||
Composition
|
||||
|
||||
};
|
||||
|
||||
|
||||
QgsRelation();
|
||||
%Docstring
|
||||
@ -53,6 +60,12 @@ class QgsRelation
|
||||
Set a name for this relation
|
||||
%End
|
||||
|
||||
void setStrength( const RelationStrength &strength );
|
||||
%Docstring
|
||||
Set a strength for this relation
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
void setReferencingLayer( const QString &id );
|
||||
%Docstring
|
||||
Set the referencing (child) layer id. This layer will be searched in the registry.
|
||||
@ -158,6 +171,15 @@ class QgsRelation
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
RelationStrength strength() const;
|
||||
%Docstring
|
||||
Returns the relation strength as a string
|
||||
|
||||
:return: strength
|
||||
.. versionadded:: 3.0
|
||||
:rtype: RelationStrength
|
||||
%End
|
||||
|
||||
QString id() const;
|
||||
%Docstring
|
||||
A (project-wide) unique id for this relation
|
||||
|
@ -22,6 +22,38 @@ class QgsVectorLayerUtils
|
||||
%End
|
||||
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() );
|
||||
%Docstring
|
||||
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
|
||||
%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 *
|
||||
* *
|
||||
|
@ -43,6 +43,17 @@ void QgsDiscoverRelationsDlg::addRelation( const QgsRelation &rel )
|
||||
mRelationsTable->setItem( row, 2, new QTableWidgetItem( rel.fieldPairs().at( 0 ).referencingField() ) );
|
||||
mRelationsTable->setItem( row, 3, new QTableWidgetItem( rel.referencedLayer()->name() ) );
|
||||
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
|
||||
|
@ -33,6 +33,10 @@ QgsRelationAddDlg::QgsRelationAddDlg( QWidget *parent )
|
||||
mCbxReferencedLayer->setFilters( QgsMapLayerProxyModel::VectorLayer );
|
||||
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]" ) );
|
||||
checkDefinitionValid();
|
||||
|
||||
@ -74,6 +78,10 @@ QString QgsRelationAddDlg::relationName()
|
||||
return mTxtRelationName->text();
|
||||
}
|
||||
|
||||
QgsRelation::RelationStrength QgsRelationAddDlg::relationStrength()
|
||||
{
|
||||
return mCbxRelationStrength->currentData().value<QgsRelation::RelationStrength>();
|
||||
}
|
||||
|
||||
void QgsRelationAddDlg::checkDefinitionValid()
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <QDialog>
|
||||
#include "ui_qgsrelationadddlgbase.h"
|
||||
#include "qgis_app.h"
|
||||
#include "qgsrelation.h"
|
||||
|
||||
class QgsVectorLayer;
|
||||
|
||||
@ -33,7 +34,7 @@ class APP_EXPORT QgsRelationAddDlg : public QDialog, private Ui::QgsRelationAddD
|
||||
QList< QPair< QString, QString > > references();
|
||||
QString relationId();
|
||||
QString relationName();
|
||||
|
||||
QgsRelation::RelationStrength relationStrength();
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -77,6 +77,19 @@ void QgsRelationManagerDialog::addRelation( const QgsRelation &rel )
|
||||
item = new QTableWidgetItem( rel.id() );
|
||||
item->setFlags( Qt::ItemIsEditable );
|
||||
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 );
|
||||
}
|
||||
|
||||
@ -116,6 +129,7 @@ void QgsRelationManagerDialog::mBtnAddRelation_clicked()
|
||||
relation.setId( relationId );
|
||||
relation.addFieldPair( addDlg.references().at( 0 ).first, addDlg.references().at( 0 ).second );
|
||||
relation.setName( addDlg.relationName() );
|
||||
relation.setStrength( addDlg.relationStrength() );
|
||||
|
||||
addRelation( relation );
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ QgsRelation QgsRelation::createFromXml( const QDomNode &node )
|
||||
QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
|
||||
QString id = elem.attribute( QStringLiteral( "id" ) );
|
||||
QString name = elem.attribute( QStringLiteral( "name" ) );
|
||||
QString strength = elem.attribute( QStringLiteral( "strength" ) );
|
||||
|
||||
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.mRelationId = id;
|
||||
relation.mRelationName = name;
|
||||
if ( strength == "Composition" )
|
||||
{
|
||||
relation.mRelationStrength = RelationStrength::Composition;
|
||||
}
|
||||
else
|
||||
{
|
||||
relation.mRelationStrength = RelationStrength::Association;
|
||||
}
|
||||
|
||||
QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
|
||||
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( "referencingLayer" ), mReferencingLayerId );
|
||||
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 )
|
||||
{
|
||||
@ -114,6 +131,12 @@ void QgsRelation::setName( const QString &name )
|
||||
mRelationName = name;
|
||||
}
|
||||
|
||||
|
||||
void QgsRelation::setStrength( const RelationStrength &strength )
|
||||
{
|
||||
mRelationStrength = strength;
|
||||
}
|
||||
|
||||
void QgsRelation::setReferencingLayer( const QString &id )
|
||||
{
|
||||
mReferencingLayerId = id;
|
||||
@ -206,6 +229,11 @@ QString QgsRelation::name() const
|
||||
return mRelationName;
|
||||
}
|
||||
|
||||
QgsRelation::RelationStrength QgsRelation::strength() const
|
||||
{
|
||||
return mRelationStrength;
|
||||
}
|
||||
|
||||
QString QgsRelation::id() const
|
||||
{
|
||||
return mRelationId;
|
||||
|
@ -48,6 +48,17 @@ class CORE_EXPORT QgsRelation
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
@ -109,6 +120,12 @@ class CORE_EXPORT QgsRelation
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -214,6 +231,14 @@ class CORE_EXPORT QgsRelation
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -345,6 +370,8 @@ class CORE_EXPORT QgsRelation
|
||||
//! The parent layer
|
||||
QgsVectorLayer *mReferencedLayer = nullptr;
|
||||
|
||||
RelationStrength mRelationStrength;
|
||||
|
||||
/**
|
||||
* A list of fields which define the relation.
|
||||
* 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
|
||||
Q_DECLARE_METATYPE( QgsRelation )
|
||||
Q_DECLARE_METATYPE( QgsRelation::RelationStrength )
|
||||
|
||||
#endif // QGSRELATION_H
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "qgsvectorlayerutils.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#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 )
|
||||
{
|
||||
@ -320,3 +323,87 @@ QgsFeature QgsVectorLayerUtils::createFeature( QgsVectorLayer *layer, const QgsG
|
||||
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:
|
||||
|
||||
/**
|
||||
* \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
|
||||
* 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(),
|
||||
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
|
||||
|
@ -62,6 +62,11 @@
|
||||
<string>Referenced Field</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Strength</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -7,31 +7,60 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>581</width>
|
||||
<height>267</height>
|
||||
<height>342</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add Relation</string>
|
||||
</property>
|
||||
<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">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="QgsMapLayerComboBox" name="mCbxReferencingLayer"/>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="mTxtRelationName"/>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Id</string>
|
||||
<string>Referenced Layer (Parent)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -41,48 +70,29 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<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">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Referencing Field</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Referenced Layer (Parent)</string>
|
||||
<string>Relationship Strength</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<item row="5" column="0" rowspan="2" colspan="2">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Referenced Field</string>
|
||||
<string>Id</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QgsFieldComboBox" name="mCbxReferencedField"/>
|
||||
</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 row="7" column="2">
|
||||
<widget class="QComboBox" name="mCbxRelationStrength"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -43,21 +43,26 @@
|
||||
<string>Referencing Field</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Referenced Layer</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Referenced Field</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Referenced Layer</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Id</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Strength</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -14,13 +14,16 @@ __revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.core import (QgsVectorLayer,
|
||||
from qgis.core import (QgsProject,
|
||||
QgsVectorLayer,
|
||||
QgsVectorLayerUtils,
|
||||
QgsFieldConstraints,
|
||||
QgsFeature,
|
||||
QgsFeatureIterator,
|
||||
QgsGeometry,
|
||||
QgsPointXY,
|
||||
QgsDefaultValue,
|
||||
QgsRelation,
|
||||
NULL
|
||||
)
|
||||
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})
|
||||
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__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user