Skip to content

Commit c74754e

Browse files
committed
[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.
1 parent 7b18f67 commit c74754e

File tree

5 files changed

+382
-339
lines changed

5 files changed

+382
-339
lines changed

src/app/qgslabelinggui.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,7 @@ void QgsLabelingGui::updatePlacementWidgets()
12891289
bool showLineFrame = false;
12901290
bool showCentroidFrame = false;
12911291
bool showQuadrantFrame = false;
1292+
bool showFixedQuadrantFrame = false;
12921293
bool showOffsetFrame = false;
12931294
bool showDistanceFrame = false;
12941295
bool showRotationFrame = false;
@@ -1302,12 +1303,17 @@ void QgsLabelingGui::updatePlacementWidgets()
13021303
showCentroidFrame = ( curWdgt == pagePolygon && radAroundCentroid->isChecked() );
13031304
showDistanceFrame = true;
13041305
//showRotationFrame = true; // TODO: uncomment when supported
1306+
if ( curWdgt == pagePoint )
1307+
{
1308+
showQuadrantFrame = true;
1309+
}
13051310
}
13061311
else if (( curWdgt == pagePoint && radOverPoint->isChecked() )
13071312
|| ( curWdgt == pagePolygon && radOverCentroid->isChecked() ) )
13081313
{
13091314
showCentroidFrame = ( curWdgt == pagePolygon && radOverCentroid->isChecked() );
13101315
showQuadrantFrame = true;
1316+
showFixedQuadrantFrame = true;
13111317
showOffsetFrame = true;
13121318
showRotationFrame = true;
13131319
}
@@ -1331,6 +1337,7 @@ void QgsLabelingGui::updatePlacementWidgets()
13311337
mPlacementLineFrame->setVisible( showLineFrame );
13321338
mPlacementCentroidFrame->setVisible( showCentroidFrame );
13331339
mPlacementQuadrantFrame->setVisible( showQuadrantFrame );
1340+
mPlacementFixedQuadrantFrame->setVisible( showFixedQuadrantFrame );
13341341
mPlacementOffsetFrame->setVisible( showOffsetFrame );
13351342
mPlacementDistanceFrame->setVisible( showDistanceFrame );
13361343
mPlacementRotationFrame->setVisible( showRotationFrame );

src/core/pal/feature.cpp

+66-51
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "labelposition.h"
4848
#include "pointset.h"
4949
#include "util.h"
50+
#include "qgis.h"
5051

5152
#ifndef M_PI
5253
#define M_PI 3.14159265358979323846
@@ -75,6 +76,7 @@ namespace pal
7576
, fixedAngle( 0.0 )
7677
, repeatDist( 0.0 )
7778
, alwaysShow( false )
79+
, mFixedQuadrant( false )
7880
{
7981
assert( finite( lx ) && finite( ly ) );
8082
}
@@ -351,7 +353,25 @@ namespace pal
351353
}
352354
}
353355

354-
if ( f->offsetPos )
356+
if ( f->layer->getArrangement() == P_POINT )
357+
{
358+
//if in "around point" placement mode, then we use the label distance to determine
359+
//the label's offset
360+
if ( qgsDoubleNear( f->quadOffsetX , 0.0 ) )
361+
{
362+
ydiff += f->quadOffsetY * f->distlabel;
363+
}
364+
else if ( qgsDoubleNear( f->quadOffsetY, 0.0 ) )
365+
{
366+
xdiff += f->quadOffsetX * f->distlabel;
367+
}
368+
else
369+
{
370+
xdiff += f->quadOffsetX * M_SQRT1_2 * f->distlabel;
371+
ydiff += f->quadOffsetY * M_SQRT1_2 * f->distlabel;
372+
}
373+
}
374+
else if ( f->offsetPos )
355375
{
356376
if ( f->offsetPosX != 0 )
357377
{
@@ -394,26 +414,20 @@ namespace pal
394414
f->layer->pal->map_unit,
395415
dpi, scale, delta_width );
396416

397-
int nbp = f->layer->pal->point_p;
417+
int numberCandidates = f->layer->pal->point_p;
398418

399419
//std::cout << "Nbp : " << nbp << std::endl;
400-
401-
int i;
402420
int icost = 0;
403421
int inc = 2;
404422

405-
double alpha;
406-
double beta = 2 * M_PI / nbp; /* angle bw 2 pos */
423+
double candidateAngleIncrement = 2 * M_PI / numberCandidates; /* angle bw 2 pos */
407424

408-
double lx, ly; /* label pos */
409-
410-
/* various alpha */
425+
/* various angles */
411426
double a90 = M_PI / 2;
412427
double a180 = M_PI;
413428
double a270 = a180 + a90;
414429
double a360 = 2 * M_PI;
415430

416-
417431
double gamma1, gamma2;
418432

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

429-
430443
if ( gamma1 > a90 / 3.0 )
431444
gamma1 = a90 / 3.0;
432445

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

442-
*lPos = new LabelPosition *[nbp];
455+
*lPos = new LabelPosition *[numberCandidates];
443456

444-
for ( i = 0, alpha = M_PI / 4; i < nbp; i++, alpha += beta )
457+
int i;
458+
double angleToCandidate;
459+
for ( i = 0, angleToCandidate = M_PI / 4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement )
445460
{
446-
lx = x;
447-
ly = y;
461+
double labelX = x;
462+
double labelY = y;
448463

449-
if ( alpha > a360 )
450-
alpha -= a360;
464+
if ( angleToCandidate > a360 )
465+
angleToCandidate -= a360;
451466

452467
LabelPosition::Quadrant quadrant = LabelPosition::QuadrantOver;
453468

454-
if ( alpha < gamma1 || alpha > a360 - gamma1 ) // on the right
469+
if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 ) // on the right
455470
{
456-
lx += distlabel;
457-
double iota = ( alpha + gamma1 );
471+
labelX += distlabel;
472+
double iota = ( angleToCandidate + gamma1 );
458473
if ( iota > a360 - gamma1 )
459474
iota -= a360;
460475

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

464479
quadrant = LabelPosition::QuadrantRight;
465480
}
466-
else if ( alpha < a90 - gamma2 ) // top-right
481+
else if ( angleToCandidate < a90 - gamma2 ) // top-right
467482
{
468-
lx += distlabel * cos( alpha );
469-
ly += distlabel * sin( alpha );
483+
labelX += distlabel * cos( angleToCandidate );
484+
labelY += distlabel * sin( angleToCandidate );
470485
quadrant = LabelPosition::QuadrantAboveRight;
471486
}
472-
else if ( alpha < a90 + gamma2 ) // top
487+
else if ( angleToCandidate < a90 + gamma2 ) // top
473488
{
474489
//lx += -xrm/2.0 - tan(alpha+a90)*(distlabel + yrm/2);
475-
lx += -xrm * ( alpha - a90 + gamma2 ) / ( 2 * gamma2 );
476-
ly += distlabel;
490+
labelX += -xrm * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
491+
labelY += distlabel;
477492
quadrant = LabelPosition::QuadrantAbove;
478493
}
479-
else if ( alpha < a180 - gamma1 ) // top left
494+
else if ( angleToCandidate < a180 - gamma1 ) // top left
480495
{
481-
lx += distlabel * cos( alpha ) - xrm;
482-
ly += distlabel * sin( alpha );
496+
labelX += distlabel * cos( angleToCandidate ) - xrm;
497+
labelY += distlabel * sin( angleToCandidate );
483498
quadrant = LabelPosition::QuadrantAboveLeft;
484499
}
485-
else if ( alpha < a180 + gamma1 ) // left
500+
else if ( angleToCandidate < a180 + gamma1 ) // left
486501
{
487-
lx += -distlabel - xrm;
502+
labelX += -distlabel - xrm;
488503
//ly += -yrm/2.0 - tan(alpha)*(distlabel + xrm/2);
489-
ly += - ( alpha - a180 + gamma1 ) * yrm / ( 2 * gamma1 );
504+
labelY += - ( angleToCandidate - a180 + gamma1 ) * yrm / ( 2 * gamma1 );
490505
quadrant = LabelPosition::QuadrantLeft;
491506
}
492-
else if ( alpha < a270 - gamma2 ) // down - left
507+
else if ( angleToCandidate < a270 - gamma2 ) // down - left
493508
{
494-
lx += distlabel * cos( alpha ) - xrm;
495-
ly += distlabel * sin( alpha ) - yrm;
509+
labelX += distlabel * cos( angleToCandidate ) - xrm;
510+
labelY += distlabel * sin( angleToCandidate ) - yrm;
496511
quadrant = LabelPosition::QuadrantBelowLeft;
497512
}
498-
else if ( alpha < a270 + gamma2 ) // down
513+
else if ( angleToCandidate < a270 + gamma2 ) // down
499514
{
500-
ly += -distlabel - yrm;
515+
labelY += -distlabel - yrm;
501516
//lx += -xrm/2.0 + tan(alpha+a90)*(distlabel + yrm/2);
502-
lx += -xrm + ( alpha - a270 + gamma2 ) * xrm / ( 2 * gamma2 );
517+
labelX += -xrm + ( angleToCandidate - a270 + gamma2 ) * xrm / ( 2 * gamma2 );
503518
quadrant = LabelPosition::QuadrantBelow;
504519
}
505-
else if ( alpha < a360 ) // down - right
520+
else if ( angleToCandidate < a360 ) // down - right
506521
{
507-
lx += distlabel * cos( alpha );
508-
ly += distlabel * sin( alpha ) - yrm;
522+
labelX += distlabel * cos( angleToCandidate );
523+
labelY += distlabel * sin( angleToCandidate ) - yrm;
509524
quadrant = LabelPosition::QuadrantBelowRight;
510525
}
511526

512527
double cost;
513528

514-
if ( nbp == 1 )
529+
if ( numberCandidates == 1 )
515530
cost = 0.0001;
516531
else
517-
cost = 0.0001 + 0.0020 * double( icost ) / double( nbp - 1 );
532+
cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );
518533

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

521536
icost += inc;
522537

523-
if ( icost == nbp )
538+
if ( icost == numberCandidates )
524539
{
525-
icost = nbp - 1;
540+
icost = numberCandidates - 1;
526541
inc = -2;
527542
}
528-
else if ( icost > nbp )
543+
else if ( icost > numberCandidates )
529544
{
530-
icost = nbp - 2;
545+
icost = numberCandidates - 2;
531546
inc = -2;
532547
}
533548

534549
}
535550

536-
return nbp;
551+
return numberCandidates;
537552
}
538553

539554
// TODO work with squared distance by remonving call to sqrt or dist_euc2d
@@ -1323,7 +1338,7 @@ namespace pal
13231338
switch ( type )
13241339
{
13251340
case GEOS_POINT:
1326-
if ( f->layer->getArrangement() == P_POINT_OVER )
1341+
if ( f->layer->getArrangement() == P_POINT_OVER || f->fixedQuadrant() )
13271342
nbp = setPositionOverPoint( x[0], y[0], scale, lPos, delta, angle );
13281343
else
13291344
nbp = setPositionForPoint( x[0], y[0], scale, lPos, delta, angle );

src/core/pal/feature.h

+10
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ namespace pal
8888
//Set label position of the feature to fixed x/y values
8989
void setFixedPosition( double x, double y ) { fixedPos = true; fixedPosX = x; fixedPosY = y;}
9090
void setQuadOffset( double x, double y ) { quadOffset = true; quadOffsetX = x; quadOffsetY = y;}
91+
92+
/** Sets whether the quadrant for the label must be respected. This can be used
93+
* to fix the quadrant for specific features when using an "around point" placement.
94+
*/
95+
void setFixedQuadrant( bool fixed ) { mFixedQuadrant = fixed; }
96+
bool fixedQuadrant() const { return mFixedQuadrant; }
97+
9198
void setPosOffset( double x, double y ) { offsetPos = true; offsetPosX = x; offsetPosY = y;}
9299
bool fixedPosition() const { return fixedPos; }
93100
//Set label rotation to fixed value
@@ -126,6 +133,9 @@ namespace pal
126133
// array of parts - possibly not necessary
127134
//int nPart;
128135
//FeaturePart** parts;
136+
private:
137+
138+
bool mFixedQuadrant;
129139
};
130140

131141
/**

src/core/qgspallabeling.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1799,6 +1799,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
17991799
double offsetX = 0.0, offsetY = 0.0;
18001800

18011801
//data defined quadrant offset?
1802+
bool ddFixedQuad = false;
18021803
QuadrantPosition quadOff = quadOffset;
18031804
if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal ) )
18041805
{
@@ -1808,6 +1809,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
18081809
if ( ok && 0 <= quadInt && quadInt <= 8 )
18091810
{
18101811
quadOff = ( QuadrantPosition )quadInt;
1812+
ddFixedQuad = true;
18111813
}
18121814
}
18131815

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

2171+
if ( ddFixedQuad )
2172+
{
2173+
feat->setFixedQuadrant( true );
2174+
}
21692175

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

0 commit comments

Comments
 (0)