@@ -968,7 +968,7 @@ int FeaturePart::createCandidatesAlongLineNearMidpoint( QList<LabelPosition*>& l
968
968
}
969
969
970
970
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 )
972
972
{
973
973
// Check that the given distance is on the given index and find the correct index and distance if not
974
974
while ( distance < 0 && index > 1 )
@@ -995,10 +995,6 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
995
995
996
996
LabelInfo* li = mLF ->curvedLabelInfo ();
997
997
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
-
1002
998
double string_height = li->label_height ;
1003
999
double old_x = path_positions->x [index-1 ];
1004
1000
double old_y = path_positions->y [index-1 ];
@@ -1018,14 +1014,18 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
1018
1014
1019
1015
LabelPosition* slp = nullptr ;
1020
1016
LabelPosition* slp_tmp = nullptr ;
1021
- // current_placement = placement_result()
1022
1017
double angle = atan2 ( -dy, dx );
1023
1018
1024
1019
bool orientation_forced = ( orientation != 0 ); // Whether the orientation was set by the caller
1025
1020
if ( !orientation_forced )
1026
1021
orientation = ( angle > 0.55 * M_PI || angle < -0.45 * M_PI ? -1 : 1 );
1027
1022
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
+ }
1029
1029
1030
1030
for ( int i = 0 ; i < li->char_num ; i++ )
1031
1031
{
@@ -1086,7 +1086,6 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
1086
1086
1087
1087
// Calculate angle from the start of the character to the end based on start_/end_ position
1088
1088
angle = atan2 ( start_y - end_y, end_x - start_x );
1089
- // angle = atan2(end_y-start_y, end_x-start_x);
1090
1089
1091
1090
// Test last_character_angle vs angle
1092
1091
// since our rendering angle has changed then check against our
@@ -1108,7 +1107,10 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
1108
1107
// and we're calculating the mean line here
1109
1108
double dist = 0.9 * li->label_height / 2 ;
1110
1109
if ( orientation < 0 )
1110
+ {
1111
1111
dist = -dist;
1112
+ flip = true ;
1113
+ }
1112
1114
start_x += dist * cos ( angle + M_PI_2 );
1113
1115
start_y -= dist * sin ( angle + M_PI_2 );
1114
1116
@@ -1137,36 +1139,15 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
1137
1139
slp_tmp->setNextPart ( tmp );
1138
1140
slp_tmp = tmp;
1139
1141
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
-
1143
1142
// 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;
1145
1144
while ( render_angle < 0 ) render_angle += 2 * M_PI;
1146
1145
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 () ;
1149
1148
}
1150
1149
// END FOR
1151
1150
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
-
1170
1151
return slp;
1171
1152
}
1172
1153
@@ -1231,99 +1212,120 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
1231
1212
flags = FLAG_ON_LINE; // default flag
1232
1213
// placements may need to be reversed if using line position dependent orientation
1233
1214
// 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 );
1235
1216
1236
1217
// an orientation of 0 means try both orientations and choose the best
1237
1218
int orientation = 0 ;
1238
- if ( !( flags & FLAG_MAP_ORIENTATION )
1239
- && mLF ->layer ()->arrangement () == QgsPalLayerSettings::PerimeterCurved )
1219
+ if ( !( flags & FLAG_MAP_ORIENTATION ) )
1240
1220
{
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)
1244
1223
orientation = reversed ? -1 : 1 ;
1245
1224
}
1246
1225
1247
1226
// generate curved labels
1248
1227
for ( int i = 0 ; i*delta < total_distance; i++ )
1249
1228
{
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 ;
1251
1234
1252
- if ( slp )
1235
+ // If we placed too many characters upside down
1236
+ if ( slp->upsideDownCharCount () >= li->char_num / 2.0 )
1253
1237
{
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 )
1259
1240
{
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 ;
1267
1255
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;
1272
1268
}
1273
1269
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 ;
1277
1279
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 );
1283
1285
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 )
1288
1296
{
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 )
1298
1311
{
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 ( );
1301
1314
}
1302
-
1303
- if ( p && mLF ->permissibleZonePrepared () )
1315
+ if ( !within )
1304
1316
{
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 ;
1317
1319
}
1318
-
1319
- if ( p )
1320
- positions.append ( p );
1321
1320
}
1322
- // delete original candidate
1323
- delete slp;
1321
+
1322
+ if ( p )
1323
+ positions.append ( p );
1324
1324
}
1325
- }
1326
1325
1326
+ // delete original candidate
1327
+ delete slp;
1328
+ }
1327
1329
1328
1330
int nbp = positions.size ();
1329
1331
for ( int i = 0 ; i < nbp; i++ )
@@ -1336,9 +1338,6 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
1336
1338
return nbp;
1337
1339
}
1338
1340
1339
-
1340
-
1341
-
1342
1341
/*
1343
1342
* seg 2
1344
1343
* pt3 ____________pt2
@@ -1588,9 +1587,7 @@ int FeaturePart::createCandidates( QList< LabelPosition*>& lPos,
1588
1587
createCandidatesAroundPoint ( x[0 ], y[0 ], lPos, angle );
1589
1588
break ;
1590
1589
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 () )
1594
1591
createCurvedCandidatesAlongLine ( lPos, mapShape );
1595
1592
else
1596
1593
createCandidatesAlongLine ( lPos, mapShape );
@@ -1774,3 +1771,28 @@ double FeaturePart::calculatePriority() const
1774
1771
1775
1772
return mLF ->priority () >= 0 ? mLF ->priority () : mLF ->layer ()->priority ();
1776
1773
}
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
+
12 commit comments
fritsvanveen commentedon Aug 25, 2016
@nyalldawson

I have one layer, where the labels are shown on the wrong size of the polygon. They should be on the inside. Is there such a thing as an inverted polygon in a database? I've also drawn an inner glow, and that is shown correctly. I can see in QgsSymbol::renderFeature that a MultiPolygon is drawn, but that is also true for other layers, which are ok. I'm a bit at a loss here.
fritsvanveen commentedon Aug 26, 2016
@nyalldawson


I've attached some more examples. Only Katwijk is displayed correctly in 2.99.0, that is with labels inside the polygon. In 2.16.1 the polygons which are cutoff are also displayed with labels inside. Can you provide some hints how I can proceed to investigate this?
Oh, curved or straight labels exhibit the same problem.
nyalldawson commentedon Aug 29, 2016
@fritsvanveen my usual approach here is to enable labels both above and below the line, and then enable the option to only draw labels which fit inside the polygon.
BTW - i've noticed since your last commit re upside down labels that many curved labels for line geometry layers are now drawn incorrectly upside down. Are you able to look into this?
fritsvanveen commentedon Aug 29, 2016
@nyalldawson Also for polygon layers. I'll look into it.
fritsvanveen commentedon Sep 2, 2016
@nyalldawson Just a quick update. The upsidedown bug is fixed. Variable 'orientation' should have been primed inside the for loop. However, with 'Line orientation dependent position' off, some labels are plotted on the wrong side of the line.
nyalldawson commentedon Sep 3, 2016
@fritsvanveen great! is there an updated commit I should be looking at, or will you update the PR?
fritsvanveen commentedon Sep 3, 2016
@nyalldawson Not yet, let's wait until I fixed the other bug.
fritsvanveen commentedon Sep 3, 2016
@nyalldawson I'm confused about these lines in createCandidatesAlongLineNearStraightSegment
Should the comment not read 'using map orientation'
nyalldawson commentedon Sep 3, 2016
Yes - sounds like that comment is wrong
fritsvanveen commentedon Sep 4, 2016
@nyalldawson Made a commit on my fork https://github.com/fritsvanveen/QGIS
fritsvanveen commentedon Sep 4, 2016
@nyalldawson Made another commit on my fork https://github.com/fritsvanveen/QGIS
Ik works now, but I want to clean up the code.
fritsvanveen commentedon Sep 5, 2016
@nyalldawson Cleaned up code committed.