mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-15 00:07:25 -05:00
Merge pull request #60710 from GispoCoding/59494_merge_policies
Add merge policies
This commit is contained in:
commit
34aedd067d
@ -7030,6 +7030,11 @@ Qgis.FieldDomainSplitPolicy.baseClass = Qgis
|
||||
Qgis.FieldDomainMergePolicy.DefaultValue.__doc__ = "Use default field value"
|
||||
Qgis.FieldDomainMergePolicy.Sum.__doc__ = "Sum of values"
|
||||
Qgis.FieldDomainMergePolicy.GeometryWeighted.__doc__ = "New values are computed as the weighted average of the source values"
|
||||
Qgis.FieldDomainMergePolicy.UnsetField.__doc__ = "Clears the field value so that the data provider backend will populate using any backend triggers or similar logic \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.LargestGeometry.__doc__ = "Use value from the feature with the largest geometry \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.MinimumValue.__doc__ = "Use the minimum value from the features-to-be-merged \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.MaximumValue.__doc__ = "Use the maximum value from the features-to-be-merged \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.SetToNull.__doc__ = "Use a null value \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.__doc__ = """Merge policy for field domains.
|
||||
|
||||
When a feature is built by merging multiple features, defines how the value of
|
||||
@ -7040,6 +7045,26 @@ attributes following the domain are computed.
|
||||
* ``DefaultValue``: Use default field value
|
||||
* ``Sum``: Sum of values
|
||||
* ``GeometryWeighted``: New values are computed as the weighted average of the source values
|
||||
* ``UnsetField``: Clears the field value so that the data provider backend will populate using any backend triggers or similar logic
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``LargestGeometry``: Use value from the feature with the largest geometry
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``MinimumValue``: Use the minimum value from the features-to-be-merged
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``MaximumValue``: Use the maximum value from the features-to-be-merged
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``SetToNull``: Use a null value
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
|
||||
"""
|
||||
# --
|
||||
|
||||
@ -2140,6 +2140,11 @@ The development version
|
||||
DefaultValue,
|
||||
Sum,
|
||||
GeometryWeighted,
|
||||
UnsetField,
|
||||
LargestGeometry,
|
||||
MinimumValue,
|
||||
MaximumValue,
|
||||
SetToNull,
|
||||
};
|
||||
|
||||
enum class FieldDuplicatePolicy /BaseType=IntEnum/
|
||||
|
||||
@ -549,6 +549,26 @@ should be handled during a duplicate operation.
|
||||
.. seealso:: :py:func:`duplicatePolicy`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
Qgis::FieldDomainMergePolicy mergePolicy() const /HoldGIL/;
|
||||
%Docstring
|
||||
Returns the field's merge policy, which indicates how field values should
|
||||
be handled during a merge operation.
|
||||
|
||||
.. seealso:: :py:func:`setMergePolicy`
|
||||
|
||||
.. versionadded:: 3.44
|
||||
%End
|
||||
|
||||
void setMergePolicy( Qgis::FieldDomainMergePolicy policy ) /HoldGIL/;
|
||||
%Docstring
|
||||
Sets the field's merge ``policy``, which indicates how field values should
|
||||
be handled during a merge operation.
|
||||
|
||||
.. seealso:: :py:func:`mergePolicy`
|
||||
|
||||
.. versionadded:: 3.44
|
||||
%End
|
||||
|
||||
SIP_PYOBJECT __repr__();
|
||||
|
||||
@ -2023,6 +2023,27 @@ Sets a duplicate ``policy`` for the field with the specified index.
|
||||
}
|
||||
%End
|
||||
|
||||
void setFieldMergePolicy( int index, Qgis::FieldDomainMergePolicy policy );
|
||||
%Docstring
|
||||
Sets a merge ``policy`` for the field with the specified index.
|
||||
|
||||
:raises KeyError: if no field with the specified index exists
|
||||
|
||||
.. versionadded:: 3.44
|
||||
%End
|
||||
|
||||
%MethodCode
|
||||
if ( a0 < 0 || a0 >= sipCpp->fields().count() )
|
||||
{
|
||||
PyErr_SetString( PyExc_KeyError, QByteArray::number( a0 ) );
|
||||
sipIsErr = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipCpp->setFieldMergePolicy( a0, a1 );
|
||||
}
|
||||
%End
|
||||
|
||||
QSet<QString> excludeAttributesWms() const /Deprecated="Since 3.16. Use fields().configurationFlags() instead."/;
|
||||
%Docstring
|
||||
A set of attributes that are not advertised in WMS requests with QGIS
|
||||
|
||||
@ -6964,6 +6964,11 @@ Qgis.FieldDomainSplitPolicy.baseClass = Qgis
|
||||
Qgis.FieldDomainMergePolicy.DefaultValue.__doc__ = "Use default field value"
|
||||
Qgis.FieldDomainMergePolicy.Sum.__doc__ = "Sum of values"
|
||||
Qgis.FieldDomainMergePolicy.GeometryWeighted.__doc__ = "New values are computed as the weighted average of the source values"
|
||||
Qgis.FieldDomainMergePolicy.UnsetField.__doc__ = "Clears the field value so that the data provider backend will populate using any backend triggers or similar logic \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.LargestGeometry.__doc__ = "Use value from the feature with the largest geometry \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.MinimumValue.__doc__ = "Use the minimum value from the features-to-be-merged \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.MaximumValue.__doc__ = "Use the maximum value from the features-to-be-merged \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.SetToNull.__doc__ = "Use a null value \n.. versionadded:: 3.44"
|
||||
Qgis.FieldDomainMergePolicy.__doc__ = """Merge policy for field domains.
|
||||
|
||||
When a feature is built by merging multiple features, defines how the value of
|
||||
@ -6974,6 +6979,26 @@ attributes following the domain are computed.
|
||||
* ``DefaultValue``: Use default field value
|
||||
* ``Sum``: Sum of values
|
||||
* ``GeometryWeighted``: New values are computed as the weighted average of the source values
|
||||
* ``UnsetField``: Clears the field value so that the data provider backend will populate using any backend triggers or similar logic
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``LargestGeometry``: Use value from the feature with the largest geometry
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``MinimumValue``: Use the minimum value from the features-to-be-merged
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``MaximumValue``: Use the maximum value from the features-to-be-merged
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
* ``SetToNull``: Use a null value
|
||||
|
||||
.. versionadded:: 3.44
|
||||
|
||||
|
||||
"""
|
||||
# --
|
||||
|
||||
@ -2140,6 +2140,11 @@ The development version
|
||||
DefaultValue,
|
||||
Sum,
|
||||
GeometryWeighted,
|
||||
UnsetField,
|
||||
LargestGeometry,
|
||||
MinimumValue,
|
||||
MaximumValue,
|
||||
SetToNull,
|
||||
};
|
||||
|
||||
enum class FieldDuplicatePolicy
|
||||
|
||||
@ -549,6 +549,26 @@ should be handled during a duplicate operation.
|
||||
.. seealso:: :py:func:`duplicatePolicy`
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
Qgis::FieldDomainMergePolicy mergePolicy() const /HoldGIL/;
|
||||
%Docstring
|
||||
Returns the field's merge policy, which indicates how field values should
|
||||
be handled during a merge operation.
|
||||
|
||||
.. seealso:: :py:func:`setMergePolicy`
|
||||
|
||||
.. versionadded:: 3.44
|
||||
%End
|
||||
|
||||
void setMergePolicy( Qgis::FieldDomainMergePolicy policy ) /HoldGIL/;
|
||||
%Docstring
|
||||
Sets the field's merge ``policy``, which indicates how field values should
|
||||
be handled during a merge operation.
|
||||
|
||||
.. seealso:: :py:func:`mergePolicy`
|
||||
|
||||
.. versionadded:: 3.44
|
||||
%End
|
||||
|
||||
SIP_PYOBJECT __repr__();
|
||||
|
||||
@ -2023,6 +2023,27 @@ Sets a duplicate ``policy`` for the field with the specified index.
|
||||
}
|
||||
%End
|
||||
|
||||
void setFieldMergePolicy( int index, Qgis::FieldDomainMergePolicy policy );
|
||||
%Docstring
|
||||
Sets a merge ``policy`` for the field with the specified index.
|
||||
|
||||
:raises KeyError: if no field with the specified index exists
|
||||
|
||||
.. versionadded:: 3.44
|
||||
%End
|
||||
|
||||
%MethodCode
|
||||
if ( a0 < 0 || a0 >= sipCpp->fields().count() )
|
||||
{
|
||||
PyErr_SetString( PyExc_KeyError, QByteArray::number( a0 ) );
|
||||
sipIsErr = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipCpp->setFieldMergePolicy( a0, a1 );
|
||||
}
|
||||
%End
|
||||
|
||||
QSet<QString> excludeAttributesWms() const /Deprecated="Since 3.16. Use fields().configurationFlags() instead."/;
|
||||
%Docstring
|
||||
A set of attributes that are not advertised in WMS requests with QGIS
|
||||
|
||||
@ -2317,6 +2317,21 @@ QString QgsFieldDomainDetailsWidget::htmlMetadata( QgsFieldDomain *domain, const
|
||||
case Qgis::FieldDomainMergePolicy::GeometryWeighted:
|
||||
metadata += tr( "Use geometry weighted value" );
|
||||
break;
|
||||
case Qgis::FieldDomainMergePolicy::UnsetField:
|
||||
metadata += tr( "Unset field" );
|
||||
break;
|
||||
case Qgis::FieldDomainMergePolicy::LargestGeometry:
|
||||
metadata += tr( "Largest geometry" );
|
||||
break;
|
||||
case Qgis::FieldDomainMergePolicy::MaximumValue:
|
||||
metadata += tr( "Maximum value" );
|
||||
break;
|
||||
case Qgis::FieldDomainMergePolicy::MinimumValue:
|
||||
metadata += tr( "Minimum value" );
|
||||
break;
|
||||
case Qgis::FieldDomainMergePolicy::SetToNull:
|
||||
metadata += tr( "Set attribute to NULL" );
|
||||
break;
|
||||
}
|
||||
|
||||
metadata += QLatin1String( "</table>\n<br><br>" );
|
||||
|
||||
@ -9464,9 +9464,8 @@ void QgisApp::mergeAttributesOfSelectedFeatures()
|
||||
QgsFeatureList featureList = vl->selectedFeatures();
|
||||
|
||||
//merge the attributes together
|
||||
QgsMergeAttributesDialog d( featureList, vl, mapCanvas() );
|
||||
//initialize dialog with all columns set to skip
|
||||
d.setAllToSkip();
|
||||
QgsMergeAttributesDialog d( featureList, vl, mapCanvas(), true );
|
||||
|
||||
if ( d.exec() == QDialog::Rejected )
|
||||
{
|
||||
return;
|
||||
|
||||
@ -50,7 +50,7 @@ const QList<Qgis::Statistic> QgsMergeAttributesDialog::DISPLAY_STATS = QList<Qgi
|
||||
<< Qgis::Statistic::ThirdQuartile
|
||||
<< Qgis::Statistic::InterQuartileRange;
|
||||
|
||||
QgsMergeAttributesDialog::QgsMergeAttributesDialog( const QgsFeatureList &features, QgsVectorLayer *vl, QgsMapCanvas *canvas, QWidget *parent, Qt::WindowFlags f )
|
||||
QgsMergeAttributesDialog::QgsMergeAttributesDialog( const QgsFeatureList &features, QgsVectorLayer *vl, QgsMapCanvas *canvas, bool skipAll, QWidget *parent, Qt::WindowFlags f )
|
||||
: QDialog( parent, f )
|
||||
, mFeatureList( features )
|
||||
, mVectorLayer( vl )
|
||||
@ -63,7 +63,11 @@ QgsMergeAttributesDialog::QgsMergeAttributesDialog( const QgsFeatureList &featur
|
||||
connect( mFromSelectedPushButton, &QPushButton::clicked, this, &QgsMergeAttributesDialog::mFromSelectedPushButton_clicked );
|
||||
connect( mFromLargestPushButton, &QPushButton::clicked, this, &QgsMergeAttributesDialog::mFromLargestPushButton_clicked );
|
||||
connect( mRemoveFeatureFromSelectionButton, &QPushButton::clicked, this, &QgsMergeAttributesDialog::mRemoveFeatureFromSelectionButton_clicked );
|
||||
createTableWidgetContents();
|
||||
|
||||
createTableWidgetContents( skipAll );
|
||||
|
||||
if ( skipAll )
|
||||
setAllToSkip();
|
||||
|
||||
QHeaderView *verticalHeader = mTableWidget->verticalHeader();
|
||||
if ( verticalHeader )
|
||||
@ -149,7 +153,7 @@ void QgsMergeAttributesDialog::setAttributeTableConfig( const QgsAttributeTableC
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::createTableWidgetContents()
|
||||
void QgsMergeAttributesDialog::createTableWidgetContents( bool skipAll )
|
||||
{
|
||||
//get information about attributes from vector layer
|
||||
if ( !mVectorLayer )
|
||||
@ -238,29 +242,163 @@ void QgsMergeAttributesDialog::createTableWidgetContents()
|
||||
//initially set any fields with default values/default value clauses to that value
|
||||
for ( int j = 0; j < mTableWidget->columnCount(); j++ )
|
||||
{
|
||||
if ( skipAll )
|
||||
break;
|
||||
|
||||
int idx = mTableWidget->horizontalHeaderItem( j )->data( FieldIndex ).toInt();
|
||||
bool setToManual = false;
|
||||
|
||||
if ( !mVectorLayer->dataProvider()->defaultValueClause( idx ).isEmpty() )
|
||||
const QgsField field = mVectorLayer->fields().at( idx );
|
||||
QComboBox *currentComboBox = qobject_cast<QComboBox *>( mTableWidget->cellWidget( 0, j ) );
|
||||
|
||||
switch ( field.mergePolicy() )
|
||||
{
|
||||
QVariant v = mVectorLayer->dataProvider()->defaultValueClause( idx );
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::DisplayRole, v );
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::UserRole, v );
|
||||
setToManual = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QVariant v = mVectorLayer->dataProvider()->defaultValue( idx );
|
||||
if ( v.isValid() )
|
||||
case Qgis::FieldDomainMergePolicy::Sum:
|
||||
{
|
||||
if ( !field.isNumeric() )
|
||||
break;
|
||||
|
||||
if ( currentComboBox )
|
||||
currentComboBox->setCurrentIndex( currentComboBox->findData( static_cast<int>( Qgis::Statistic::Sum ) ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::DefaultValue:
|
||||
{
|
||||
// create a dummy feature with the combined geometry in case the default value expression uses the geometry.
|
||||
// however populating the feature's fields is problematic because the values haven't been set yet and
|
||||
// generating them at this point isn't possible since the expression might refer to another field
|
||||
// with a default value expression leading to conflicts
|
||||
QgsFeature f;
|
||||
|
||||
QVector<QgsGeometry> geoms;
|
||||
for ( const QgsFeature &f : mFeatureList )
|
||||
geoms << f.geometry();
|
||||
|
||||
const QgsGeometry mergedGeom = QgsGeometry::unaryUnion( geoms );
|
||||
f.setGeometry( mergedGeom );
|
||||
|
||||
const QVariant v = mVectorLayer->defaultValue( idx, f );
|
||||
|
||||
if ( !v.isValid() )
|
||||
break;
|
||||
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::DisplayRole, v );
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::UserRole, v );
|
||||
setToManual = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::GeometryWeighted:
|
||||
{
|
||||
if ( !field.isNumeric() || mVectorLayer->geometryType() == Qgis::GeometryType::Point )
|
||||
break;
|
||||
|
||||
QVector<QgsGeometry> geoms;
|
||||
for ( const QgsFeature &f : mFeatureList )
|
||||
geoms << f.geometry();
|
||||
|
||||
const QgsGeometry mergedGeom = QgsGeometry::unaryUnion( geoms );
|
||||
const double mergedSize = mVectorLayer->geometryType() == Qgis::GeometryType::Polygon ? mergedGeom.area() : mergedGeom.length();
|
||||
|
||||
const double value = std::accumulate( mFeatureList.constBegin(), mFeatureList.constEnd(), 0.0, [&, idx]( double sum, const QgsFeature &f ) {
|
||||
const double geomSize = mVectorLayer->geometryType() == Qgis::GeometryType::Polygon ? f.geometry().area() : f.geometry().length();
|
||||
const double weightMultiplier = geomSize / mergedSize;
|
||||
return sum + ( f.attribute( idx ).toDouble() * weightMultiplier );
|
||||
} );
|
||||
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::DisplayRole, value );
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::UserRole, value );
|
||||
setToManual = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::UnsetField:
|
||||
{
|
||||
if ( !mVectorLayer->dataProvider()->defaultValueClause( idx ).isEmpty() )
|
||||
{
|
||||
QVariant v = mVectorLayer->dataProvider()->defaultValueClause( idx );
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::DisplayRole, v );
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::UserRole, v );
|
||||
setToManual = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QVariant v = mVectorLayer->dataProvider()->defaultValue( idx );
|
||||
if ( v.isValid() )
|
||||
{
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::DisplayRole, v );
|
||||
mTableWidget->item( mTableWidget->rowCount() - 1, j )->setData( Qt::UserRole, v );
|
||||
setToManual = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::LargestGeometry:
|
||||
{
|
||||
if ( mVectorLayer->geometryType() == Qgis::GeometryType::Unknown || mVectorLayer->geometryType() == Qgis::GeometryType::Null )
|
||||
break;
|
||||
|
||||
QgsFeatureId largestFeatureId = FID_NULL;
|
||||
|
||||
if ( mVectorLayer->geometryType() == Qgis::GeometryType::Point )
|
||||
{
|
||||
QList<QgsFeature>::iterator largestSelectedFeature = std::max_element( mFeatureList.begin(), mFeatureList.end(), []( const QgsFeature &a, const QgsFeature &b ) -> bool {
|
||||
return a.geometry().constGet()->partCount() < b.geometry().constGet()->partCount();
|
||||
} );
|
||||
|
||||
largestFeatureId = largestSelectedFeature->id();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::function<double( const QgsGeometry & )> getSize = mVectorLayer->geometryType() == Qgis::GeometryType::Polygon ? &QgsGeometry::area : &QgsGeometry::length;
|
||||
|
||||
QList<QgsFeature>::iterator largestSelectedFeature = std::max_element( mFeatureList.begin(), mFeatureList.end(), [&getSize]( const QgsFeature &a, const QgsFeature &b ) -> bool {
|
||||
return getSize( a.geometry() ) < getSize( b.geometry() );
|
||||
} );
|
||||
|
||||
largestFeatureId = largestSelectedFeature->id();
|
||||
}
|
||||
|
||||
if ( largestFeatureId == FID_NULL )
|
||||
break;
|
||||
|
||||
if ( currentComboBox )
|
||||
currentComboBox->setCurrentIndex( currentComboBox->findData( QStringLiteral( "f%1" ).arg( FID_TO_STRING( largestFeatureId ) ) ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::MinimumValue:
|
||||
{
|
||||
if ( currentComboBox )
|
||||
currentComboBox->setCurrentIndex( currentComboBox->findData( static_cast<int>( Qgis::Statistic::Min ) ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::MaximumValue:
|
||||
{
|
||||
if ( currentComboBox )
|
||||
currentComboBox->setCurrentIndex( currentComboBox->findData( static_cast<int>( Qgis::Statistic::Max ) ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::SetToNull:
|
||||
{
|
||||
if ( currentComboBox )
|
||||
currentComboBox->setCurrentIndex( currentComboBox->findData( QStringLiteral( "null" ) ) );
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( setToManual )
|
||||
{
|
||||
QComboBox *currentComboBox = qobject_cast<QComboBox *>( mTableWidget->cellWidget( 0, j ) );
|
||||
if ( currentComboBox )
|
||||
{
|
||||
currentComboBox->blockSignals( true );
|
||||
@ -315,6 +453,7 @@ QComboBox *QgsMergeAttributesDialog::createMergeComboBox( QMetaType::Type column
|
||||
|
||||
newComboBox->addItem( tr( "Skip Attribute" ), QStringLiteral( "skip" ) );
|
||||
newComboBox->addItem( tr( "Manual Value" ), QStringLiteral( "manual" ) );
|
||||
newComboBox->addItem( tr( "Set to NULL" ), QStringLiteral( "null" ) );
|
||||
|
||||
connect( newComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [=]() {
|
||||
bool isManual = newComboBox->currentData() == QLatin1String( "manual" );
|
||||
@ -404,6 +543,10 @@ void QgsMergeAttributesDialog::refreshMergedValue( int col )
|
||||
{
|
||||
mergeResult = tr( "Skipped" );
|
||||
}
|
||||
else if ( mergeBehaviorString == QLatin1String( "null" ) )
|
||||
{
|
||||
mergeResult = tr( "NULL" );
|
||||
}
|
||||
else if ( mergeBehaviorString.startsWith( 'f' ) )
|
||||
{
|
||||
//an existing feature value
|
||||
@ -422,7 +565,7 @@ void QgsMergeAttributesDialog::refreshMergedValue( int col )
|
||||
|
||||
// Result formatting
|
||||
QString stringVal;
|
||||
if ( mergeBehaviorString != QLatin1String( "skip" ) && mergeBehaviorString != QLatin1String( "manual" ) )
|
||||
if ( mergeBehaviorString != QLatin1String( "skip" ) && mergeBehaviorString != QLatin1String( "manual" ) && mergeBehaviorString != QLatin1String( "null" ) )
|
||||
{
|
||||
const QgsEditorWidgetSetup setup = mFields.at( fieldIdx ).editorWidgetSetup();
|
||||
const QgsFieldFormatter *formatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
|
||||
@ -783,6 +926,14 @@ QgsAttributes QgsMergeAttributesDialog::mergedAttributes() const
|
||||
continue;
|
||||
|
||||
QVariant value;
|
||||
|
||||
if ( comboBox->currentData().toString() == QLatin1String( "null" ) )
|
||||
{
|
||||
results[fieldIdx] = value;
|
||||
widgetIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
QWidget *w = mTableWidget->cellWidget( mFeatureList.size() + 1, widgetIndex );
|
||||
QgsEditorWidgetWrapper *eww = QgsEditorWidgetWrapper::fromWidget( w );
|
||||
if ( eww )
|
||||
|
||||
@ -42,7 +42,7 @@ class APP_EXPORT QgsMergeAttributesDialog : public QDialog, private Ui::QgsMerge
|
||||
};
|
||||
|
||||
|
||||
QgsMergeAttributesDialog( const QgsFeatureList &features, QgsVectorLayer *vl, QgsMapCanvas *canvas, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags() );
|
||||
QgsMergeAttributesDialog( const QgsFeatureList &features, QgsVectorLayer *vl, QgsMapCanvas *canvas, bool skipAll = false, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags() );
|
||||
~QgsMergeAttributesDialog() override;
|
||||
|
||||
QgsAttributes mergedAttributes() const;
|
||||
@ -82,7 +82,7 @@ class APP_EXPORT QgsMergeAttributesDialog : public QDialog, private Ui::QgsMerge
|
||||
|
||||
private:
|
||||
QgsMergeAttributesDialog(); //default constructor forbidden
|
||||
void createTableWidgetContents();
|
||||
void createTableWidgetContents( bool skipAll );
|
||||
void setAttributeTableConfig( const QgsAttributeTableConfig &config );
|
||||
|
||||
//! Create new combo box with the options for featureXX / mean / min / max
|
||||
|
||||
@ -3775,6 +3775,11 @@ class CORE_EXPORT Qgis
|
||||
DefaultValue, //!< Use default field value
|
||||
Sum, //!< Sum of values
|
||||
GeometryWeighted, //!< New values are computed as the weighted average of the source values
|
||||
UnsetField, //!< Clears the field value so that the data provider backend will populate using any backend triggers or similar logic \since QGIS 3.44
|
||||
LargestGeometry, //!< Use value from the feature with the largest geometry \since QGIS 3.44
|
||||
MinimumValue, //!< Use the minimum value from the features-to-be-merged \since QGIS 3.44
|
||||
MaximumValue, //!< Use the maximum value from the features-to-be-merged \since QGIS 3.44
|
||||
SetToNull, //!< Use a null value \since QGIS 3.44
|
||||
};
|
||||
Q_ENUM( FieldDomainMergePolicy )
|
||||
|
||||
|
||||
@ -778,6 +778,16 @@ void QgsField::setDuplicatePolicy( Qgis::FieldDuplicatePolicy policy )
|
||||
d->duplicatePolicy = policy;
|
||||
}
|
||||
|
||||
Qgis::FieldDomainMergePolicy QgsField::mergePolicy() const
|
||||
{
|
||||
return d->mergePolicy;
|
||||
}
|
||||
|
||||
void QgsField::setMergePolicy( Qgis::FieldDomainMergePolicy policy )
|
||||
{
|
||||
d->mergePolicy = policy;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* This class is considered CRITICAL and any change MUST be accompanied with
|
||||
* full unit tests in testqgsfield.cpp.
|
||||
|
||||
@ -545,6 +545,26 @@ class CORE_EXPORT QgsField
|
||||
*/
|
||||
void setDuplicatePolicy( Qgis::FieldDuplicatePolicy policy ) SIP_HOLDGIL;
|
||||
|
||||
/**
|
||||
* Returns the field's merge policy, which indicates how field values should
|
||||
* be handled during a merge operation.
|
||||
*
|
||||
* \see setMergePolicy()
|
||||
*
|
||||
* \since QGIS 3.44
|
||||
*/
|
||||
Qgis::FieldDomainMergePolicy mergePolicy() const SIP_HOLDGIL;
|
||||
|
||||
/**
|
||||
* Sets the field's merge \a policy, which indicates how field values should
|
||||
* be handled during a merge operation.
|
||||
*
|
||||
* \see mergePolicy()
|
||||
*
|
||||
* \since QGIS 3.44
|
||||
*/
|
||||
void setMergePolicy( Qgis::FieldDomainMergePolicy policy ) SIP_HOLDGIL;
|
||||
|
||||
#ifdef SIP_RUN
|
||||
SIP_PYOBJECT __repr__();
|
||||
% MethodCode
|
||||
|
||||
@ -85,6 +85,7 @@ class QgsFieldPrivate : public QSharedData
|
||||
, editorWidgetSetup( other.editorWidgetSetup )
|
||||
, splitPolicy( other.splitPolicy )
|
||||
, duplicatePolicy( other.duplicatePolicy )
|
||||
, mergePolicy( other.mergePolicy )
|
||||
, isReadOnly( other.isReadOnly )
|
||||
{
|
||||
}
|
||||
@ -101,6 +102,7 @@ class QgsFieldPrivate : public QSharedData
|
||||
&& ( constraints == other.constraints ) && ( flags == other.flags )
|
||||
&& ( splitPolicy == other.splitPolicy )
|
||||
&& ( duplicatePolicy == other.duplicatePolicy )
|
||||
&& ( mergePolicy == other.mergePolicy )
|
||||
&& ( isReadOnly == other.isReadOnly )
|
||||
&& ( editorWidgetSetup == other.editorWidgetSetup ) );
|
||||
}
|
||||
@ -149,6 +151,9 @@ class QgsFieldPrivate : public QSharedData
|
||||
//! Duplicate policy
|
||||
Qgis::FieldDuplicatePolicy duplicatePolicy = Qgis::FieldDuplicatePolicy::Duplicate;
|
||||
|
||||
//! Merge policy
|
||||
Qgis::FieldDomainMergePolicy mergePolicy = Qgis::FieldDomainMergePolicy::UnsetField;
|
||||
|
||||
//! Read-only
|
||||
bool isReadOnly = false;
|
||||
|
||||
|
||||
@ -2298,6 +2298,14 @@ OGRFieldDomainH QgsOgrUtils::convertFieldDomain( const QgsFieldDomain *domain )
|
||||
case Qgis::FieldDomainMergePolicy::Sum:
|
||||
OGR_FldDomain_SetMergePolicy( res, OFDMP_SUM );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::UnsetField:
|
||||
case Qgis::FieldDomainMergePolicy::LargestGeometry:
|
||||
case Qgis::FieldDomainMergePolicy::MinimumValue:
|
||||
case Qgis::FieldDomainMergePolicy::MaximumValue:
|
||||
case Qgis::FieldDomainMergePolicy::SetToNull:
|
||||
// not supported
|
||||
break;
|
||||
}
|
||||
|
||||
switch ( domain->splitPolicy() )
|
||||
|
||||
@ -2245,6 +2245,10 @@ bool QgsVectorLayer::setDataProvider( QString const &provider, const QgsDataProv
|
||||
{
|
||||
mAttributeDuplicatePolicy[ field.name() ] = field.duplicatePolicy();
|
||||
}
|
||||
if ( !mAttributeMergePolicy.contains( field.name() ) )
|
||||
{
|
||||
mAttributeMergePolicy[ field.name() ] = field.mergePolicy();
|
||||
}
|
||||
}
|
||||
|
||||
if ( profile )
|
||||
@ -2585,6 +2589,19 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes
|
||||
}
|
||||
}
|
||||
|
||||
const QDomNode mergePoliciesNode = layerNode.namedItem( QStringLiteral( "mergePolicies" ) );
|
||||
if ( !mergePoliciesNode.isNull() )
|
||||
{
|
||||
const QDomNodeList mergePolicyNodeList = mergePoliciesNode.toElement().elementsByTagName( QStringLiteral( "policy" ) );
|
||||
for ( int i = 0; i < mergePolicyNodeList.size(); ++i )
|
||||
{
|
||||
const QDomElement mergePolicyElem = mergePolicyNodeList.at( i ).toElement();
|
||||
const QString field = mergePolicyElem.attribute( QStringLiteral( "field" ) );
|
||||
const Qgis::FieldDomainMergePolicy policy = qgsEnumKeyToValue( mergePolicyElem.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainMergePolicy::DefaultValue );
|
||||
mAttributeMergePolicy.insert( field, policy );
|
||||
}
|
||||
}
|
||||
|
||||
// default expressions
|
||||
mDefaultExpressionMap.clear();
|
||||
QDomNode defaultsNode = layerNode.namedItem( QStringLiteral( "defaults" ) );
|
||||
@ -3136,6 +3153,19 @@ bool QgsVectorLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString
|
||||
node.appendChild( duplicatePoliciesElement );
|
||||
}
|
||||
|
||||
//merge policies
|
||||
{
|
||||
QDomElement mergePoliciesElement = doc.createElement( QStringLiteral( "mergePolicies" ) );
|
||||
for ( const QgsField &field : std::as_const( mFields ) )
|
||||
{
|
||||
QDomElement mergePolicyElem = doc.createElement( QStringLiteral( "policy" ) );
|
||||
mergePolicyElem.setAttribute( QStringLiteral( "field" ), field.name() );
|
||||
mergePolicyElem.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.mergePolicy() ) );
|
||||
mergePoliciesElement.appendChild( mergePolicyElem );
|
||||
}
|
||||
node.appendChild( mergePoliciesElement );
|
||||
}
|
||||
|
||||
//default expressions
|
||||
QDomElement defaultsElem = doc.createElement( QStringLiteral( "defaults" ) );
|
||||
for ( const QgsField &field : std::as_const( mFields ) )
|
||||
@ -3655,6 +3685,20 @@ void QgsVectorLayer::setFieldDuplicatePolicy( int index, Qgis::FieldDuplicatePol
|
||||
emit layerModified(); // TODO[MD]: should have a different signal?
|
||||
}
|
||||
|
||||
void QgsVectorLayer::setFieldMergePolicy( int index, Qgis::FieldDomainMergePolicy policy )
|
||||
{
|
||||
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
|
||||
|
||||
if ( index < 0 || index >= fields().count() )
|
||||
return;
|
||||
|
||||
const QString name = fields().at( index ).name();
|
||||
|
||||
mAttributeMergePolicy.insert( name, policy );
|
||||
mFields[ index ].setMergePolicy( policy );
|
||||
mEditFormConfig.setFields( mFields );
|
||||
emit layerModified(); // TODO[MD]: should have a different signal?
|
||||
}
|
||||
|
||||
QSet<QString> QgsVectorLayer::excludeAttributesWms() const
|
||||
{
|
||||
@ -4537,6 +4581,15 @@ void QgsVectorLayer::updateFields()
|
||||
mFields[ index ].setDuplicatePolicy( duplicatePolicyIt.value() );
|
||||
}
|
||||
|
||||
for ( auto mergePolicyIt = mAttributeMergePolicy.constBegin(); mergePolicyIt != mAttributeMergePolicy.constEnd(); ++mergePolicyIt )
|
||||
{
|
||||
int index = mFields.lookupField( mergePolicyIt.key() );
|
||||
if ( index < 0 )
|
||||
continue;
|
||||
|
||||
mFields[ index ].setMergePolicy( mergePolicyIt.value() );
|
||||
}
|
||||
|
||||
// Update configuration flags
|
||||
QMap< QString, Qgis::FieldConfigurationFlags >::const_iterator flagsIt = mFieldConfigurationFlags.constBegin();
|
||||
for ( ; flagsIt != mFieldConfigurationFlags.constEnd(); ++flagsIt )
|
||||
|
||||
@ -1851,6 +1851,13 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
void setFieldDuplicatePolicy( int index, Qgis::FieldDuplicatePolicy policy );
|
||||
|
||||
/**
|
||||
* Sets a merge \a policy for the field with the specified index.
|
||||
*
|
||||
* \since QGIS 3.44
|
||||
*/
|
||||
void setFieldMergePolicy( int index, Qgis::FieldDomainMergePolicy policy );
|
||||
#else
|
||||
|
||||
/**
|
||||
@ -1892,6 +1899,26 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
sipCpp->setFieldDuplicatePolicy( a0, a1 );
|
||||
}
|
||||
% End
|
||||
|
||||
/**
|
||||
* Sets a merge \a policy for the field with the specified index.
|
||||
*
|
||||
* \throws KeyError if no field with the specified index exists
|
||||
* \since QGIS 3.44
|
||||
*/
|
||||
void setFieldMergePolicy( int index, Qgis::FieldDomainMergePolicy policy );
|
||||
|
||||
% MethodCode
|
||||
if ( a0 < 0 || a0 >= sipCpp->fields().count() )
|
||||
{
|
||||
PyErr_SetString( PyExc_KeyError, QByteArray::number( a0 ) );
|
||||
sipIsErr = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipCpp->setFieldMergePolicy( a0, a1 );
|
||||
}
|
||||
% End
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -2909,6 +2936,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
//! Map that stores the duplicate policy for attributes
|
||||
QMap< QString, Qgis::FieldDuplicatePolicy > mAttributeDuplicatePolicy;
|
||||
|
||||
//! Map that stores the merge policy for attributes
|
||||
QMap< QString, Qgis::FieldDomainMergePolicy > mAttributeMergePolicy;
|
||||
|
||||
//! An internal structure to keep track of fields that have a defaultValueOnUpdate
|
||||
QSet<int> mDefaultValueOnUpdateFields;
|
||||
|
||||
|
||||
@ -126,6 +126,34 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx
|
||||
mDuplicatePolicyComboBox->addItem( tr( "Remove Value" ), QVariant::fromValue( Qgis::FieldDuplicatePolicy::UnsetField ) );
|
||||
connect( mDuplicatePolicyComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAttributeTypeDialog::updateDuplicatePolicyLabel );
|
||||
updateDuplicatePolicyLabel();
|
||||
|
||||
if ( mLayer->isSpatial() )
|
||||
{
|
||||
mMergePolicyComboBox->addItem( tr( "Remove Value" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::UnsetField ) );
|
||||
mMergePolicyComboBox->addItem( tr( "Use Default Value" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::DefaultValue ) );
|
||||
|
||||
if ( mLayer->fields().at( mFieldIdx ).isNumeric() )
|
||||
{
|
||||
mMergePolicyComboBox->addItem( tr( "Use Sum" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::Sum ) );
|
||||
mMergePolicyComboBox->addItem( tr( "Use Maximum Value" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::MaximumValue ) );
|
||||
mMergePolicyComboBox->addItem( tr( "Use Minimum Value" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::MinimumValue ) );
|
||||
|
||||
if ( mLayer->geometryType() != Qgis::GeometryType::Point )
|
||||
mMergePolicyComboBox->addItem( tr( "Use Average Weighted by Geometry" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::GeometryWeighted ) );
|
||||
}
|
||||
|
||||
mMergePolicyComboBox->addItem( tr( "Use Largest Feature" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::LargestGeometry ) );
|
||||
mMergePolicyComboBox->addItem( tr( "Set to NULL" ), QVariant::fromValue( Qgis::FieldDomainMergePolicy::SetToNull ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mMergePolicyComboBox->setEnabled( false );
|
||||
mMergePolicyLabel->setEnabled( false );
|
||||
mMergePolicyDescriptionLabel->hide();
|
||||
}
|
||||
|
||||
connect( mMergePolicyComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAttributeTypeDialog::updateMergePolicyLabel );
|
||||
updateMergePolicyLabel();
|
||||
}
|
||||
|
||||
QgsAttributeTypeDialog::~QgsAttributeTypeDialog()
|
||||
@ -400,6 +428,17 @@ void QgsAttributeTypeDialog::setDuplicatePolicy( Qgis::FieldDuplicatePolicy poli
|
||||
updateSplitPolicyLabel();
|
||||
}
|
||||
|
||||
void QgsAttributeTypeDialog::setMergePolicy( Qgis::FieldDomainMergePolicy policy )
|
||||
{
|
||||
mMergePolicyComboBox->setCurrentIndex( mMergePolicyComboBox->findData( QVariant::fromValue( policy ) ) );
|
||||
updateMergePolicyLabel();
|
||||
}
|
||||
|
||||
Qgis::FieldDomainMergePolicy QgsAttributeTypeDialog::mergePolicy() const
|
||||
{
|
||||
return mMergePolicyComboBox->currentData().value<Qgis::FieldDomainMergePolicy>();
|
||||
}
|
||||
|
||||
QString QgsAttributeTypeDialog::constraintExpression() const
|
||||
{
|
||||
return constraintExpressionWidget->asExpression();
|
||||
@ -537,6 +576,46 @@ void QgsAttributeTypeDialog::updateDuplicatePolicyLabel()
|
||||
mDuplicatePolicyDescriptionLabel->setText( QStringLiteral( "<i>%1</i>" ).arg( helperText ) );
|
||||
}
|
||||
|
||||
void QgsAttributeTypeDialog::updateMergePolicyLabel()
|
||||
{
|
||||
QString helperText;
|
||||
switch ( mMergePolicyComboBox->currentData().value<Qgis::FieldDomainMergePolicy>() )
|
||||
{
|
||||
case Qgis::FieldDomainMergePolicy::DefaultValue:
|
||||
helperText = tr( "Use default field value." );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::Sum:
|
||||
helperText = tr( "Sum of values." );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::GeometryWeighted:
|
||||
helperText = tr( "New values are computed as the weighted average of the source values." );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::UnsetField:
|
||||
helperText = tr( "Clears the field to an unset state." );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::LargestGeometry:
|
||||
helperText = tr( "Use value from feature with the largest geometry." );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::MinimumValue:
|
||||
helperText = tr( "Use the lowest value from the selected features." );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::MaximumValue:
|
||||
helperText = tr( "Use the highest value from the selected features." );
|
||||
break;
|
||||
|
||||
case Qgis::FieldDomainMergePolicy::SetToNull:
|
||||
helperText = tr( "Set attribute to NULL." );
|
||||
break;
|
||||
}
|
||||
mMergePolicyDescriptionLabel->setText( QStringLiteral( "<i>%1</i>" ).arg( helperText ) );
|
||||
}
|
||||
|
||||
QStandardItem *QgsAttributeTypeDialog::currentItem() const
|
||||
{
|
||||
QStandardItemModel *widgetTypeModel = qobject_cast<QStandardItemModel *>( mWidgetTypeComboBox->model() );
|
||||
|
||||
@ -271,6 +271,24 @@ class GUI_EXPORT QgsAttributeTypeDialog : public QWidget, private Ui::QgsAttribu
|
||||
*/
|
||||
void setDuplicatePolicy( Qgis::FieldDuplicatePolicy policy );
|
||||
|
||||
/**
|
||||
* Returns the field's merge policy.
|
||||
*
|
||||
* \see setMergePolicy()
|
||||
*
|
||||
* \since QGIS 3.44
|
||||
*/
|
||||
Qgis::FieldDomainMergePolicy mergePolicy() const;
|
||||
|
||||
/**
|
||||
* Sets the field's merge policy.
|
||||
*
|
||||
* \see mergePolicy()
|
||||
*
|
||||
* \since QGIS 3.44
|
||||
*/
|
||||
void setMergePolicy( Qgis::FieldDomainMergePolicy policy );
|
||||
|
||||
private slots:
|
||||
|
||||
/**
|
||||
@ -285,6 +303,8 @@ class GUI_EXPORT QgsAttributeTypeDialog : public QWidget, private Ui::QgsAttribu
|
||||
|
||||
void updateDuplicatePolicyLabel();
|
||||
|
||||
void updateMergePolicyLabel();
|
||||
|
||||
private:
|
||||
QgsVectorLayer *mLayer = nullptr;
|
||||
int mFieldIdx;
|
||||
|
||||
@ -343,6 +343,7 @@ void QgsAttributesFormProperties::loadAttributeTypeDialogFromConfiguration( cons
|
||||
mAttributeTypeDialog->setUniqueEnforced( constraints.constraintStrength( QgsFieldConstraints::ConstraintUnique ) == QgsFieldConstraints::ConstraintStrengthHard );
|
||||
mAttributeTypeDialog->setSplitPolicy( config.mSplitPolicy );
|
||||
mAttributeTypeDialog->setDuplicatePolicy( config.mDuplicatePolicy );
|
||||
mAttributeTypeDialog->setMergePolicy( config.mMergePolicy );
|
||||
|
||||
QgsFieldConstraints::Constraints providerConstraints = QgsFieldConstraints::Constraints();
|
||||
if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) == QgsFieldConstraints::ConstraintOriginProvider )
|
||||
@ -417,6 +418,7 @@ void QgsAttributesFormProperties::storeAttributeTypeDialog()
|
||||
cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
|
||||
cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
|
||||
cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
|
||||
cfg.mMergePolicy = mAttributeTypeDialog->mergePolicy();
|
||||
|
||||
const int fieldIndex = mAttributeTypeDialog->fieldIdx();
|
||||
mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
|
||||
@ -1047,6 +1049,7 @@ void QgsAttributesFormProperties::apply()
|
||||
mLayer->setFieldAlias( idx, cfg.mAlias );
|
||||
mLayer->setFieldSplitPolicy( idx, cfg.mSplitPolicy );
|
||||
mLayer->setFieldDuplicatePolicy( idx, cfg.mDuplicatePolicy );
|
||||
mLayer->setFieldMergePolicy( idx, cfg.mMergePolicy );
|
||||
}
|
||||
|
||||
// tabs and groups
|
||||
@ -1118,6 +1121,7 @@ QgsAttributesFormProperties::FieldConfig::FieldConfig( QgsVectorLayer *layer, in
|
||||
mEditorWidgetConfig = setup.config();
|
||||
mSplitPolicy = layer->fields().at( idx ).splitPolicy();
|
||||
mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
|
||||
mMergePolicy = layer->fields().at( idx ).mergePolicy();
|
||||
}
|
||||
|
||||
QgsAttributesFormProperties::FieldConfig::operator QVariant()
|
||||
@ -2117,6 +2121,11 @@ void QgsAttributesFormProperties::copyWidgetConfiguration()
|
||||
duplicatePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) );
|
||||
documentElement.appendChild( duplicatePolicyElement );
|
||||
|
||||
// Merge policy
|
||||
QDomElement mergePolicyElement = doc.createElement( QStringLiteral( "mergePolicy" ) );
|
||||
mergePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.mergePolicy() ) );
|
||||
documentElement.appendChild( mergePolicyElement );
|
||||
|
||||
// Default expressions
|
||||
QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) );
|
||||
defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() );
|
||||
@ -2262,6 +2271,14 @@ void QgsAttributesFormProperties::pasteWidgetConfiguration()
|
||||
config.mDuplicatePolicy = policy;
|
||||
}
|
||||
|
||||
// Merge policy
|
||||
const QDomElement mergePolicyElement = docElem.firstChildElement( QStringLiteral( "mergePolicy" ) );
|
||||
if ( !mergePolicyElement.isNull() )
|
||||
{
|
||||
const Qgis::FieldDomainMergePolicy policy = qgsEnumKeyToValue( mergePolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainMergePolicy::DefaultValue );
|
||||
config.mMergePolicy = policy;
|
||||
}
|
||||
|
||||
// Default expressions
|
||||
const QDomElement defaultElement = docElem.firstChildElement( QStringLiteral( "default" ) );
|
||||
if ( !defaultElement.isNull() )
|
||||
|
||||
@ -376,6 +376,7 @@ class GUI_EXPORT QgsAttributesFormProperties : public QWidget, public QgsExpress
|
||||
QString mComment;
|
||||
Qgis::FieldDomainSplitPolicy mSplitPolicy = Qgis::FieldDomainSplitPolicy::Duplicate;
|
||||
Qgis::FieldDuplicatePolicy mDuplicatePolicy = Qgis::FieldDuplicatePolicy::Duplicate;
|
||||
Qgis::FieldDomainMergePolicy mMergePolicy = Qgis::FieldDomainMergePolicy::DefaultValue;
|
||||
|
||||
operator QVariant();
|
||||
};
|
||||
|
||||
@ -284,19 +284,6 @@
|
||||
<string>Policies</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="mSplitPolicyComboBox"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="mDuplicatePolicyLabel">
|
||||
<property name="text">
|
||||
<string>When duplicating features</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="mDuplicatePolicyComboBox"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="mSplitPolicyLabel">
|
||||
<property name="text">
|
||||
@ -304,6 +291,9 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="mDuplicatePolicyComboBox"/>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="4">
|
||||
<widget class="QLabel" name="mDuplicatePolicyDescriptionLabel">
|
||||
<property name="text">
|
||||
@ -314,6 +304,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="mMergePolicyComboBox"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="mMergePolicyLabel">
|
||||
<property name="text">
|
||||
<string>When merging features</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="mDuplicatePolicyLabel">
|
||||
<property name="text">
|
||||
<string>When duplicating features</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="mSplitPolicyComboBox"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="4">
|
||||
<widget class="QLabel" name="mSplitPolicyDescriptionLabel">
|
||||
<property name="text">
|
||||
@ -324,6 +334,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="4">
|
||||
<widget class="QLabel" name="mMergePolicyDescriptionLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -220,6 +220,71 @@ class TestQgsMergeattributesDialog : public QgsTest
|
||||
// QVariant gets turned into default value while saving the layer
|
||||
QCOMPARE( dialog.mergedAttributes(), QgsAttributes() << 1 << QVariant() );
|
||||
}
|
||||
|
||||
void testMergePolicies()
|
||||
{
|
||||
// Create test layer
|
||||
QgsVectorFileWriter::SaveVectorOptions options;
|
||||
QgsVectorLayer ml( "LineString", "test", "memory" );
|
||||
QVERIFY( ml.isValid() );
|
||||
|
||||
QgsField defaultValueField( QStringLiteral( "defaultValue" ), QMetaType::Type::Int );
|
||||
QgsField sumField( QStringLiteral( "sum" ), QMetaType::Type::Int );
|
||||
QgsField geometryWeightedField( QStringLiteral( "geometryWeighted" ), QMetaType::Type::Double );
|
||||
QgsField largestGeometryField( QStringLiteral( "largestGeometry" ), QMetaType::Type::QString );
|
||||
QgsField minimumValueField( QStringLiteral( "minimumValue" ), QMetaType::Type::Int );
|
||||
QgsField maximumValueField( QStringLiteral( "maximumValue" ), QMetaType::Type::Int );
|
||||
QgsField skipAttributeField( QStringLiteral( "skipAttribute" ), QMetaType::Type::Int );
|
||||
QgsField unsetField( QStringLiteral( "unsetField" ), QMetaType::Type::Int );
|
||||
|
||||
QVERIFY( ml.dataProvider()->addAttributes( { defaultValueField, sumField, geometryWeightedField, largestGeometryField, minimumValueField, maximumValueField, skipAttributeField, unsetField } ) );
|
||||
ml.updateFields();
|
||||
|
||||
// set policies
|
||||
ml.setFieldMergePolicy( 0, Qgis::FieldDomainMergePolicy::DefaultValue );
|
||||
ml.setFieldMergePolicy( 1, Qgis::FieldDomainMergePolicy::Sum );
|
||||
ml.setFieldMergePolicy( 2, Qgis::FieldDomainMergePolicy::GeometryWeighted );
|
||||
ml.setFieldMergePolicy( 3, Qgis::FieldDomainMergePolicy::LargestGeometry );
|
||||
ml.setFieldMergePolicy( 4, Qgis::FieldDomainMergePolicy::MinimumValue );
|
||||
ml.setFieldMergePolicy( 5, Qgis::FieldDomainMergePolicy::MaximumValue );
|
||||
ml.setFieldMergePolicy( 6, Qgis::FieldDomainMergePolicy::SetToNull );
|
||||
ml.setFieldMergePolicy( 7, Qgis::FieldDomainMergePolicy::UnsetField );
|
||||
|
||||
// verify that policies have been correctly set
|
||||
|
||||
QCOMPARE( ml.fields().field( 0 ).mergePolicy(), Qgis::FieldDomainMergePolicy::DefaultValue );
|
||||
QCOMPARE( ml.fields().field( 1 ).mergePolicy(), Qgis::FieldDomainMergePolicy::Sum );
|
||||
QCOMPARE( ml.fields().field( 2 ).mergePolicy(), Qgis::FieldDomainMergePolicy::GeometryWeighted );
|
||||
QCOMPARE( ml.fields().field( 3 ).mergePolicy(), Qgis::FieldDomainMergePolicy::LargestGeometry );
|
||||
QCOMPARE( ml.fields().field( 4 ).mergePolicy(), Qgis::FieldDomainMergePolicy::MinimumValue );
|
||||
QCOMPARE( ml.fields().field( 5 ).mergePolicy(), Qgis::FieldDomainMergePolicy::MaximumValue );
|
||||
QCOMPARE( ml.fields().field( 6 ).mergePolicy(), Qgis::FieldDomainMergePolicy::SetToNull );
|
||||
QCOMPARE( ml.fields().field( 7 ).mergePolicy(), Qgis::FieldDomainMergePolicy::UnsetField );
|
||||
|
||||
// Create features
|
||||
QgsFeature f1( ml.fields(), 1 );
|
||||
f1.setAttributes( QVector<QVariant>() << 10 << 200 << 7.5 << QStringLiteral( "smaller" ) << 10 << -10 << 0 << 20 );
|
||||
f1.setGeometry( QgsGeometry::fromWkt( "LINESTRING(10 0, 15 0)" ) );
|
||||
QVERIFY( ml.dataProvider()->addFeature( f1 ) );
|
||||
QCOMPARE( ml.featureCount(), 1 );
|
||||
|
||||
QgsFeature f2( ml.fields(), 2 );
|
||||
f2.setAttributes( QVector<QVariant>() << 15 << 100 << 5 << QStringLiteral( "bigger" ) << -10 << 10 << 5 << 12 );
|
||||
f2.setGeometry( QgsGeometry::fromWkt( "LINESTRING(0 0, 10 0)" ) );
|
||||
QVERIFY( ml.dataProvider()->addFeature( f2 ) );
|
||||
QCOMPARE( ml.featureCount(), 2 );
|
||||
|
||||
QgsMergeAttributesDialog dialog1( QgsFeatureList() << f1 << f2, &ml, mQgisApp->mapCanvas() );
|
||||
|
||||
QCOMPARE( dialog1.mergedAttributes().at( 0 ).toInt(), 10 );
|
||||
QCOMPARE( dialog1.mergedAttributes().at( 1 ).toInt(), 300 );
|
||||
QVERIFY( qgsDoubleNear( dialog1.mergedAttributes().at( 2 ).toDouble(), 5.83333, 0.00001 ) );
|
||||
QCOMPARE( dialog1.mergedAttributes().at( 3 ).toString(), QStringLiteral( "bigger" ) );
|
||||
QCOMPARE( dialog1.mergedAttributes().at( 4 ).toInt(), -10 );
|
||||
QCOMPARE( dialog1.mergedAttributes().at( 5 ).toInt(), 10 );
|
||||
QVERIFY( !dialog1.mergedAttributes().at( 6 ).isValid() );
|
||||
QCOMPARE( dialog1.mergedAttributes().at( 7 ).toInt(), 20 );
|
||||
}
|
||||
};
|
||||
|
||||
QGSTEST_MAIN( TestQgsMergeattributesDialog )
|
||||
|
||||
@ -209,6 +209,9 @@ void TestQgsField::gettersSetters()
|
||||
field.setDuplicatePolicy( Qgis::FieldDuplicatePolicy::UnsetField );
|
||||
QCOMPARE( field.duplicatePolicy(), Qgis::FieldDuplicatePolicy::UnsetField );
|
||||
|
||||
field.setMergePolicy( Qgis::FieldDomainMergePolicy::GeometryWeighted );
|
||||
QCOMPARE( field.mergePolicy(), Qgis::FieldDomainMergePolicy::GeometryWeighted );
|
||||
|
||||
field.setMetadata( { { static_cast<int>( Qgis::FieldMetadataProperty::GeometryCrs ), QStringLiteral( "abc" ) }, { 2, 5 } } );
|
||||
QMap<int, QVariant> expected { { static_cast<int>( Qgis::FieldMetadataProperty::GeometryCrs ), QStringLiteral( "abc" ) }, { 2, 5 } };
|
||||
QCOMPARE( field.metadata(), expected );
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user