Skip to content

Commit

Permalink
Merge pull request #8125 from elpaso/bugfix-20020-paste-geometry-coll…
Browse files Browse the repository at this point in the history
…apsed-features

Show a warning when pasted feature geometry collapsed
  • Loading branch information
elpaso committed Oct 9, 2018
2 parents b4a2218 + dbfbcca commit 795c27f
Showing 1 changed file with 31 additions and 59 deletions.
90 changes: 31 additions & 59 deletions src/app/qgisapp.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -8634,69 +8634,36 @@ void QgisApp::pasteFromClipboard( QgsMapLayer *destinationLayer )
pasteVectorLayer->beginEditCommand( tr( "Features pasted" ) ); pasteVectorLayer->beginEditCommand( tr( "Features pasted" ) );
QgsFeatureList features = clipboard()->transformedCopyOf( pasteVectorLayer->crs(), pasteVectorLayer->fields() ); QgsFeatureList features = clipboard()->transformedCopyOf( pasteVectorLayer->crs(), pasteVectorLayer->fields() );
int nTotalFeatures = features.count(); 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(); QgsExpressionContext context = pasteVectorLayer->createExpressionContext();



QgsFeatureList compatibleFeatures( QgsVectorLayerUtils::makeFeaturesCompatible( features, pasteVectorLayer ) );
QgsFeatureList newFeatures; QgsFeatureList newFeatures;
QgsFeatureList::const_iterator featureIt = features.constBegin(); // Count collapsed geometries
while ( featureIt != features.constEnd() ) int invalidGeometriesCount = 0;
{
QgsAttributes srcAttr = featureIt->attributes();
QgsAttributeMap dstAttr;


for ( int src = 0; src < srcAttr.count(); ++src ) for ( const auto &feature : qgis::as_const( compatibleFeatures ) )
{ {
int dst = remap.value( src, -1 );
if ( dst < 0 )
continue;


dstAttr[ dst ] = srcAttr.at( src ); QgsGeometry geom = feature.geometry();
}


QgsGeometry geom = featureIt->geometry(); if ( !( geom.isEmpty() || geom.isNull( ) ) )
if ( featureIt->hasGeometry() )
{ {
// 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 // avoid intersection if enabled in digitize settings
geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); 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 // now create new feature using pasted feature as a template. This automatically handles default
// values and field constraints // values and field constraints
newFeatures << QgsVectorLayerUtils::createFeature( pasteVectorLayer, geom, dstAttr, &context ); newFeatures << QgsVectorLayerUtils::createFeature( pasteVectorLayer, geom, attrMap, &context );
++featureIt;
} }

pasteVectorLayer->addFeatures( newFeatures ); pasteVectorLayer->addFeatures( newFeatures );
QgsFeatureIds newIds; QgsFeatureIds newIds;
for ( const QgsFeature &f : qgis::as_const( newFeatures ) ) for ( const QgsFeature &f : qgis::as_const( newFeatures ) )
Expand All @@ -8710,26 +8677,31 @@ void QgisApp::pasteFromClipboard( QgsMapLayer *destinationLayer )
pasteVectorLayer->updateExtents(); pasteVectorLayer->updateExtents();


int nCopiedFeatures = features.count(); int nCopiedFeatures = features.count();
Qgis::MessageLevel level = ( nCopiedFeatures == 0 || nCopiedFeatures < nTotalFeatures || invalidGeometriesCount > 0 ) ? Qgis::Warning : Qgis::Info;
QString message;
if ( nCopiedFeatures == 0 ) if ( nCopiedFeatures == 0 )
{ {
messageBar()->pushMessage( tr( "Paste features" ), message = tr( "No features could be successfully pasted." );
tr( "no features could be successfully pasted." ),
Qgis::Warning, messageTimeout() );

} }
else if ( nCopiedFeatures == nTotalFeatures ) else if ( nCopiedFeatures == nTotalFeatures )
{ {
messageBar()->pushMessage( tr( "Paste features" ), message = tr( "%1 features were successfully pasted." ).arg( nCopiedFeatures );
tr( "%1 features were successfully pasted." ).arg( nCopiedFeatures ),
Qgis::Info, messageTimeout() );
} }
else else
{ {
messageBar()->pushMessage( tr( "Paste features" ), message = tr( "%1 of %2 features could be successfully pasted." ).arg( nCopiedFeatures ).arg( nTotalFeatures );
tr( "%1 of %2 features could be successfully pasted." ).arg( nCopiedFeatures ).arg( nTotalFeatures ),
Qgis::Warning, messageTimeout() );
} }


// 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(); pasteVectorLayer->triggerRepaint();
} }


Expand Down

0 comments on commit 795c27f

Please sign in to comment.