void QgsMapToolShowHideLabels::showHideLabels( QMouseEvent *e ) { QgsMapLayer *layer = mCanvas->currentLayer(); QgsVectorLayer *vlayer = dynamic_cast<QgsVectorLayer *>( layer ); if ( !vlayer ) return; bool doHide = e->modifiers() & Qt::ShiftModifier; QString editTxt = doHide ? tr( "Hid labels" ) : tr( "Showed labels" ); vlayer->beginEditCommand( editTxt ); bool labelChanged = false; if ( doHide ) { QList<QgsLabelPosition> positions; if ( selectedLabelFeatures( vlayer, positions ) ) { for ( const QgsLabelPosition &pos : qgis::as_const( positions ) ) { if ( showHide( pos, false ) ) labelChanged = true; } } } else { QgsFeatureIds fids; if ( selectedFeatures( vlayer, fids ) ) { for ( const QgsFeatureId &fid : qgis::as_const( fids ) ) { QgsLabelPosition pos; pos.featureId = fid; pos.layerID = vlayer->id(); // we want to show labels... pos.isDiagram = false; if ( showHide( pos, true ) ) labelChanged = true; // ... and diagrams pos.isDiagram = true; if ( showHide( pos, true ) ) labelChanged = true; } } } if ( labelChanged ) { vlayer->endEditCommand(); vlayer->triggerRepaint(); } else { vlayer->destroyEditCommand(); } }
void QgsSymbolLegendNode::checkAll( bool state ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ); if ( !vlayer || !vlayer->renderer() ) return; const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems(); for ( const auto &item : symbolList ) { vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state ); } emit dataChanged(); vlayer->triggerRepaint(); }
void QgsSymbolLegendNode::setSymbol( QgsSymbol *symbol ) { if ( !symbol ) return; QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ); if ( !vlayer || !vlayer->renderer() ) return; mItem.setSymbol( symbol ); vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), symbol ); mPixmap = QPixmap(); emit dataChanged(); vlayer->triggerRepaint(); }
void QgsSymbolLegendNode::setSymbol( QgsSymbol *symbol ) { if ( !symbol ) return; std::unique_ptr< QgsSymbol > s( symbol ); // this method takes ownership of symbol QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ); if ( !vlayer || !vlayer->renderer() ) return; mItem.setSymbol( s.get() ); // doesn't transfer ownership vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), s.release() ); // DOES transfer ownership! mPixmap = QPixmap(); emit dataChanged(); vlayer->triggerRepaint(); }
bool QgsSymbolV2LegendNode::setData( const QVariant& value, int role ) { if ( role != Qt::CheckStateRole ) return false; if ( !mItem.isCheckable() ) return false; QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( mLayerNode->layer() ); if ( !vlayer || !vlayer->rendererV2() ) return false; vlayer->rendererV2()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked ); emit dataChanged(); vlayer->triggerRepaint(); return true; }
void QgsMapToolChangeLabelProperties::applyChanges( const QgsAttributeMap& changes ) { QgsVectorLayer* vlayer = mCurrentLabel.layer; if ( !vlayer ) return; if ( !changes.isEmpty() ) { vlayer->beginEditCommand( tr( "Changed properties for label" ) + QStringLiteral( " '%1'" ).arg( currentLabelText( 24 ) ) ); QgsAttributeMap::const_iterator changeIt = changes.constBegin(); for ( ; changeIt != changes.constEnd(); ++changeIt ) { vlayer->changeAttributeValue( mCurrentLabel.pos.featureId, changeIt.key(), changeIt.value() ); } vlayer->endEditCommand(); vlayer->triggerRepaint(); } }
void QgsGeometryValidationService::fixError( QgsGeometryCheckError *error, int method ) { QgsGeometryCheck::Changes changes; error->check()->fixError( mFeaturePools, error, method, QMap<QString, int>(), changes ); error->setFixed( method ); QgsFeaturePool *featurePool = mFeaturePools.value( error->layerId() ); QgsVectorLayer *layer = nullptr; if ( featurePool ) layer = featurePool->layer(); else { // Some checks don't tell us on which layer they are because they are able to do cross-layer checks. // E.g. the gap check will report in such a way for ( auto layerCheck = mLayerChecks.constBegin(); layerCheck != mLayerChecks.constEnd(); ++layerCheck ) { const QList<std::shared_ptr<QgsGeometryCheckError>> &topologyCheckErrors = layerCheck.value().topologyCheckErrors; for ( const auto &checkError : topologyCheckErrors ) { if ( checkError.get() == error ) { layer = layerCheck.key(); break; } } } } if ( layer ) { layer->triggerRepaint(); emit topologyErrorUpdated( layer, error ); } }
void QgsMapToolRotateLabel::canvasReleaseEvent( QgsMapMouseEvent* e ) { Q_UNUSED( e ); if ( !mLabelRubberBand ) //no rubber band created (most likely because the current label cannot be rotated ) { return; } deleteRubberBands(); delete mRotationItem; mRotationItem = nullptr; delete mRotationPreviewBox; mRotationPreviewBox = nullptr; QgsVectorLayer* vlayer = mCurrentLabel.layer; if ( !vlayer ) { return; } int rotationCol; if ( !labelIsRotatable( vlayer, mCurrentLabel.settings, rotationCol ) ) { return; } double rotation = mCtrlPressed ? roundTo15Degrees( mCurrentRotation ) : mCurrentRotation; if ( rotation == mStartRotation ) //mouse button pressed / released, but no rotation { return; } vlayer->beginEditCommand( tr( "Rotated label" ) + QString( " '%1'" ).arg( currentLabelText( 24 ) ) ); vlayer->changeAttributeValue( mCurrentLabel.pos.featureId, rotationCol, rotation ); vlayer->endEditCommand(); vlayer->triggerRepaint(); }
void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { //check if we operate on a vector layer QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } bool isGeometryEmpty = false; QgsFeatureList selectedFeatures = vlayer->selectedFeatures(); if ( !selectedFeatures.isEmpty() && selectedFeatures.at( 0 ).geometry().isNull() ) isGeometryEmpty = true; if ( !checkSelection() ) { stopCapturing(); return; } int errorCode = 0; switch ( mode() ) { case CapturePoint: { QgsPoint layerPoint; QgsPointXY mapPoint = e->mapPoint(); if ( nextPoint( QgsPoint( mapPoint ), layerPoint ) != 0 ) { QgsDebugMsg( "nextPoint failed" ); return; } vlayer->beginEditCommand( tr( "Part added" ) ); errorCode = vlayer->addPart( QgsPointSequence() << layerPoint ); } break; case CaptureLine: case CapturePolygon: { //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { QgsDebugMsg( "current layer is not a vector layer" ); return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Coordinate transform error. Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } startCapturing(); return; } else if ( e->button() != Qt::RightButton ) { deleteTempRubberBand(); return; } if ( !isCapturing() ) return; if ( mode() == CapturePolygon ) { closePolygon(); } //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QgsCurve *curveToAdd = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); } vlayer->beginEditCommand( tr( "Part added" ) ); if ( mode() == CapturePolygon ) { //avoid intersections QgsCurvePolygon *cp = new QgsCurvePolygon(); cp->setExteriorRing( curveToAdd ); QgsGeometry *geom = new QgsGeometry( cp ); geom->avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); const QgsCurvePolygon *cpGeom = qgsgeometry_cast<const QgsCurvePolygon *>( geom->constGet() ); if ( !cpGeom ) { stopCapturing(); delete geom; vlayer->destroyEditCommand(); return; } errorCode = vlayer->addPart( cpGeom->exteriorRing()->clone() ); delete geom; } else { errorCode = vlayer->addPart( curveToAdd ); } stopCapturing(); } break; default: Q_ASSERT( !"invalid capture mode" ); errorCode = 6; break; } QString errorMessage; switch ( errorCode ) { case 0: { // remove previous message emit messageDiscarded(); //add points to other features to keep topology up-to-date bool topologicalEditing = QgsProject::instance()->topologicalEditing(); if ( topologicalEditing ) { addTopologicalPoints( points() ); } vlayer->endEditCommand(); vlayer->triggerRepaint(); if ( ( !isGeometryEmpty ) && QgsWkbTypes::isSingleType( vlayer->wkbType() ) ) { emit messageEmitted( tr( "Add part: Feature geom is single part and you've added more than one" ), Qgis::Warning ); } return; } case 1: errorMessage = tr( "Selected feature is not multi part." ); break; case 2: errorMessage = tr( "New part's geometry is not valid." ); break; case 3: errorMessage = tr( "New polygon ring not disjoint with existing polygons." ); break; case 4: errorMessage = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" ); break; case 5: errorMessage = tr( "Several features are selected. Please select only one feature to which an island should be added." ); break; case 6: errorMessage = tr( "Selected geometry could not be found" ); break; } emit messageEmitted( errorMessage, Qgis::Warning ); vlayer->destroyEditCommand(); }
void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e ) { QgsVectorLayer* vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } QgsWkbTypes::Type layerWKBType = vlayer->wkbType(); QgsVectorDataProvider* provider = vlayer->dataProvider(); if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) ) { emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), QgsMessageBar::WARNING ); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } // POINT CAPTURING if ( mode() == CapturePoint ) { if ( e->button() != Qt::LeftButton ) return; //check we only use this tool for point/multipoint layers if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } QgsPoint savePoint; //point in layer coordinates try { QgsPointV2 fetchPoint; int res; res = fetchLayerPoint( e->mapPointMatch(), fetchPoint ); if ( res == 0 ) { savePoint = QgsPoint( fetchPoint.x(), fetchPoint.y() ); } else { savePoint = toLayerCoordinates( vlayer, e->mapPoint() ); } QgsDebugMsg( "savePoint = " + savePoint.toString() ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } //only do the rest for provider with feature addition support //note that for the grass provider, this will return false since //grass provider has its own mechanism of feature addition if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) { QgsFeature f( vlayer->fields(), 0 ); QgsGeometry g; if ( layerWKBType == QgsWkbTypes::Point ) { g = QgsGeometry::fromPoint( savePoint ); } else if ( layerWKBType == QgsWkbTypes::Point25D ) { g = QgsGeometry( new QgsPointV2( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), 0.0 ) ); } else if ( layerWKBType == QgsWkbTypes::MultiPoint ) { g = QgsGeometry::fromMultiPoint( QgsMultiPoint() << savePoint ); } else if ( layerWKBType == QgsWkbTypes::MultiPoint25D ) { QgsMultiPointV2* mp = new QgsMultiPointV2(); mp->addGeometry( new QgsPointV2( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), 0.0 ) ); g = QgsGeometry( mp ); } else { // if layer supports more types (mCheckGeometryType is false) g = QgsGeometry::fromPoint( savePoint ); } f.setGeometry( g ); f.setValid( true ); addFeature( vlayer, &f, false ); vlayer->triggerRepaint(); } } // LINE AND POLYGON CAPTURING else if ( mode() == CaptureLine || mode() == CapturePolygon ) { //check we only use the line tool for line/multiline layers if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } //check we only use the polygon tool for polygon/multipolygon layers if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { // End of string deleteTempRubberBand(); //lines: bail out if there are not at least two vertices if ( mode() == CaptureLine && size() < 2 ) { stopCapturing(); return; } //polygons: bail out if there are not at least two vertices if ( mode() == CapturePolygon && size() < 3 ) { stopCapturing(); return; } if ( mode() == CapturePolygon ) { closePolygon(); } //create QgsFeature with wkb representation QScopedPointer< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) ); //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QgsCurve* curveToAdd = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); } if ( mode() == CaptureLine ) { QgsGeometry* g = new QgsGeometry( curveToAdd ); f->setGeometry( *g ); delete g; } else { QgsCurvePolygon* poly = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { poly = new QgsCurvePolygon(); } else { poly = new QgsPolygonV2(); } poly->setExteriorRing( curveToAdd ); QgsGeometry* g = new QgsGeometry( poly ); f->setGeometry( *g ); delete g; QgsGeometry featGeom = f->geometry(); int avoidIntersectionsReturn = featGeom.avoidIntersections(); f->setGeometry( featGeom ); if ( avoidIntersectionsReturn == 1 ) { //not a polygon type. Impossible to get there } if ( f->geometry().isGeosEmpty() ) //avoid intersection might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), QgsMessageBar::CRITICAL ); stopCapturing(); return; } } f->setValid( true ); if ( addFeature( vlayer, f.data(), false ) ) { //add points to other features to keep topology up-to-date int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); //use always topological editing for avoidIntersection. //Otherwise, no way to guarantee the geometries don't have a small gap in between. QStringList intersectionLayers = QgsProject::instance()->readListEntry( "Digitizing", "/AvoidIntersectionsList" ); bool avoidIntersection = !intersectionLayers.isEmpty(); if ( avoidIntersection ) //try to add topological points also to background layers { QStringList::const_iterator lIt = intersectionLayers.constBegin(); for ( ; lIt != intersectionLayers.constEnd(); ++lIt ) { QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( *lIt ); QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml ); //can only add topological points if background layer is editable... if ( vl && vl->geometryType() == QgsWkbTypes::PolygonGeometry && vl->isEditable() ) { vl->addTopologicalPoints( f->geometry() ); } } } else if ( topologicalEditing ) { vlayer->addTopologicalPoints( f->geometry() ); } } stopCapturing(); } } }
void QgsMapToolMoveFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer || !vlayer->isEditable() ) { delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); cadDockWidget()->clear(); notifyNotEditableLayer(); return; } if ( !mRubberBand ) { //find first geometry under mouse cursor and store iterator to it QgsPointXY layerCoords = toLayerCoordinates( vlayer, e->mapPoint() ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); if ( vlayer->selectedFeatureCount() == 0 ) { QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setNoAttributes() ); //find the closest feature QgsGeometry pointGeometry = QgsGeometry::fromPointXY( layerCoords ); if ( pointGeometry.isNull() ) { cadDockWidget()->clear(); return; } double minDistance = std::numeric_limits<double>::max(); QgsFeature cf; QgsFeature f; while ( fit.nextFeature( f ) ) { if ( f.hasGeometry() ) { double currentDistance = pointGeometry.distance( f.geometry() ); if ( currentDistance < minDistance ) { minDistance = currentDistance; cf = f; } } } if ( minDistance == std::numeric_limits<double>::max() ) { cadDockWidget()->clear(); return; } mMovedFeatures.clear(); mMovedFeatures << cf.id(); //todo: take the closest feature, not the first one... mRubberBand = createRubberBand( vlayer->geometryType() ); mRubberBand->setToGeometry( cf.geometry(), vlayer ); } else { mMovedFeatures = vlayer->selectedFeatureIds(); mRubberBand = createRubberBand( vlayer->geometryType() ); QgsFeature feat; QgsFeatureIterator it = vlayer->getSelectedFeatures( QgsFeatureRequest().setNoAttributes() ); bool allFeaturesInView = true; QgsRectangle viewRect = mCanvas->mapSettings().mapToLayerCoordinates( vlayer, mCanvas->extent() ); while ( it.nextFeature( feat ) ) { mRubberBand->addGeometry( feat.geometry(), vlayer ); if ( allFeaturesInView && !viewRect.intersects( feat.geometry().boundingBox() ) ) allFeaturesInView = false; } if ( !allFeaturesInView ) { // for extra safety to make sure we are not modifying geometries by accident int res = QMessageBox::warning( mCanvas, tr( "Move features" ), tr( "Some of the selected features are outside of the current map view. Would you still like to continue?" ), QMessageBox::Yes | QMessageBox::No ); if ( res != QMessageBox::Yes ) { mMovedFeatures.clear(); delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); return; } } } mStartPointMapCoords = e->mapPoint(); mRubberBand->show(); } else { // copy and move mode if ( e->button() != Qt::LeftButton ) { cadDockWidget()->clear(); delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); return; } QgsPointXY startPointLayerCoords = toLayerCoordinates( ( QgsMapLayer * )vlayer, mStartPointMapCoords ); QgsPointXY stopPointLayerCoords = toLayerCoordinates( ( QgsMapLayer * )vlayer, e->mapPoint() ); double dx = stopPointLayerCoords.x() - startPointLayerCoords.x(); double dy = stopPointLayerCoords.y() - startPointLayerCoords.y(); vlayer->beginEditCommand( mMode == Move ? tr( "Feature moved" ) : tr( "Feature copied and moved" ) ); switch ( mMode ) { case Move: Q_FOREACH ( QgsFeatureId id, mMovedFeatures ) { vlayer->translateFeature( id, dx, dy ); } delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); cadDockWidget()->clear(); break; case CopyMove: QgsFeatureRequest request; request.setFilterFids( mMovedFeatures ); QString *errorMsg = new QString(); if ( !QgisApp::instance()->vectorLayerTools()->copyMoveFeatures( vlayer, request, dx, dy, errorMsg ) ) { emit messageEmitted( *errorMsg, Qgis::Critical ); delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); } break; } vlayer->endEditCommand(); vlayer->triggerRepaint(); }
void QgsMapToolMoveLabel::canvasReleaseEvent( QgsMapMouseEvent *e ) { if ( !mLabelRubberBand ) { return; } deleteRubberBands(); QgsVectorLayer *vlayer = mCurrentLabel.layer; if ( !vlayer ) { return; } QgsPointXY releaseCoords = toMapCoordinates( e->pos() ); double xdiff = releaseCoords.x() - mStartPointMapCoords.x(); double ydiff = releaseCoords.y() - mStartPointMapCoords.y(); int xCol, yCol; double xPosOrig, yPosOrig; bool xSuccess, ySuccess; if ( !currentLabelDataDefinedPosition( xPosOrig, xSuccess, yPosOrig, ySuccess, xCol, yCol ) ) { return; } double xPosNew, yPosNew; if ( !xSuccess || !ySuccess ) { xPosNew = releaseCoords.x() - mClickOffsetX; yPosNew = releaseCoords.y() - mClickOffsetY; } else { //transform to map crs first, because xdiff,ydiff are in map coordinates const QgsMapSettings &ms = mCanvas->mapSettings(); QgsPointXY transformedPoint = ms.layerToMapCoordinates( vlayer, QgsPointXY( xPosOrig, yPosOrig ) ); xPosOrig = transformedPoint.x(); yPosOrig = transformedPoint.y(); xPosNew = xPosOrig + xdiff; yPosNew = yPosOrig + ydiff; } //transform back to layer crs if ( mCanvas ) { const QgsMapSettings &s = mCanvas->mapSettings(); QgsPointXY transformedPoint = s.mapToLayerCoordinates( vlayer, QgsPointXY( xPosNew, yPosNew ) ); xPosNew = transformedPoint.x(); yPosNew = transformedPoint.y(); } vlayer->beginEditCommand( tr( "Moved label" ) + QStringLiteral( " '%1'" ).arg( currentLabelText( 24 ) ) ); vlayer->changeAttributeValue( mCurrentLabel.pos.featureId, xCol, xPosNew ); vlayer->changeAttributeValue( mCurrentLabel.pos.featureId, yCol, yPosNew ); // set rotation to that of label, if data-defined and no rotation set yet // honor whether to preserve preexisting data on pin // must come after setting x and y positions if ( !mCurrentLabel.pos.isDiagram && !mCurrentLabel.pos.isPinned && !currentLabelPreserveRotation() ) { double defRot; bool rSuccess; int rCol; if ( currentLabelDataDefinedRotation( defRot, rSuccess, rCol ) ) { double labelRot = mCurrentLabel.pos.rotation * 180 / M_PI; vlayer->changeAttributeValue( mCurrentLabel.pos.featureId, rCol, labelRot ); } } vlayer->endEditCommand(); vlayer->triggerRepaint(); }