|
@@ -8634,69 +8634,36 @@ void QgisApp::pasteFromClipboard( QgsMapLayer *destinationLayer ) |
|
|
pasteVectorLayer->beginEditCommand( tr( "Features pasted" ) ); |
|
|
QgsFeatureList features = clipboard()->transformedCopyOf( pasteVectorLayer->crs(), pasteVectorLayer->fields() ); |
|
|
int nTotalFeatures = features.count(); |
|
|
|
|
|
QHash<int, int> remap; |
|
|
QgsFields fields = clipboard()->fields(); |
|
|
for ( int idx = 0; idx < fields.count(); ++idx ) |
|
|
{ |
|
|
int dst = pasteVectorLayer->fields().lookupField( fields.at( idx ).name() ); |
|
|
if ( dst < 0 ) |
|
|
continue; |
|
|
|
|
|
remap.insert( idx, dst ); |
|
|
} |
|
|
|
|
|
QgsExpressionContext context = pasteVectorLayer->createExpressionContext(); |
|
|
|
|
|
|
|
|
QgsFeatureList compatibleFeatures( QgsVectorLayerUtils::makeFeaturesCompatible( features, pasteVectorLayer ) ); |
|
|
QgsFeatureList newFeatures; |
|
|
QgsFeatureList::const_iterator featureIt = features.constBegin(); |
|
|
while ( featureIt != features.constEnd() ) |
|
|
{ |
|
|
QgsAttributes srcAttr = featureIt->attributes(); |
|
|
QgsAttributeMap dstAttr; |
|
|
// Count collapsed geometries |
|
|
int invalidGeometriesCount = 0; |
|
|
|
|
|
for ( int src = 0; src < srcAttr.count(); ++src ) |
|
|
{ |
|
|
int dst = remap.value( src, -1 ); |
|
|
if ( dst < 0 ) |
|
|
continue; |
|
|
for ( const auto &feature : qgis::as_const( compatibleFeatures ) ) |
|
|
{ |
|
|
|
|
|
dstAttr[ dst ] = srcAttr.at( src ); |
|
|
} |
|
|
QgsGeometry geom = feature.geometry(); |
|
|
|
|
|
QgsGeometry geom = featureIt->geometry(); |
|
|
if ( featureIt->hasGeometry() ) |
|
|
if ( !( geom.isEmpty() || geom.isNull( ) ) ) |
|
|
{ |
|
|
// convert geometry to match destination layer |
|
|
QgsWkbTypes::GeometryType destType = pasteVectorLayer->geometryType(); |
|
|
bool destIsMulti = QgsWkbTypes::isMultiType( pasteVectorLayer->wkbType() ); |
|
|
if ( pasteVectorLayer->dataProvider() && |
|
|
!pasteVectorLayer->dataProvider()->doesStrictFeatureTypeCheck() ) |
|
|
{ |
|
|
// force destination to multi if provider doesn't do a feature strict check |
|
|
destIsMulti = true; |
|
|
} |
|
|
|
|
|
if ( destType != QgsWkbTypes::UnknownGeometry ) |
|
|
{ |
|
|
QgsGeometry newGeometry = geom.convertToType( destType, destIsMulti ); |
|
|
if ( newGeometry.isNull() ) |
|
|
{ |
|
|
continue; |
|
|
} |
|
|
geom = newGeometry; |
|
|
} |
|
|
// avoid intersection if enabled in digitize settings |
|
|
geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); |
|
|
// Count collapsed geometries |
|
|
if ( geom.isEmpty() || geom.isNull( ) ) |
|
|
invalidGeometriesCount++; |
|
|
} |
|
|
|
|
|
QgsAttributeMap attrMap; |
|
|
for ( int i = 0; i < feature.fields().count(); i++ ) |
|
|
{ |
|
|
attrMap[i] = feature.attribute( i ); |
|
|
} |
|
|
// now create new feature using pasted feature as a template. This automatically handles default |
|
|
// values and field constraints |
|
|
newFeatures << QgsVectorLayerUtils::createFeature( pasteVectorLayer, geom, dstAttr, &context ); |
|
|
++featureIt; |
|
|
newFeatures << QgsVectorLayerUtils::createFeature( pasteVectorLayer, geom, attrMap, &context ); |
|
|
} |
|
|
|
|
|
pasteVectorLayer->addFeatures( newFeatures ); |
|
|
QgsFeatureIds newIds; |
|
|
for ( const QgsFeature &f : qgis::as_const( newFeatures ) ) |
|
@@ -8710,26 +8677,31 @@ void QgisApp::pasteFromClipboard( QgsMapLayer *destinationLayer ) |
|
|
pasteVectorLayer->updateExtents(); |
|
|
|
|
|
int nCopiedFeatures = features.count(); |
|
|
Qgis::MessageLevel level = ( nCopiedFeatures == 0 || nCopiedFeatures < nTotalFeatures || invalidGeometriesCount > 0 ) ? Qgis::Warning : Qgis::Info; |
|
|
QString message; |
|
|
if ( nCopiedFeatures == 0 ) |
|
|
{ |
|
|
messageBar()->pushMessage( tr( "Paste features" ), |
|
|
tr( "no features could be successfully pasted." ), |
|
|
Qgis::Warning, messageTimeout() ); |
|
|
|
|
|
message = tr( "No features could be successfully pasted." ); |
|
|
} |
|
|
else if ( nCopiedFeatures == nTotalFeatures ) |
|
|
{ |
|
|
messageBar()->pushMessage( tr( "Paste features" ), |
|
|
tr( "%1 features were successfully pasted." ).arg( nCopiedFeatures ), |
|
|
Qgis::Info, messageTimeout() ); |
|
|
message = tr( "%1 features were successfully pasted." ).arg( nCopiedFeatures ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
messageBar()->pushMessage( tr( "Paste features" ), |
|
|
tr( "%1 of %2 features could be successfully pasted." ).arg( nCopiedFeatures ).arg( nTotalFeatures ), |
|
|
Qgis::Warning, messageTimeout() ); |
|
|
message = tr( "%1 of %2 features could be successfully pasted." ).arg( nCopiedFeatures ).arg( nTotalFeatures ); |
|
|
} |
|
|
|
|
|
// warn the user if the pasted features have invalid geometries |
|
|
if ( invalidGeometriesCount > 0 ) |
|
|
message += invalidGeometriesCount == 1 ? tr( " Geometry collapsed due to intersection avoidance." ) : |
|
|
tr( "%1 geometries collapsed due to intersection avoidance." ) |
|
|
.arg( invalidGeometriesCount ); |
|
|
|
|
|
messageBar()->pushMessage( tr( "Paste features" ), |
|
|
message, |
|
|
level, messageTimeout() ); |
|
|
|
|
|
pasteVectorLayer->triggerRepaint(); |
|
|
} |
|
|
|
|
|