Skip to content
Permalink
Browse files

[feature] When interactively moving a callout line, holding shift

will cause the callouti line angle to snap to 15 degree increments

This allows for easier creation of nicely parallel callout lines
  • Loading branch information
nyalldawson committed Mar 15, 2021
1 parent 6daeebc commit 7550fbe42291a47ef458d1ce0eb7fc8fd87af0f2
Showing with 56 additions and 3 deletions.
  1. +54 −3 src/app/labeling/qgsmaptoolmovelabel.cpp
  2. +2 −0 src/app/labeling/qgsmaptoolmovelabel.h
@@ -25,6 +25,7 @@
#include "qgsadvanceddigitizingdockwidget.h"
#include "qgsvectorlayerlabeling.h"
#include "qgscallout.h"
#include "qgsstatusbar.h"

QgsMapToolMoveLabel::QgsMapToolMoveLabel( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDock )
: QgsMapToolLabel( canvas, cadDock )
@@ -67,7 +68,15 @@ void QgsMapToolMoveLabel::cadCanvasMoveEvent( QgsMapMouseEvent *e )
else if ( mCalloutMoveRubberBand )
{
const int index = mCurrentCalloutMoveOrigin ? 0 : 1;
mCalloutMoveRubberBand->movePoint( index, e->mapPoint() );

QgsPointXY mapPoint = e->mapPoint();
if ( e->modifiers() & Qt::ShiftModifier )
{
// shift modifier = snap to common angles
mapPoint = snapCalloutPointToCommonAngle( mapPoint, true );
}

mCalloutMoveRubberBand->movePoint( index, mapPoint );
mCalloutMoveRubberBand->update();
}
else
@@ -231,17 +240,23 @@ void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
case Qt::LeftButton:
{
// second click drops label/callout
const bool isCalloutMove = !mCurrentCallout.layerID.isEmpty();
QgsPointXY releaseCoords = e->mapPoint();
if ( isCalloutMove && e->modifiers() & Qt::ShiftModifier )
{
// shift modifier = snap to common angles
releaseCoords = snapCalloutPointToCommonAngle( releaseCoords, false );
}

deleteRubberBands();

const bool isCalloutMove = !mCurrentCallout.layerID.isEmpty();
QgsVectorLayer *vlayer = !isCalloutMove ? mCurrentLabel.layer : QgsProject::instance()->mapLayer<QgsVectorLayer *>( mCurrentCallout.layerID );
if ( !vlayer )
{
return;
}
const QgsFeatureId featureId = !isCalloutMove ? mCurrentLabel.pos.featureId : mCurrentCallout.featureId;

QgsPointXY releaseCoords = e->mapPoint();
double xdiff = releaseCoords.x() - mStartPointMapCoords.x();
double ydiff = releaseCoords.y() - mStartPointMapCoords.y();

@@ -549,5 +564,41 @@ bool QgsMapToolMoveLabel::currentCalloutDataDefinedPosition( double &x, bool &xS
return true;
}

QgsPointXY QgsMapToolMoveLabel::snapCalloutPointToCommonAngle( const QgsPointXY &mapPoint, bool showStatusMessage ) const
{
const int index = mCurrentCalloutMoveOrigin ? 0 : 1;

QgsPointXY start = *mCalloutMoveRubberBand->getPoint( 0, index == 0 ? 1 : 0 );
const double cursorDistance = start.distance( mapPoint );

// snap to common angles (15 degree increments)
double closestDist = std::numeric_limits< double >::max();
double closestX = 0;
double closestY = 0;
int bestAngle = 0;

const double angleOffset = -canvas()->rotation();

for ( int angle = 0; angle < 360; angle += 15 )
{
const QgsPointXY end = start.project( cursorDistance * 2, angle + angleOffset );
double minDistX = 0;
double minDistY = 0;
const double angleDist = QgsGeometryUtils::sqrDistToLine( mapPoint.x(), mapPoint.y(), start.x(), start.y(), end.x(), end.y(), minDistX, minDistY, 4 * std::numeric_limits<double>::epsilon() );
if ( angleDist < closestDist )
{
closestDist = angleDist;
closestX = minDistX;
closestY = minDistY;
bestAngle = angle;
}
}

if ( showStatusMessage )
QgisApp::instance()->statusBarIface()->showMessage( tr( "Callout angle: %1°" ).arg( bestAngle ), 2000 );

return QgsPointXY( closestX, closestY );
}



@@ -57,6 +57,8 @@ class APP_EXPORT QgsMapToolMoveLabel: public QgsMapToolLabel
private:
bool currentCalloutDataDefinedPosition( double &x, bool &xSuccess, double &y, bool &ySuccess, int &xCol, int &yCol );

QgsPointXY snapCalloutPointToCommonAngle( const QgsPointXY &mapPoint, bool showStatusMessage ) const;

};

#endif // QGSMAPTOOLMOVELABEL_H

0 comments on commit 7550fbe

Please sign in to comment.