Skip to content
Permalink
Browse files

[pal] Allow data defined quadrant when in "around point" mode

Previously, placement options were either all-or-nothing. If you
wanted to manually set the quadrant for specific labels, it was
only possible when in "offset from point" mode. But this meant
that all other labels would be forced to have the same quadrant
placement, resulting in poor placement. If instead you used
"around point" mode, then all labels would be dynamically placed
but there was no way to force a single label to a specific
quadrant.

Now, you can set the placement mode to "around point" so that the
majority of labels get auto placed, and then set a data defined
quadrant override for specific labels.

Additionally, when data defined quadrant is set when in "around
point" placement, then the label distance is used to offset the
label rather then the offset X/Y used when in "offset from point"
mode.
  • Loading branch information
nyalldawson committed Jul 8, 2015
1 parent 7b18f67 commit c74754ed8db19a0e02a7906b92cef262355d9ccf
Showing with 382 additions and 339 deletions.
  1. +7 −0 src/app/qgslabelinggui.cpp
  2. +66 −51 src/core/pal/feature.cpp
  3. +10 −0 src/core/pal/feature.h
  4. +6 −0 src/core/qgspallabeling.cpp
  5. +293 −288 src/ui/qgslabelingguibase.ui
@@ -1289,6 +1289,7 @@ void QgsLabelingGui::updatePlacementWidgets()
bool showLineFrame = false;
bool showCentroidFrame = false;
bool showQuadrantFrame = false;
bool showFixedQuadrantFrame = false;
bool showOffsetFrame = false;
bool showDistanceFrame = false;
bool showRotationFrame = false;
@@ -1302,12 +1303,17 @@ void QgsLabelingGui::updatePlacementWidgets()
showCentroidFrame = ( curWdgt == pagePolygon && radAroundCentroid->isChecked() );
showDistanceFrame = true;
//showRotationFrame = true; // TODO: uncomment when supported
if ( curWdgt == pagePoint )
{
showQuadrantFrame = true;
}
}
else if (( curWdgt == pagePoint && radOverPoint->isChecked() )
|| ( curWdgt == pagePolygon && radOverCentroid->isChecked() ) )
{
showCentroidFrame = ( curWdgt == pagePolygon && radOverCentroid->isChecked() );
showQuadrantFrame = true;
showFixedQuadrantFrame = true;
showOffsetFrame = true;
showRotationFrame = true;
}
@@ -1331,6 +1337,7 @@ void QgsLabelingGui::updatePlacementWidgets()
mPlacementLineFrame->setVisible( showLineFrame );
mPlacementCentroidFrame->setVisible( showCentroidFrame );
mPlacementQuadrantFrame->setVisible( showQuadrantFrame );
mPlacementFixedQuadrantFrame->setVisible( showFixedQuadrantFrame );
mPlacementOffsetFrame->setVisible( showOffsetFrame );
mPlacementDistanceFrame->setVisible( showDistanceFrame );
mPlacementRotationFrame->setVisible( showRotationFrame );
@@ -47,6 +47,7 @@
#include "labelposition.h"
#include "pointset.h"
#include "util.h"
#include "qgis.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
@@ -75,6 +76,7 @@ namespace pal
, fixedAngle( 0.0 )
, repeatDist( 0.0 )
, alwaysShow( false )
, mFixedQuadrant( false )
{
assert( finite( lx ) && finite( ly ) );
}
@@ -351,7 +353,25 @@ namespace pal
}
}

if ( f->offsetPos )
if ( f->layer->getArrangement() == P_POINT )
{
//if in "around point" placement mode, then we use the label distance to determine
//the label's offset
if ( qgsDoubleNear( f->quadOffsetX , 0.0 ) )
{
ydiff += f->quadOffsetY * f->distlabel;
}
else if ( qgsDoubleNear( f->quadOffsetY, 0.0 ) )
{
xdiff += f->quadOffsetX * f->distlabel;
}
else
{
xdiff += f->quadOffsetX * M_SQRT1_2 * f->distlabel;
ydiff += f->quadOffsetY * M_SQRT1_2 * f->distlabel;
}
}
else if ( f->offsetPos )
{
if ( f->offsetPosX != 0 )
{
@@ -394,26 +414,20 @@ namespace pal
f->layer->pal->map_unit,
dpi, scale, delta_width );

int nbp = f->layer->pal->point_p;
int numberCandidates = f->layer->pal->point_p;

//std::cout << "Nbp : " << nbp << std::endl;

int i;
int icost = 0;
int inc = 2;

double alpha;
double beta = 2 * M_PI / nbp; /* angle bw 2 pos */
double candidateAngleIncrement = 2 * M_PI / numberCandidates; /* angle bw 2 pos */

double lx, ly; /* label pos */

/* various alpha */
/* various angles */
double a90 = M_PI / 2;
double a180 = M_PI;
double a270 = a180 + a90;
double a360 = 2 * M_PI;


double gamma1, gamma2;

if ( distlabel > 0 )
@@ -426,7 +440,6 @@ namespace pal
gamma1 = gamma2 = a90 / 3.0;
}


if ( gamma1 > a90 / 3.0 )
gamma1 = a90 / 3.0;

@@ -439,101 +452,103 @@ namespace pal
std::cout << "Oups... label size error..." << std::endl;
}

*lPos = new LabelPosition *[nbp];
*lPos = new LabelPosition *[numberCandidates];

for ( i = 0, alpha = M_PI / 4; i < nbp; i++, alpha += beta )
int i;
double angleToCandidate;
for ( i = 0, angleToCandidate = M_PI / 4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement )
{
lx = x;
ly = y;
double labelX = x;
double labelY = y;

if ( alpha > a360 )
alpha -= a360;
if ( angleToCandidate > a360 )
angleToCandidate -= a360;

LabelPosition::Quadrant quadrant = LabelPosition::QuadrantOver;

if ( alpha < gamma1 || alpha > a360 - gamma1 ) // on the right
if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 ) // on the right
{
lx += distlabel;
double iota = ( alpha + gamma1 );
labelX += distlabel;
double iota = ( angleToCandidate + gamma1 );
if ( iota > a360 - gamma1 )
iota -= a360;

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

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

double cost;

if ( nbp == 1 )
if ( numberCandidates == 1 )
cost = 0.0001;
else
cost = 0.0001 + 0.0020 * double( icost ) / double( nbp - 1 );
cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );

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

icost += inc;

if ( icost == nbp )
if ( icost == numberCandidates )
{
icost = nbp - 1;
icost = numberCandidates - 1;
inc = -2;
}
else if ( icost > nbp )
else if ( icost > numberCandidates )
{
icost = nbp - 2;
icost = numberCandidates - 2;
inc = -2;
}

}

return nbp;
return numberCandidates;
}

// TODO work with squared distance by remonving call to sqrt or dist_euc2d
@@ -1323,7 +1338,7 @@ namespace pal
switch ( type )
{
case GEOS_POINT:
if ( f->layer->getArrangement() == P_POINT_OVER )
if ( f->layer->getArrangement() == P_POINT_OVER || f->fixedQuadrant() )
nbp = setPositionOverPoint( x[0], y[0], scale, lPos, delta, angle );
else
nbp = setPositionForPoint( x[0], y[0], scale, lPos, delta, angle );
@@ -88,6 +88,13 @@ namespace pal
//Set label position of the feature to fixed x/y values
void setFixedPosition( double x, double y ) { fixedPos = true; fixedPosX = x; fixedPosY = y;}
void setQuadOffset( double x, double y ) { quadOffset = true; quadOffsetX = x; quadOffsetY = y;}

/** Sets whether the quadrant for the label must be respected. This can be used
* to fix the quadrant for specific features when using an "around point" placement.
*/
void setFixedQuadrant( bool fixed ) { mFixedQuadrant = fixed; }
bool fixedQuadrant() const { return mFixedQuadrant; }

void setPosOffset( double x, double y ) { offsetPos = true; offsetPosX = x; offsetPosY = y;}
bool fixedPosition() const { return fixedPos; }
//Set label rotation to fixed value
@@ -126,6 +133,9 @@ namespace pal
// array of parts - possibly not necessary
//int nPart;
//FeaturePart** parts;
private:

bool mFixedQuadrant;
};

/**
@@ -1799,6 +1799,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
double offsetX = 0.0, offsetY = 0.0;

//data defined quadrant offset?
bool ddFixedQuad = false;
QuadrantPosition quadOff = quadOffset;
if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal ) )
{
@@ -1808,6 +1809,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
if ( ok && 0 <= quadInt && quadInt <= 8 )
{
quadOff = ( QuadrantPosition )quadInt;
ddFixedQuad = true;
}
}

@@ -2166,6 +2168,10 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
}

if ( ddFixedQuad )
{
feat->setFixedQuadrant( true );
}

//add parameters for data defined labeling to QgsPalGeometry
QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();

0 comments on commit c74754e

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