Skip to content
Permalink
Browse files

[FEATURE] Add follow point alignment for multiline labels

When set to this mode, text alignment for labels will be dependant
on the final placement of the label relative to the point. Eg, if
the label is placed to the left of the point then the label will
be right aligned, and if it is placed to the right of the point
then the label will be left aligned.
(fix #11153)
  • Loading branch information
nyalldawson committed Mar 19, 2015
1 parent 7622f5c commit 44f7930ecf93216cc1648c58919154210ed31ffc
@@ -140,7 +140,9 @@ class QgsPalLayerSettings
{
MultiLeft,
MultiCenter,
MultiRight
MultiRight,
MultiFollowPlacement /*< Alignment follows placement of label, eg labels to the left of a feature
will be drawn with right alignment*/
};

enum ShapeType
@@ -141,6 +141,12 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
qFatal( "unknown geometry type unexpected" );
}

if ( layer->geometryType() == QGis::Point )
{
// follow placement alignment is only valid for point layers
mFontMultiLineAlignComboBox->addItem( tr( "Follow label placement" ) );
}

// show/hide options based upon geometry type
chkMergeLines->setVisible( layer->geometryType() == QGis::Line );
mDirectSymbolsFrame->setVisible( layer->geometryType() == QGis::Line );
@@ -913,7 +919,7 @@ void QgsLabelingGui::populateDataDefinedButtons( QgsPalLayerSettings& s )
mFontLineHeightDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::MultiLineHeight ),
QgsDataDefinedButton::AnyType, tr( "double [0.0-10.0]" ) );
mFontMultiLineAlignDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::MultiLineAlignment ),
QgsDataDefinedButton::String, QgsDataDefinedButton::textHorzAlignDesc() );
QgsDataDefinedButton::String, trString + "[<b>Left</b>|<b>Center</b>|<b>Right</b>|<b>Follow</b>]" );

mDirectSymbDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::DirSymbDraw ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::boolDesc() );
@@ -275,6 +275,55 @@ namespace pal
return f->uid;
}

LabelPosition::Quadrant FeaturePart::quadrantFromOffset() const
{
if ( f->quadOffsetX < 0 )
{
if ( f->quadOffsetY < 0 )
{
return LabelPosition::QuadrantAboveLeft;
}
else if ( f->quadOffsetY > 0 )
{
return LabelPosition::QuadrantBelowLeft;
}
else
{
return LabelPosition::QuadrantLeft;
}
}
else if ( f->quadOffsetX > 0 )
{
if ( f->quadOffsetY < 0 )
{
return LabelPosition::QuadrantAboveRight;
}
else if ( f->quadOffsetY > 0 )
{
return LabelPosition::QuadrantBelowRight;
}
else
{
return LabelPosition::QuadrantRight;
}
}
else
{
if ( f->quadOffsetY < 0 )
{
return LabelPosition::QuadrantAbove;
}
else if ( f->quadOffsetY > 0 )
{
return LabelPosition::QuadrantBelow;
}
else
{
return LabelPosition::QuadrantOver;
}
}
}

int FeaturePart::setPositionOverPoint( double x, double y, double scale, LabelPosition ***lPos, double delta_width, double angle )
{
Q_UNUSED( scale );
@@ -322,6 +371,7 @@ namespace pal
delete lp;
}

LabelPosition::Quadrant quadrant = f->quadOffset ? quadrantFromOffset() : LabelPosition::QuadrantOver;
if ( f->quadOffset )
{
if ( f->quadOffsetX != 0 )
@@ -349,7 +399,7 @@ namespace pal
lx = x + xdiff;
ly = y + ydiff;

( *lPos )[0] = new LabelPosition( id, lx, ly, label_x, label_y, angle, cost, this );
( *lPos )[0] = new LabelPosition( id, lx, ly, label_x, label_y, angle, cost, this, false, quadrant );
return nbp;
}

@@ -443,6 +493,8 @@ namespace pal
if ( alpha > a360 )
alpha -= a360;

LabelPosition::Quadrant quadrant = LabelPosition::QuadrantOver;

if ( alpha < gamma1 || alpha > a360 - gamma1 ) // on the right
{
lx += distlabel;
@@ -452,44 +504,53 @@ namespace pal

//ly += -yrm/2.0 + tan(alpha)*(distlabel + xrm/2);
ly += -yrm + yrm * iota / ( 2 * gamma1 );

quadrant = LabelPosition::QuadrantRight;
}
else if ( alpha < a90 - gamma2 ) // top-right
{
lx += distlabel * cos( alpha );
ly += distlabel * sin( alpha );
quadrant = LabelPosition::QuadrantAboveRight;
}
else if ( alpha < a90 + gamma2 ) // top
{
//lx += -xrm/2.0 - tan(alpha+a90)*(distlabel + yrm/2);
lx += -xrm * ( alpha - a90 + gamma2 ) / ( 2 * gamma2 );
ly += distlabel;
quadrant = LabelPosition::QuadrantAbove;
}
else if ( alpha < a180 - gamma1 ) // top left
{
lx += distlabel * cos( alpha ) - xrm;
ly += distlabel * sin( alpha );
quadrant = LabelPosition::QuadrantAboveLeft;
}
else if ( alpha < a180 + gamma1 ) // left
{
lx += -distlabel - xrm;
//ly += -yrm/2.0 - tan(alpha)*(distlabel + xrm/2);
ly += - ( alpha - a180 + gamma1 ) * yrm / ( 2 * gamma1 );
quadrant = LabelPosition::QuadrantLeft;
}
else if ( alpha < a270 - gamma2 ) // down - left
{
lx += distlabel * cos( alpha ) - xrm;
ly += distlabel * sin( alpha ) - yrm;
quadrant = LabelPosition::QuadrantBelowLeft;
}
else if ( alpha < a270 + gamma2 ) // down
{
ly += -distlabel - yrm;
//lx += -xrm/2.0 + tan(alpha+a90)*(distlabel + yrm/2);
lx += -xrm + ( alpha - a270 + gamma2 ) * xrm / ( 2 * gamma2 );
quadrant = LabelPosition::QuadrantBelow;
}
else if ( alpha < a360 )
else if ( alpha < a360 ) // down - right
{
lx += distlabel * cos( alpha );
ly += distlabel * sin( alpha ) - yrm;
quadrant = LabelPosition::QuadrantBelowRight;
}

double cost;
@@ -499,7 +560,7 @@ namespace pal
else
cost = 0.0001 + 0.0020 * double( icost ) / double( nbp - 1 );

( *lPos )[i] = new LabelPosition( i, lx, ly, xrm, yrm, angle, cost, this );
( *lPos )[i] = new LabelPosition( i, lx, ly, xrm, yrm, angle, cost, this, false, quadrant );

icost += inc;

@@ -44,7 +44,7 @@

#include "pointset.h"
#include "util.h"

#include "labelposition.h"

namespace pal
{
@@ -321,6 +321,9 @@ namespace pal

void addSizePenalty( int nbp, LabelPosition** lPos, double bbx[4], double bby[4] );

private:

LabelPosition::Quadrant quadrantFromOffset() const;
};

} // end namespace pal
@@ -54,8 +54,9 @@

namespace pal
{
LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed )
: id( id ), cost( cost ), feature( feature ), probFeat( 0 ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ), reversed( isReversed ), upsideDown( false )
LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed, Quadrant quadrant )
: id( id ), cost( cost ), feature( feature ), probFeat( 0 ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ), reversed( isReversed ), upsideDown( false ),
quadrant( quadrant )
{

// alpha take his value bw 0 and 2*pi rad
@@ -166,6 +167,7 @@ namespace pal
partId = other.partId;
upsideDown = other.upsideDown;
reversed = other.reversed;
quadrant = other.quadrant;
}

bool LabelPosition::isIn( double *bbox )
@@ -48,13 +48,31 @@ namespace pal


/**
* \brief LabelPositon is a candidate feature label position
* \brief LabelPosition is a candidate feature label position
*/
class CORE_EXPORT LabelPosition
{
friend class CostCalculator;
friend class PolygonCostCalculator;

public:

/**
* \brief Position of label candidate relative to feature.
*/
enum Quadrant
{
QuadrantAboveLeft,
QuadrantAbove,
QuadrantAboveRight,
QuadrantLeft,
QuadrantOver,
QuadrantRight,
QuadrantBelowLeft,
QuadrantBelow,
QuadrantBelowRight
};

protected:

int id;
@@ -81,10 +99,13 @@ namespace pal

bool upsideDown;

LabelPosition::Quadrant quadrant;

bool isInConflictSinglePart( LabelPosition* lp );
bool isInConflictMultiPart( LabelPosition* lp );

public:

/**
* \brief create a new LabelPosition
*
@@ -97,11 +118,12 @@ namespace pal
* \param cost geographic cost
* \param feature labelpos owners
* \param isReversed label is reversed
* \param quadrant relative position of label to feature
*/
LabelPosition( int id, double x1, double y1,
double w, double h,
double alpha, double cost,
FeaturePart *feature, bool isReversed = false );
FeaturePart *feature, bool isReversed = false, Quadrant quadrant = QuadrantOver );

/** copy constructor */
LabelPosition( const LabelPosition& other );
@@ -215,6 +237,8 @@ namespace pal
bool getReversed() const { return reversed; }
bool getUpsideDown() const { return upsideDown; }

Quadrant getQuadrant() const { return quadrant; }

void print();

LabelPosition* getNextPart() const { return nextPart; }
@@ -2720,6 +2720,10 @@ void QgsPalLayerSettings::parseTextFormatting()
{
aligntype = QgsPalLayerSettings::MultiRight;
}
else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
{
aligntype = QgsPalLayerSettings::MultiFollowPlacement;
}
dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant(( int )aligntype ) );
}
}
@@ -4038,6 +4042,29 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
#endif
tmpLyr.textFont = dFont;

if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiFollowPlacement )
{
//calculate font alignment based on label quadrant
switch (( *it )->getQuadrant() )
{
case LabelPosition::QuadrantAboveLeft:
case LabelPosition::QuadrantLeft:
case LabelPosition::QuadrantBelowLeft:
tmpLyr.multilineAlign = QgsPalLayerSettings::MultiRight;
break;
case LabelPosition::QuadrantAbove:
case LabelPosition::QuadrantOver:
case LabelPosition::QuadrantBelow:
tmpLyr.multilineAlign = QgsPalLayerSettings::MultiCenter;
break;
case LabelPosition::QuadrantAboveRight:
case LabelPosition::QuadrantRight:
case LabelPosition::QuadrantBelowRight:
tmpLyr.multilineAlign = QgsPalLayerSettings::MultiLeft;
break;
}
}

// update tmpLyr with any data defined text style values
dataDefinedTextStyle( tmpLyr, ddValues );

@@ -120,7 +120,9 @@ class CORE_EXPORT QgsPalLayerSettings
{
MultiLeft = 0,
MultiCenter,
MultiRight
MultiRight,
MultiFollowPlacement /*< Alignment follows placement of label, eg labels to the left of a feature
will be drawn with right alignment*/
};

enum ShapeType

1 comment on commit 44f7930

@nirvn

This comment has been minimized.

Copy link
Contributor

@nirvn nirvn commented on 44f7930 Mar 19, 2015

!

Please sign in to comment.
You can’t perform that action at this time.