Skip to content
Permalink
Browse files

[FEATURE] Allow moving callout start or end points interactively

using the Move Labels tool

Allows users to manually place the exact start or end point of label
callout lines.

*Callouts must have a data defined origin/destination
field set to permit this.
  • Loading branch information
nyalldawson committed Feb 23, 2021
1 parent d84da56 commit f6c93de0fe22d7965a07c692c9b1b2744ed6e82b
@@ -48,6 +48,7 @@ QgsMapToolLabel::~QgsMapToolLabel()
{
delete mLabelRubberBand;
delete mHoverRubberBand;
delete mCalloutOtherPointsRubberBand;
delete mFeatureRubberBand;
delete mFixPointRubberBand;
}
@@ -119,6 +120,41 @@ bool QgsMapToolLabel::labelAtPosition( QMouseEvent *e, QgsLabelPosition &p )
return true;
}

bool QgsMapToolLabel::calloutAtPosition( QMouseEvent *e, QgsCalloutPosition &p, bool &isOrigin )
{
QgsPointXY pt = toMapCoordinates( e->pos() );
const QgsLabelingResults *labelingResults = mCanvas->labelingResults();
if ( !labelingResults )
return false;

const double tol = QgsTolerance::vertexSearchRadius( canvas()->mapSettings() );

QList<QgsCalloutPosition> calloutPosList = labelingResults->calloutsWithinRectangle( QgsRectangle::fromCenterAndSize( pt, tol * 2, tol * 2 ) );
if ( calloutPosList.empty() )
return false;

// prioritize callouts in the current selected layer, in case of overlaps
QList<QgsCalloutPosition> activeLayerCallouts;
if ( const QgsVectorLayer *currentLayer = qobject_cast< QgsVectorLayer * >( mCanvas->currentLayer() ) )
{
for ( const QgsCalloutPosition &pos : qgis::as_const( calloutPosList ) )
{
if ( pos.layerID == currentLayer->id() )
{
activeLayerCallouts.append( pos );
}
}
}
if ( !activeLayerCallouts.empty() )
calloutPosList = activeLayerCallouts;

p = calloutPosList.at( 0 );

isOrigin = QgsPointXY( p.origin() ).sqrDist( pt ) < QgsPointXY( p.destination() ).sqrDist( pt );

return true;
}

void QgsMapToolLabel::createRubberBands()
{
delete mLabelRubberBand;
@@ -894,7 +930,53 @@ void QgsMapToolLabel::updateHoveredLabel( QgsMapMouseEvent *e )
mHoverRubberBand->setWidth( 2 );
mHoverRubberBand->setSecondaryStrokeColor( QColor( 255, 255, 255, 100 ) );
mHoverRubberBand->setColor( QColor( 200, 0, 120, 255 ) );
mHoverRubberBand->setIcon( QgsRubberBand::ICON_BOX );

double scaleFactor = mCanvas->fontMetrics().xHeight();
mHoverRubberBand->setIconSize( scaleFactor );
}

QgsCalloutPosition calloutPosition;
bool isOrigin = false;
int xCol = 0;
int yCol = 0;
if ( calloutAtPosition( e, calloutPosition, isOrigin ) && canModifyCallout( calloutPosition, isOrigin, xCol, yCol ) )
{
if ( !mCalloutOtherPointsRubberBand )
{
mCalloutOtherPointsRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PointGeometry );
mCalloutOtherPointsRubberBand->setWidth( 2 );
mCalloutOtherPointsRubberBand->setSecondaryStrokeColor( QColor( 255, 255, 255, 100 ) );
mCalloutOtherPointsRubberBand->setColor( QColor( 200, 0, 120, 255 ) );
mCalloutOtherPointsRubberBand->setIcon( QgsRubberBand::ICON_X );
double scaleFactor = mCanvas->fontMetrics().xHeight();
mCalloutOtherPointsRubberBand->setIconSize( scaleFactor );
}

// callouts are a smaller target, so they take precedence over labels
mCurrentHoverLabel = LabelDetails();

mHoverRubberBand->show();
mHoverRubberBand->reset( QgsWkbTypes::PointGeometry );
mCalloutOtherPointsRubberBand->show();
mCalloutOtherPointsRubberBand->reset( QgsWkbTypes::PointGeometry );

if ( isOrigin )
{
mHoverRubberBand->addPoint( calloutPosition.origin() );
mCalloutOtherPointsRubberBand->addPoint( calloutPosition.destination() );
}
else
{
mHoverRubberBand->addPoint( calloutPosition.destination() );
mCalloutOtherPointsRubberBand->addPoint( calloutPosition.origin() );
}
return;
}

if ( mCalloutOtherPointsRubberBand )
mCalloutOtherPointsRubberBand->hide();

QgsLabelPosition labelPos;
if ( !labelAtPosition( e, labelPos ) )
{
@@ -935,10 +1017,18 @@ void QgsMapToolLabel::clearHoveredLabel()
{
if ( mHoverRubberBand )
mHoverRubberBand->hide();
if ( mCalloutOtherPointsRubberBand )
mCalloutOtherPointsRubberBand->hide();

mCurrentHoverLabel = LabelDetails();
}

bool QgsMapToolLabel::canModifyLabel( const QgsMapToolLabel::LabelDetails & )
{
return true;
}

bool QgsMapToolLabel::canModifyCallout( const QgsCalloutPosition &, bool, int &, int & )
{
return false;
}
@@ -22,6 +22,7 @@
#include "qgspallabeling.h"
#include "qgsnewauxiliarylayerdialog.h"
#include "qgsauxiliarystorage.h"
#include "qgscalloutposition.h"
#include "qgis_app.h"

class QgsRubberBand;
@@ -38,6 +39,8 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
QgsMapToolLabel( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDock );
~QgsMapToolLabel() override;



void deactivate() override;

/**
@@ -87,6 +90,7 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing

protected:
QgsRubberBand *mHoverRubberBand = nullptr;
QgsRubberBand *mCalloutOtherPointsRubberBand = nullptr;
QgsRubberBand *mLabelRubberBand = nullptr;
QgsRubberBand *mFeatureRubberBand = nullptr;
//! Shows label fixpoint (left/bottom by default)
@@ -116,6 +120,15 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
*/
bool labelAtPosition( QMouseEvent *e, QgsLabelPosition &p );

/**
* Returns callout position for a mouse event.
* \param e mouse event
* \param p out: callout position
* \param isOrigin set to TRUE if the origin is at the position, or FALSE if the callout destination is at the position
* \returns TRUE in case of success, FALSE if no callout at this location
*/
bool calloutAtPosition( QMouseEvent *e, QgsCalloutPosition &p, bool &isOrigin );

/**
* Finds out rotation point of current label position
* \param ignoreUpsideDown treat label as right-side-up
@@ -127,7 +140,7 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
void createRubberBands();

//! Removes label / feature / fixpoint rubber bands
void deleteRubberBands();
virtual void deleteRubberBands();

/**
* Returns current label's text
@@ -211,6 +224,7 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
void updateHoveredLabel( QgsMapMouseEvent *e );
void clearHoveredLabel();
virtual bool canModifyLabel( const LabelDetails &label );
virtual bool canModifyCallout( const QgsCalloutPosition &callout, bool isOrigin, int &xCol, int &yCol );

QList<QgsPalLayerSettings::Property> mPalProperties;
QList<QgsDiagramLayerSettings::Property> mDiagramProperties;

0 comments on commit f6c93de

Please sign in to comment.