Skip to content

Commit 34e2bea

Browse files
fritsvanveennyalldawson
authored andcommittedAug 17, 2016
'Using perimeter (curved)' labels on polygon layers will now respect 'Show upside-down labels' setting.
1 parent d6b419a commit 34e2bea

File tree

6 files changed

+169
-138
lines changed

6 files changed

+169
-138
lines changed
 

‎src/core/pal/feature.cpp

Lines changed: 127 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList<LabelPosition*>& l
968968
}
969969

970970

971-
LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, double* path_distances, int orientation, int index, double distance )
971+
LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, double* path_distances, int& orientation, int index, double distance, bool& flip )
972972
{
973973
// Check that the given distance is on the given index and find the correct index and distance if not
974974
while ( distance < 0 && index > 1 )
@@ -995,10 +995,6 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
995995

996996
LabelInfo* li = mLF->curvedLabelInfo();
997997

998-
// Keep track of the initial index,distance incase we need to re-call get_placement_offset
999-
int initial_index = index;
1000-
double initial_distance = distance;
1001-
1002998
double string_height = li->label_height;
1003999
double old_x = path_positions->x[index-1];
10041000
double old_y = path_positions->y[index-1];
@@ -1018,14 +1014,18 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
10181014

10191015
LabelPosition* slp = nullptr;
10201016
LabelPosition* slp_tmp = nullptr;
1021-
// current_placement = placement_result()
10221017
double angle = atan2( -dy, dx );
10231018

10241019
bool orientation_forced = ( orientation != 0 ); // Whether the orientation was set by the caller
10251020
if ( !orientation_forced )
10261021
orientation = ( angle > 0.55 * M_PI || angle < -0.45 * M_PI ? -1 : 1 );
10271022

1028-
int upside_down_char_count = 0; // Count of characters that are placed upside down.
1023+
if ( !isUprightLabel() )
1024+
{
1025+
if ( orientation != 1 )
1026+
flip = true; // Report to the caller, that the orientation is flipped
1027+
orientation = 1;
1028+
}
10291029

10301030
for ( int i = 0; i < li->char_num; i++ )
10311031
{
@@ -1086,7 +1086,6 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
10861086

10871087
// Calculate angle from the start of the character to the end based on start_/end_ position
10881088
angle = atan2( start_y - end_y, end_x - start_x );
1089-
//angle = atan2(end_y-start_y, end_x-start_x);
10901089

10911090
// Test last_character_angle vs angle
10921091
// since our rendering angle has changed then check against our
@@ -1108,7 +1107,10 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
11081107
// and we're calculating the mean line here
11091108
double dist = 0.9 * li->label_height / 2;
11101109
if ( orientation < 0 )
1110+
{
11111111
dist = -dist;
1112+
flip = true;
1113+
}
11121114
start_x += dist * cos( angle + M_PI_2 );
11131115
start_y -= dist * sin( angle + M_PI_2 );
11141116

@@ -1137,36 +1139,15 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
11371139
slp_tmp->setNextPart( tmp );
11381140
slp_tmp = tmp;
11391141

1140-
//current_placement.add_node(ci.character,render_x, -render_y, render_angle);
1141-
//current_placement.add_node(ci.character,render_x - current_placement.starting_x, render_y - current_placement.starting_y, render_angle)
1142-
11431142
// Normalise to 0 <= angle < 2PI
1144-
while ( render_angle >= 2*M_PI ) render_angle -= 2 * M_PI;
1143+
while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
11451144
while ( render_angle < 0 ) render_angle += 2 * M_PI;
11461145

1147-
if ( render_angle > M_PI / 2 && render_angle < 1.5*M_PI )
1148-
upside_down_char_count++;
1146+
if ( render_angle > M_PI / 2 && render_angle < 1.5 * M_PI )
1147+
slp->incrementUpsideDownCharCount();
11491148
}
11501149
// END FOR
11511150

1152-
// If we placed too many characters upside down
1153-
if ( upside_down_char_count >= li->char_num / 2.0 )
1154-
{
1155-
// if we auto-detected the orientation then retry with the opposite orientation
1156-
if ( !orientation_forced )
1157-
{
1158-
orientation = -orientation;
1159-
delete slp;
1160-
slp = curvedPlacementAtOffset( path_positions, path_distances, orientation, initial_index, initial_distance );
1161-
}
1162-
else
1163-
{
1164-
// Otherwise we have failed to find a placement
1165-
delete slp;
1166-
return nullptr;
1167-
}
1168-
}
1169-
11701151
return slp;
11711152
}
11721153

@@ -1231,99 +1212,120 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
12311212
flags = FLAG_ON_LINE; // default flag
12321213
// placements may need to be reversed if using line position dependent orientation
12331214
// and the line has right-to-left direction
1234-
bool reversed = ( !( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false );
1215+
bool reversed = (( flags & FLAG_MAP_ORIENTATION ) ? isRightToLeft : false );
12351216

12361217
// an orientation of 0 means try both orientations and choose the best
12371218
int orientation = 0;
1238-
if ( !( flags & FLAG_MAP_ORIENTATION )
1239-
&& mLF->layer()->arrangement() == QgsPalLayerSettings::PerimeterCurved )
1219+
if ( !( flags & FLAG_MAP_ORIENTATION ) )
12401220
{
1241-
//... but if we are labeling the perimeter of a polygon and using line orientation flags,
1242-
// then we can only accept a single orientation, as we need to ensure that the labels fall
1243-
// inside or outside the polygon (and not mixed)
1221+
//... but if we are using line orientation flags, then we can only accept a single orientation,
1222+
// as we need to ensure that the labels fall inside or outside the polyline or polygon (and not mixed)
12441223
orientation = reversed ? -1 : 1;
12451224
}
12461225

12471226
// generate curved labels
12481227
for ( int i = 0; i*delta < total_distance; i++ )
12491228
{
1250-
LabelPosition* slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta );
1229+
bool flip = false;
1230+
bool orientation_forced = ( orientation != 0 ); // Whether the orientation was set by the caller
1231+
LabelPosition* slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta, flip );
1232+
if ( slp == nullptr )
1233+
continue;
12511234

1252-
if ( slp )
1235+
// If we placed too many characters upside down
1236+
if ( slp->upsideDownCharCount() >= li->char_num / 2.0 )
12531237
{
1254-
// evaluate cost
1255-
double angle_diff = 0.0, angle_last = 0.0, diff;
1256-
LabelPosition* tmp = slp;
1257-
double sin_avg = 0, cos_avg = 0;
1258-
while ( tmp )
1238+
// if we auto-detected the orientation then retry with the opposite orientation
1239+
if ( !orientation_forced )
12591240
{
1260-
if ( tmp != slp ) // not first?
1261-
{
1262-
diff = fabs( tmp->getAlpha() - angle_last );
1263-
if ( diff > 2*M_PI ) diff -= 2 * M_PI;
1264-
diff = qMin( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg...
1265-
angle_diff += diff;
1266-
}
1241+
orientation = -orientation;
1242+
delete slp;
1243+
slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta, flip );
1244+
}
1245+
else if ( isUprightLabel() && !flip )
1246+
{
1247+
// Retry with the opposite orientation
1248+
orientation = -orientation;
1249+
delete slp;
1250+
slp = curvedPlacementAtOffset( mapShape, path_distances, orientation, 1, i * delta, flip );
1251+
}
1252+
}
1253+
if ( slp == nullptr )
1254+
continue;
12671255

1268-
sin_avg += sin( tmp->getAlpha() );
1269-
cos_avg += cos( tmp->getAlpha() );
1270-
angle_last = tmp->getAlpha();
1271-
tmp = tmp->getNextPart();
1256+
// evaluate cost
1257+
double angle_diff = 0.0, angle_last = 0.0, diff;
1258+
LabelPosition* tmp = slp;
1259+
double sin_avg = 0, cos_avg = 0;
1260+
while ( tmp )
1261+
{
1262+
if ( tmp != slp ) // not first?
1263+
{
1264+
diff = fabs( tmp->getAlpha() - angle_last );
1265+
if ( diff > 2*M_PI ) diff -= 2 * M_PI;
1266+
diff = qMin( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg...
1267+
angle_diff += diff;
12721268
}
12731269

1274-
double angle_diff_avg = li->char_num > 1 ? ( angle_diff / ( li->char_num - 1 ) ) : 0; // <0, pi> but pi/8 is much already
1275-
double cost = angle_diff_avg / 100; // <0, 0.031 > but usually <0, 0.003 >
1276-
if ( cost < 0.0001 ) cost = 0.0001;
1270+
sin_avg += sin( tmp->getAlpha() );
1271+
cos_avg += cos( tmp->getAlpha() );
1272+
angle_last = tmp->getAlpha();
1273+
tmp = tmp->getNextPart();
1274+
}
1275+
1276+
double angle_diff_avg = li->char_num > 1 ? ( angle_diff / ( li->char_num - 1 ) ) : 0; // <0, pi> but pi/8 is much already
1277+
double cost = angle_diff_avg / 100; // <0, 0.031 > but usually <0, 0.003 >
1278+
if ( cost < 0.0001 ) cost = 0.0001;
12771279

1278-
// penalize positions which are further from the line's midpoint
1279-
double labelCenter = ( i * delta ) + getLabelWidth() / 2;
1280-
double costCenter = qAbs( total_distance / 2 - labelCenter ) / total_distance; // <0, 0.5>
1281-
cost += costCenter / 1000; // < 0, 0.0005 >
1282-
slp->setCost( cost );
1280+
// penalize positions which are further from the line's midpoint
1281+
double labelCenter = ( i * delta ) + getLabelWidth() / 2;
1282+
double costCenter = qAbs( total_distance / 2 - labelCenter ) / total_distance; // <0, 0.5>
1283+
cost += costCenter / 1000; // < 0, 0.0005 >
1284+
slp->setCost( cost );
12831285

1284-
// average angle is calculated with respect to periodicity of angles
1285-
double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num );
1286-
// displacement - we loop through 3 times, generating above, online then below line placements successively
1287-
for ( int i = 0; i <= 2; ++i )
1286+
// average angle is calculated with respect to periodicity of angles
1287+
double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num );
1288+
bool localreversed = flip ? !reversed : reversed;
1289+
// displacement - we loop through 3 times, generating above, online then below line placements successively
1290+
for ( int i = 0; i <= 2; ++i )
1291+
{
1292+
LabelPosition* p = nullptr;
1293+
if ( i == 0 && (( !localreversed && ( flags & FLAG_ABOVE_LINE ) ) || ( localreversed && ( flags & FLAG_BELOW_LINE ) ) ) )
1294+
p = _createCurvedCandidate( slp, angle_avg, mLF->distLabel() + li->label_height / 2 );
1295+
if ( i == 1 && flags & FLAG_ON_LINE )
12881296
{
1289-
LabelPosition* p = nullptr;
1290-
if ( i == 0 && (( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ) ) )
1291-
p = _createCurvedCandidate( slp, angle_avg, mLF->distLabel() + li->label_height / 2 );
1292-
if ( i == 1 && flags & FLAG_ON_LINE )
1293-
{
1294-
p = _createCurvedCandidate( slp, angle_avg, 0 );
1295-
p->setCost( p->cost() + 0.002 );
1296-
}
1297-
if ( i == 2 && (( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
1297+
p = _createCurvedCandidate( slp, angle_avg, 0 );
1298+
p->setCost( p->cost() + 0.002 );
1299+
}
1300+
if ( i == 2 && (( !localreversed && ( flags & FLAG_BELOW_LINE ) ) || ( localreversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
1301+
{
1302+
p = _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 - mLF->distLabel() );
1303+
p->setCost( p->cost() + 0.001 );
1304+
}
1305+
1306+
if ( p && mLF->permissibleZonePrepared() )
1307+
{
1308+
bool within = true;
1309+
LabelPosition* currentPos = p;
1310+
while ( within && currentPos )
12981311
{
1299-
p = _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 - mLF->distLabel() );
1300-
p->setCost( p->cost() + 0.001 );
1312+
within = GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), currentPos->getX(), currentPos->getY(), currentPos->getWidth(), currentPos->getHeight(), currentPos->getAlpha() );
1313+
currentPos = currentPos->getNextPart();
13011314
}
1302-
1303-
if ( p && mLF->permissibleZonePrepared() )
1315+
if ( !within )
13041316
{
1305-
bool within = true;
1306-
LabelPosition* currentPos = p;
1307-
while ( within && currentPos )
1308-
{
1309-
within = GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), currentPos->getX(), currentPos->getY(), currentPos->getWidth(), currentPos->getHeight(), currentPos->getAlpha() );
1310-
currentPos = currentPos->getNextPart();
1311-
}
1312-
if ( !within )
1313-
{
1314-
delete p;
1315-
p = nullptr;
1316-
}
1317+
delete p;
1318+
p = nullptr;
13171319
}
1318-
1319-
if ( p )
1320-
positions.append( p );
13211320
}
1322-
// delete original candidate
1323-
delete slp;
1321+
1322+
if ( p )
1323+
positions.append( p );
13241324
}
1325-
}
13261325

1326+
// delete original candidate
1327+
delete slp;
1328+
}
13271329

13281330
int nbp = positions.size();
13291331
for ( int i = 0; i < nbp; i++ )
@@ -1336,9 +1338,6 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
13361338
return nbp;
13371339
}
13381340

1339-
1340-
1341-
13421341
/*
13431342
* seg 2
13441343
* pt3 ____________pt2
@@ -1588,9 +1587,7 @@ int FeaturePart::createCandidates( QList< LabelPosition*>& lPos,
15881587
createCandidatesAroundPoint( x[0], y[0], lPos, angle );
15891588
break;
15901589
case GEOS_LINESTRING:
1591-
if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Curved )
1592-
createCurvedCandidatesAlongLine( lPos, mapShape );
1593-
else if ( mLF->layer()->arrangement() == QgsPalLayerSettings::PerimeterCurved )
1590+
if ( mLF->layer()->isCurved() )
15941591
createCurvedCandidatesAlongLine( lPos, mapShape );
15951592
else
15961593
createCandidatesAlongLine( lPos, mapShape );
@@ -1774,3 +1771,28 @@ double FeaturePart::calculatePriority() const
17741771

17751772
return mLF->priority() >= 0 ? mLF->priority() : mLF->layer()->priority();
17761773
}
1774+
1775+
bool FeaturePart::isUprightLabel() const
1776+
{
1777+
bool uprightLabel = false;
1778+
1779+
switch ( mLF->layer()->upsidedownLabels() )
1780+
{
1781+
case Layer::Upright:
1782+
uprightLabel = true;
1783+
break;
1784+
case Layer::ShowDefined:
1785+
// upright only dynamic labels
1786+
if ( !hasFixedRotation() || ( !hasFixedPosition() && fixedAngle() == 0.0 ) )
1787+
{
1788+
uprightLabel = true;
1789+
}
1790+
break;
1791+
case Layer::ShowAll:
1792+
break;
1793+
default:
1794+
uprightLabel = true;
1795+
}
1796+
return uprightLabel;
1797+
}
1798+

‎src/core/pal/feature.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ namespace pal
178178
int createCandidatesAlongLineNearMidpoint( QList<LabelPosition *> &lPos, PointSet *mapShape, double initialCost = 0.0 );
179179

180180
LabelPosition* curvedPlacementAtOffset( PointSet* path_positions, double* path_distances,
181-
int orientation, int index, double distance );
181+
int& orientation, int index, double distance, bool& flip );
182182

183183
/** Generate curved candidates for line features.
184184
* @param lPos pointer to an array of candidates, will be filled by generated candidates
@@ -213,13 +213,28 @@ namespace pal
213213
double getLabelHeight() const { return mLF->size().height(); }
214214
double getLabelDistance() const { return mLF->distLabel(); }
215215

216-
bool getFixedRotation() { return mLF->hasFixedAngle(); }
217-
double getLabelAngle() { return mLF->fixedAngle(); }
218-
bool getFixedPosition() { return mLF->hasFixedPosition(); }
219-
bool getAlwaysShow() { return mLF->alwaysShow(); }
220-
bool isObstacle() { return mLF->isObstacle(); }
221-
double obstacleFactor() { return mLF->obstacleFactor(); }
222-
double repeatDistance() { return mLF->repeatDistance(); }
216+
//! Returns true if the feature's label has a fixed rotation
217+
bool hasFixedRotation() const { return mLF->hasFixedAngle(); }
218+
219+
//! Returns the fixed angle for the feature's label
220+
double fixedAngle() const { return mLF->fixedAngle(); }
221+
222+
//! Returns true if the feature's label has a fixed position
223+
bool hasFixedPosition() const { return mLF->hasFixedPosition(); }
224+
225+
//! Returns true if the feature's label should always been shown,
226+
//! even when it collides with other labels
227+
bool alwaysShow() const { return mLF->alwaysShow(); }
228+
229+
//! Returns true if the feature should act as an obstacle to labels
230+
bool isObstacle() const { return mLF->isObstacle(); }
231+
232+
//! Returns the feature's obstacle factor, which represents the penalty
233+
//! incurred for a label to overlap the feature
234+
double obstacleFactor() const { return mLF->obstacleFactor(); }
235+
236+
//! Returns the distance between repeating labels for this feature
237+
double repeatDistance() const { return mLF->repeatDistance(); }
223238

224239
//! Get number of holes (inner rings) - they are considered as obstacles
225240
int getNumSelfObstacles() const { return mHoles.count(); }
@@ -242,6 +257,8 @@ namespace pal
242257
*/
243258
double calculatePriority() const;
244259

260+
//! Returns true if feature's label must be displayed upright
261+
bool isUprightLabel() const;
245262

246263
protected:
247264

0 commit comments

Comments
 (0)