mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Port remaining map tools, paste features, offline editing
to use new constraint/default clause handling methods
This commit is contained in:
parent
091f488985
commit
631bd48aa0
@ -241,6 +241,7 @@
|
||||
#include "qgsvirtuallayerdefinitionutils.h"
|
||||
#include "qgstransaction.h"
|
||||
#include "qgstransactiongroup.h"
|
||||
#include "qgsvectorlayerutils.h"
|
||||
|
||||
#include "qgssublayersdialog.h"
|
||||
#include "ogr/qgsopenvectorlayerdialog.h"
|
||||
@ -7484,7 +7485,6 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
|
||||
|
||||
QHash<int, int> remap;
|
||||
QgsFields fields = clipboard()->fields();
|
||||
QgsAttributeList pkAttrList = pasteVectorLayer->pkAttributeList();
|
||||
for ( int idx = 0; idx < fields.count(); ++idx )
|
||||
{
|
||||
int dst = pasteVectorLayer->fields().lookupField( fields.at( idx ).name() );
|
||||
@ -7495,33 +7495,14 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
|
||||
}
|
||||
|
||||
QgsExpressionContext context = pasteVectorLayer->createExpressionContext();
|
||||
int dstAttrCount = pasteVectorLayer->fields().count();
|
||||
|
||||
QgsFeatureList::iterator featureIt = features.begin();
|
||||
while ( featureIt != features.end() )
|
||||
QgsFeatureIds newIds;
|
||||
|
||||
QgsFeatureList::const_iterator featureIt = features.constBegin();
|
||||
while ( featureIt != features.constEnd() )
|
||||
{
|
||||
QgsAttributes srcAttr = featureIt->attributes();
|
||||
QgsAttributes dstAttr( dstAttrCount );
|
||||
|
||||
// pre-initialized with default values
|
||||
for ( int dst = 0; dst < dstAttr.count(); ++dst )
|
||||
{
|
||||
QVariant defVal;
|
||||
if ( !pasteVectorLayer->defaultValueExpression( dst ).isEmpty() )
|
||||
{
|
||||
// client side default expression set - use this in preference to provider default
|
||||
defVal = pasteVectorLayer->defaultValue( dst, *featureIt, &context );
|
||||
}
|
||||
else
|
||||
{
|
||||
defVal = pasteVectorLayer->dataProvider()->defaultValueClause( dst );
|
||||
}
|
||||
|
||||
if ( defVal.isValid() && !defVal.isNull() )
|
||||
{
|
||||
dstAttr[ dst ] = defVal;
|
||||
}
|
||||
}
|
||||
QgsAttributeMap dstAttr;
|
||||
|
||||
for ( int src = 0; src < srcAttr.count(); ++src )
|
||||
{
|
||||
@ -7529,21 +7510,10 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
|
||||
if ( dst < 0 )
|
||||
continue;
|
||||
|
||||
// don't overwrite default value for primary key fields if it's NOT NULL
|
||||
// or for spatialite layers
|
||||
if ( pkAttrList.contains( dst ) )
|
||||
{
|
||||
if ( !dstAttr.at( dst ).isNull() )
|
||||
continue;
|
||||
else if ( pasteVectorLayer->providerType() == QLatin1String( "spatialite" ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
dstAttr[ dst ] = srcAttr.at( src );
|
||||
}
|
||||
|
||||
featureIt->setAttributes( dstAttr );
|
||||
|
||||
QgsGeometry geom = featureIt->geometry();
|
||||
if ( featureIt->hasGeometry() )
|
||||
{
|
||||
// convert geometry to match destination layer
|
||||
@ -7558,25 +7528,29 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
|
||||
|
||||
if ( destType != QgsWkbTypes::UnknownGeometry )
|
||||
{
|
||||
QgsGeometry newGeometry = featureIt->geometry().convertToType( destType, destIsMulti );
|
||||
QgsGeometry newGeometry = geom.convertToType( destType, destIsMulti );
|
||||
if ( newGeometry.isEmpty() )
|
||||
{
|
||||
featureIt = features.erase( featureIt );
|
||||
continue;
|
||||
}
|
||||
featureIt->setGeometry( newGeometry );
|
||||
geom = newGeometry;
|
||||
}
|
||||
// avoid intersection if enabled in digitize settings
|
||||
QgsGeometry g = featureIt->geometry();
|
||||
g.avoidIntersections();
|
||||
featureIt->setGeometry( g );
|
||||
geom.avoidIntersections();
|
||||
}
|
||||
|
||||
// now create new feature using pasted feature as a template. This automatically handles default
|
||||
// values and field constraints
|
||||
QgsFeature newFeature = QgsVectorLayerUtils::createFeature( pasteVectorLayer, geom, dstAttr, &context );
|
||||
pasteVectorLayer->addFeature( newFeature, false );
|
||||
newIds << newFeature.id();
|
||||
|
||||
++featureIt;
|
||||
}
|
||||
|
||||
pasteVectorLayer->addFeatures( features );
|
||||
pasteVectorLayer->selectByIds( newIds );
|
||||
pasteVectorLayer->endEditCommand();
|
||||
pasteVectorLayer->updateExtents();
|
||||
|
||||
int nCopiedFeatures = features.count();
|
||||
if ( nCopiedFeatures == 0 )
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsactionmanager.h"
|
||||
#include "qgsaction.h"
|
||||
#include "qgsvectorlayerutils.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QSettings>
|
||||
@ -144,53 +145,34 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes, boo
|
||||
if ( !mLayer || !mLayer->isEditable() )
|
||||
return false;
|
||||
|
||||
QgsVectorDataProvider *provider = mLayer->dataProvider();
|
||||
QgsAttributeList pkAttrList = mLayer->pkAttributeList();
|
||||
|
||||
QSettings settings;
|
||||
bool reuseLastValues = settings.value( QStringLiteral( "/qgis/digitizing/reuseLastValues" ), false ).toBool();
|
||||
QgsDebugMsg( QString( "reuseLastValues: %1" ).arg( reuseLastValues ) );
|
||||
|
||||
QgsExpressionContext context = mLayer->createExpressionContext();
|
||||
QgsFields fields = mLayer->fields();
|
||||
QgsAttributeMap initialAttributeValues;
|
||||
|
||||
// add the fields to the QgsFeature
|
||||
const QgsFields& fields = mLayer->fields();
|
||||
mFeature->initAttributes( fields.count() );
|
||||
for ( int idx = 0; idx < fields.count(); ++idx )
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if ( defaultAttributes.contains( idx ) )
|
||||
{
|
||||
v = defaultAttributes.value( idx );
|
||||
initialAttributeValues.insert( idx, defaultAttributes.value( idx ) );
|
||||
}
|
||||
else if ( !mLayer->defaultValueExpression( idx ).isEmpty() )
|
||||
else if ( reuseLastValues && sLastUsedValues.contains( mLayer ) && sLastUsedValues[ mLayer ].contains( idx ) )
|
||||
{
|
||||
// client side default expression set - use this in preference to reusing last value
|
||||
v = mLayer->defaultValue( idx, *mFeature, &context );
|
||||
initialAttributeValues.insert( idx, sLastUsedValues[ mLayer ][idx] );
|
||||
}
|
||||
else if ( reuseLastValues && sLastUsedValues.contains( mLayer ) && sLastUsedValues[ mLayer ].contains( idx ) && !pkAttrList.contains( idx ) )
|
||||
{
|
||||
v = sLastUsedValues[ mLayer ][idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
QVariant defaultLiteral = mLayer->dataProvider()->defaultValue( idx );
|
||||
if ( defaultLiteral.isValid() )
|
||||
{
|
||||
v = defaultLiteral;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString defaultClause = provider->defaultValueClause( idx );
|
||||
if ( !defaultClause.isEmpty() )
|
||||
v = defaultClause;
|
||||
}
|
||||
}
|
||||
|
||||
mFeature->setAttribute( idx, v );
|
||||
}
|
||||
|
||||
// create new feature template - this will initialise the attributes to valid values, handling default
|
||||
// values and field constraints
|
||||
QgsExpressionContext context = mLayer->createExpressionContext();
|
||||
QgsFeature newFeature = QgsVectorLayerUtils::createFeature( mLayer, mFeature->geometry(), initialAttributeValues,
|
||||
&context );
|
||||
*mFeature = newFeature;
|
||||
|
||||
//show the dialog to enter attribute values
|
||||
//only show if enabled in settings and layer has fields
|
||||
bool isDisabledAttributeValuesDlg = ( fields.count() == 0 ) || settings.value( QStringLiteral( "/qgis/digitizing/disable_enter_attribute_values_dialog" ), false ).toBool();
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsattributedialog.h"
|
||||
#include "qgisapp.h"
|
||||
|
||||
#include "qgsvectorlayerutils.h"
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <limits>
|
||||
@ -139,26 +139,26 @@ void QgsMapToolFillRing::cadCanvasReleaseEvent( QgsMapMouseEvent * e )
|
||||
bBox.setXMaximum( xMax );
|
||||
bBox.setYMaximum( yMax );
|
||||
|
||||
QgsExpressionContext context = vlayer->createExpressionContext();
|
||||
|
||||
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterFid( modifiedFid ) );
|
||||
|
||||
QgsFeature f;
|
||||
if ( fit.nextFeature( f ) )
|
||||
{
|
||||
//create QgsFeature with wkb representation
|
||||
QgsFeature* ft = new QgsFeature( vlayer->fields(), 0 );
|
||||
|
||||
QgsGeometry g = QgsGeometry::fromPolygon( QgsPolygon() << pointList.toVector() );
|
||||
ft->setGeometry( g );
|
||||
ft->setAttributes( f.attributes() );
|
||||
|
||||
//create QgsFeature with wkb representation
|
||||
QgsFeature ft = QgsVectorLayerUtils::createFeature( vlayer, g, f.attributes().toMap(), &context );
|
||||
|
||||
bool res = false;
|
||||
if ( QApplication::keyboardModifiers() == Qt::ControlModifier )
|
||||
{
|
||||
res = vlayer->addFeature( *ft );
|
||||
res = vlayer->addFeature( ft );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, ft, false, nullptr, true );
|
||||
QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, &ft, false, nullptr, true );
|
||||
dialog->setMode( QgsAttributeForm::AddFeatureMode );
|
||||
res = dialog->exec(); // will also add the feature
|
||||
}
|
||||
@ -169,7 +169,6 @@ void QgsMapToolFillRing::cadCanvasReleaseEvent( QgsMapMouseEvent * e )
|
||||
}
|
||||
else
|
||||
{
|
||||
delete ft;
|
||||
vlayer->destroyEditCommand();
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,6 @@ void QgsMergeAttributesDialog::createTableWidgetContents()
|
||||
|
||||
//create combo boxes and insert attribute names
|
||||
mFields = mVectorLayer->fields();
|
||||
QSet<int> pkAttrList = mVectorLayer->pkAttributeList().toSet();
|
||||
|
||||
int col = 0;
|
||||
mHiddenAttributes.clear();
|
||||
@ -127,7 +126,7 @@ void QgsMergeAttributesDialog::createTableWidgetContents()
|
||||
mTableWidget->setColumnCount( col + 1 );
|
||||
|
||||
QComboBox *cb = createMergeComboBox( mFields.at( idx ).type() );
|
||||
if ( pkAttrList.contains( idx ) )
|
||||
if ( mFields.at( idx ).constraints().constraints() & QgsFieldConstraints::ConstraintUnique )
|
||||
{
|
||||
cb->setCurrentIndex( cb->findData( "skip" ) );
|
||||
}
|
||||
@ -413,15 +412,17 @@ void QgsMergeAttributesDialog::on_mFromSelectedPushButton_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
QSet<int> pkAttributes = mVectorLayer->pkAttributeList().toSet();
|
||||
for ( int i = 0; i < mTableWidget->columnCount(); ++i )
|
||||
{
|
||||
if ( pkAttributes.contains( i ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
QComboBox* currentComboBox = qobject_cast<QComboBox *>( mTableWidget->cellWidget( 0, i ) );
|
||||
if ( currentComboBox )
|
||||
if ( !currentComboBox )
|
||||
continue;
|
||||
|
||||
if ( mVectorLayer->fields().at( i ).constraints().constraints() & QgsFieldConstraints::ConstraintUnique )
|
||||
{
|
||||
currentComboBox->setCurrentIndex( currentComboBox->findData( "skip" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
currentComboBox->setCurrentIndex( currentComboBox->findData( QStringLiteral( "f%1" ).arg( FID_TO_STRING( featureId ) ) ) );
|
||||
}
|
||||
@ -528,8 +529,6 @@ QgsAttributes QgsMergeAttributesDialog::mergedAttributes() const
|
||||
return QgsAttributes();
|
||||
}
|
||||
|
||||
QgsExpressionContext context = mVectorLayer->createExpressionContext();
|
||||
|
||||
int widgetIndex = 0;
|
||||
QgsAttributes results( mFields.count() );
|
||||
for ( int fieldIdx = 0; fieldIdx < mFields.count(); ++fieldIdx )
|
||||
@ -537,12 +536,7 @@ QgsAttributes QgsMergeAttributesDialog::mergedAttributes() const
|
||||
if ( mHiddenAttributes.contains( fieldIdx ) )
|
||||
{
|
||||
//hidden attribute, set to default value
|
||||
if ( !mVectorLayer->defaultValueExpression( fieldIdx ).isEmpty() )
|
||||
results[fieldIdx] = mVectorLayer->defaultValue( fieldIdx, mFeatureList.at( 0 ), &context );
|
||||
else if ( mVectorLayer->dataProvider() )
|
||||
results[fieldIdx] = mVectorLayer->dataProvider()->defaultValueClause( fieldIdx );
|
||||
else
|
||||
results[fieldIdx] = QVariant();
|
||||
results[fieldIdx] = QVariant();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -561,14 +555,6 @@ QgsAttributes QgsMergeAttributesDialog::mergedAttributes() const
|
||||
{
|
||||
results[fieldIdx] = currentItem->data( Qt::DisplayRole );
|
||||
}
|
||||
else if ( !mVectorLayer->defaultValueExpression( fieldIdx ).isEmpty() )
|
||||
{
|
||||
results[fieldIdx] = mVectorLayer->defaultValue( fieldIdx, mFeatureList.at( 0 ), &context );
|
||||
}
|
||||
else if ( mVectorLayer->dataProvider() )
|
||||
{
|
||||
results[fieldIdx] = mVectorLayer->dataProvider()->defaultValueClause( fieldIdx );
|
||||
}
|
||||
widgetIndex++;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "qgsslconnect.h"
|
||||
#include "qgsfeatureiterator.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsvectorlayerutils.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDomDocument>
|
||||
@ -747,21 +748,22 @@ void QgsOfflineEditing::applyAttributesAdded( QgsVectorLayer* remoteLayer, sqlit
|
||||
void QgsOfflineEditing::applyFeaturesAdded( QgsVectorLayer* offlineLayer, QgsVectorLayer* remoteLayer, sqlite3* db, int layerId )
|
||||
{
|
||||
QString sql = QStringLiteral( "SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
|
||||
QList<int> newFeatureIds = sqlQueryInts( db, sql );
|
||||
|
||||
QgsFields remoteFlds = remoteLayer->fields();
|
||||
QList<int> featureIdInts = sqlQueryInts( db, sql );
|
||||
QgsFeatureIds newFeatureIds;
|
||||
Q_FOREACH ( int id, featureIdInts )
|
||||
{
|
||||
newFeatureIds << id;
|
||||
}
|
||||
|
||||
QgsExpressionContext context = remoteLayer->createExpressionContext();
|
||||
|
||||
// get new features from offline layer
|
||||
QgsFeatureList features;
|
||||
for ( int i = 0; i < newFeatureIds.size(); i++ )
|
||||
QgsFeatureIterator it = offlineLayer->getFeatures( QgsFeatureRequest().setFilterFids( newFeatureIds ) );
|
||||
QgsFeature feature;
|
||||
while ( it.nextFeature( feature ) )
|
||||
{
|
||||
QgsFeature feature;
|
||||
if ( offlineLayer->getFeatures( QgsFeatureRequest().setFilterFid( newFeatureIds.at( i ) ) ).nextFeature( feature ) )
|
||||
{
|
||||
features << feature;
|
||||
}
|
||||
features << feature;
|
||||
}
|
||||
|
||||
// copy features to remote layer
|
||||
@ -771,33 +773,18 @@ void QgsOfflineEditing::applyFeaturesAdded( QgsVectorLayer* offlineLayer, QgsVec
|
||||
int newAttrsCount = remoteLayer->fields().count();
|
||||
for ( QgsFeatureList::iterator it = features.begin(); it != features.end(); ++it )
|
||||
{
|
||||
QgsFeature f = *it;
|
||||
|
||||
// NOTE: Spatialite provider ignores position of geometry column
|
||||
// restore gap in QgsAttributeMap if geometry column is not last (WORKAROUND)
|
||||
QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
|
||||
QgsAttributes newAttrs( newAttrsCount );
|
||||
QgsAttributes attrs = f.attributes();
|
||||
QgsAttributes attrs = it->attributes();
|
||||
for ( int it = 0; it < attrs.count(); ++it )
|
||||
{
|
||||
newAttrs[ attrLookup[ it ] ] = attrs.at( it );
|
||||
}
|
||||
|
||||
// try to use default value from the provider
|
||||
// (important especially e.g. for postgis primary key generated from a sequence)
|
||||
for ( int k = 0; k < newAttrs.count(); ++k )
|
||||
{
|
||||
if ( !newAttrs.at( k ).isNull() )
|
||||
continue;
|
||||
|
||||
if ( !remoteLayer->defaultValueExpression( k ).isEmpty() )
|
||||
newAttrs[k] = remoteLayer->defaultValue( k, f, &context );
|
||||
else if ( remoteFlds.fieldOrigin( k ) == QgsFields::OriginProvider )
|
||||
newAttrs[k] = remoteLayer->dataProvider()->defaultValueClause( remoteFlds.fieldOriginIndex( k ) );
|
||||
}
|
||||
|
||||
f.setAttributes( newAttrs );
|
||||
|
||||
// respect constraints and provider default values
|
||||
QgsFeature f = QgsVectorLayerUtils::createFeature( remoteLayer, it->geometry(), newAttrs.toMap(), &context );
|
||||
remoteLayer->addFeature( f, false );
|
||||
|
||||
emit progressUpdated( i++ );
|
||||
|
Loading…
x
Reference in New Issue
Block a user