43 #include <QLinkedList>    53   mGeos = 
const_cast<GEOSGeometry *
>( geom );
    59   for ( 
int i = 0; i < 
mHoles.count(); i++ )
    61     mHoles.at( i )->holeOf = 
this;
    73     mHoles.last()->holeOf = 
this;
    87   const GEOSCoordSequence *coordSeq = 
nullptr;
    90   type = GEOSGeomTypeId_r( geosctxt, geom );
    92   if ( 
type == GEOS_POLYGON )
    94     if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
    96       int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
    98       for ( 
int i = 0; i < numHoles; ++i )
   100         const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
   112     geom = GEOSGetExteriorRing_r( geosctxt, geom );
   121   nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
   122   coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
   125   xmin = 
ymin = std::numeric_limits<double>::max();
   126   xmax = 
ymax = std::numeric_limits<double>::lowest();
   133   for ( 
int i = 0; i < 
nbPoints; ++i )
   135     GEOSCoordSeq_getX_r( geosctxt, coordSeq, i, &
x[i] );
   136     GEOSCoordSeq_getY_r( geosctxt, coordSeq, i, &
y[i] );
   175   qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
   177   if ( quadOffsetX < 0 )
   179     if ( quadOffsetY < 0 )
   183     else if ( quadOffsetY > 0 )
   192   else  if ( quadOffsetX > 0 )
   194     if ( quadOffsetY < 0 )
   198     else if ( quadOffsetY > 0 )
   209     if ( quadOffsetY < 0 )
   213     else if ( quadOffsetY > 0 )
   232   double cost = 0.0001;
   235   double xdiff = -labelW / 2.0;
   236   double ydiff = -labelH / 2.0;
   251       double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
   252       double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
   288   double lx = x + xdiff;
   289   double ly = y + ydiff;
   299   lPos << 
new LabelPosition( 
id, lx, ly, labelW, labelH, angle, cost, 
this, 
false, quadrantFromOffset() );
   314   double cost = 0.0001;
   327         deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
   328         deltaY = -visualMargin.
bottom() + symbolHeightOffset;
   334         deltaX = -labelWidth / 4.0 - visualMargin.
left();
   335         deltaY = -visualMargin.
bottom() + symbolHeightOffset;
   341         deltaX = -labelWidth / 2.0;
   342         deltaY = -visualMargin.
bottom() + symbolHeightOffset;
   348         deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
   349         deltaY = -visualMargin.
bottom() + symbolHeightOffset;
   355         deltaX = - visualMargin.
left() + symbolWidthOffset;
   356         deltaY = -visualMargin.
bottom() + symbolHeightOffset;
   362         deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
   363         deltaY = -labelHeight / 2.0;
   369         deltaX = -visualMargin.
left() + symbolWidthOffset;
   370         deltaY = -labelHeight / 2.0;
   376         deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
   377         deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
   383         deltaX = -labelWidth / 4.0 - visualMargin.
left();
   384         deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
   390         deltaX = -labelWidth / 2.0;
   391         deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
   397         deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
   398         deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
   404         deltaX = -visualMargin.
left() + symbolWidthOffset;
   405         deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
   410     double referenceX = std::cos( alpha ) * distanceToLabel + 
x;
   411     double referenceY = std::sin( alpha ) * distanceToLabel + 
y;
   413     double labelX = referenceX + deltaX;
   414     double labelY = referenceY + deltaY;
   418       lPos << 
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost, 
this, 
false, quadrant );
   440   double candidateAngleIncrement = 2 * M_PI / numberCandidates; 
   445   double a270 = a180 + a90;
   446   double a360 = 2 * M_PI;
   448   double gamma1, gamma2;
   450   if ( distanceToLabel > 0 )
   452     gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
   453     gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
   457     gamma1 = gamma2 = a90 / 3.0;
   460   if ( gamma1 > a90 / 3.0 )
   463   if ( gamma2 > a90 / 3.0 )
   466   QList< LabelPosition * > candidates;
   469   double angleToCandidate;
   470   for ( i = 0, angleToCandidate = M_PI_4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement )
   475     if ( angleToCandidate > a360 )
   476       angleToCandidate -= a360;
   480     if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )  
   482       labelX += distanceToLabel;
   483       double iota = ( angleToCandidate + gamma1 );
   484       if ( iota > a360 - gamma1 )
   488       labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
   492     else if ( angleToCandidate < a90 - gamma2 )  
   494       labelX += distanceToLabel * std::cos( angleToCandidate );
   495       labelY += distanceToLabel * std::sin( angleToCandidate );
   498     else if ( angleToCandidate < a90 + gamma2 ) 
   501       labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
   502       labelY += distanceToLabel;
   505     else if ( angleToCandidate < a180 - gamma1 )  
   507       labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
   508       labelY += distanceToLabel * std::sin( angleToCandidate );
   511     else if ( angleToCandidate < a180 + gamma1 ) 
   513       labelX += -distanceToLabel - labelWidth;
   515       labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
   518     else if ( angleToCandidate < a270 - gamma2 ) 
   520       labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
   521       labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
   524     else if ( angleToCandidate < a270 + gamma2 ) 
   526       labelY += -distanceToLabel - labelHeight;
   528       labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
   531     else if ( angleToCandidate < a360 ) 
   533       labelX += distanceToLabel * std::cos( angleToCandidate );
   534       labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
   540     if ( numberCandidates == 1 )
   543       cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );
   554     candidates << 
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost, 
this, 
false, quadrant );
   558     if ( icost == numberCandidates )
   560       icost = numberCandidates - 1;
   563     else if ( icost > numberCandidates )
   565       icost = numberCandidates - 2;
   571   if ( !candidates.isEmpty() )
   573     for ( 
int i = 0; i < candidates.count(); ++i )
   575       lPos << candidates.at( i );
   579   return candidates.count();
   587   if ( candidates < mLF->
layer()->
pal->line_p )
   605   QVector< int > extremeAngleNodes;
   613   for ( 
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
   615     double x1 = x[i - 1];
   617     double x3 = x[ i == numberNodes - 1 ? 1 : i + 1]; 
   618     double y1 = y[i - 1];
   620     double y3 = y[ i == numberNodes - 1 ? 1 : i + 1]; 
   625     double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
   629     if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
   630       extremeAngleNodes << i;
   632   extremeAngleNodes << numberNodes - 1;
   634   if ( extremeAngleNodes.isEmpty() )
   641   double *segmentLengths = 
new double[ numberNodes - 1 ]; 
   642   double *distanceToSegment = 
new double[ numberNodes ]; 
   643   double totalLineLength = 0.0;
   644   QVector< double > straightSegmentLengths;
   645   QVector< double > straightSegmentAngles;
   646   straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
   647   straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
   648   double currentStraightSegmentLength = 0;
   649   double longestSegmentLength = 0;
   650   int segmentIndex = 0;
   651   double segmentStartX = x[0];
   652   double segmentStartY = y[0];
   653   for ( 
int i = 0; i < numberNodes - 1; i++ )
   656       distanceToSegment[i] = 0;
   658       distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
   661     totalLineLength += segmentLengths[i];
   662     if ( extremeAngleNodes.contains( i ) )
   665       straightSegmentLengths << currentStraightSegmentLength;
   667       longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
   669       currentStraightSegmentLength = 0;
   670       segmentStartX = x[i];
   671       segmentStartY = y[i];
   673     currentStraightSegmentLength += segmentLengths[i];
   675   distanceToSegment[line->
nbPoints - 1] = totalLineLength;
   676   straightSegmentLengths << currentStraightSegmentLength;
   678   longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
   679   double middleOfLine = totalLineLength / 2.0;
   681   if ( totalLineLength < labelWidth )
   683     delete[] segmentLengths;
   684     delete[] distanceToSegment;
   688   double lineStepDistance = ( totalLineLength - labelWidth ); 
   689   lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / 
mLF->
layer()->
pal->line_p );
   691   double distanceToEndOfSegment = 0.0;
   692   int lastNodeInSegment = 0;
   694   for ( 
int i = 0; i < straightSegmentLengths.count(); ++i )
   696     currentStraightSegmentLength = straightSegmentLengths.at( i );
   697     double currentSegmentAngle = straightSegmentAngles.at( i );
   698     lastNodeInSegment = extremeAngleNodes.at( i );
   699     double distanceToStartOfSegment = distanceToEndOfSegment;
   700     distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
   701     double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
   703     if ( currentStraightSegmentLength < labelWidth )
   707     double currentDistanceAlongLine = distanceToStartOfSegment;
   708     double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
   709     double candidateLength = 0.0;
   715     double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength; 
   716     double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2; 
   718     while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
   721       line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
   722       line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
   724       candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
   730       cost = candidateLength / labelWidth;
   736         cost = ( 1 - cost ) / 100; 
   740       double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
   741       double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment ); 
   742       cost += costCenter * 0.0005;  
   749         double costLineCenter = 2 * std::fabs( labelCenter - middleOfLine ) / totalLineLength; 
   750         cost += costLineCenter * 0.0005;  
   753       cost += segmentCost * 0.0005; 
   754       cost += segmentAngleCost * 0.0001; 
   761         angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
   763       beta = angle + M_PI_2;
   768         bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
   778             const double candidateCost = cost + ( reversed ? 0 : 0.001 );
   779             lPos.append( 
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost, 
this, isRightToLeft ) ); 
   786             const double candidateCost = cost + ( !reversed ? 0 : 0.001 ); 
   787             lPos.append( 
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost, 
this, isRightToLeft ) ); 
   794             const double candidateCost = cost + 0.002;
   795             lPos.append( 
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost, 
this, isRightToLeft ) ); 
   801         lPos.append( 
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost, 
this ) ); 
   808       currentDistanceAlongLine += lineStepDistance;
   812   delete[] segmentLengths;
   813   delete[] distanceToSegment;
   831   QList<LabelPosition *> positions;
   838   double *segmentLengths = 
new double[nbPoints - 1]; 
   839   double *distanceToSegment = 
new double[
nbPoints]; 
   841   double totalLineLength = 0.0; 
   842   for ( 
int i = 0; i < line->
nbPoints - 1; i++ )
   845       distanceToSegment[i] = 0;
   847       distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
   850     totalLineLength += segmentLengths[i];
   852   distanceToSegment[line->
nbPoints - 1] = totalLineLength;
   854   double lineStepDistance = ( totalLineLength - labelWidth ); 
   855   double currentDistanceAlongLine = 0;
   857   if ( totalLineLength > labelWidth )
   859     lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / 
mLF->
layer()->
pal->line_p );
   863     currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
   864     lineStepDistance = -1;
   865     totalLineLength = labelWidth;
   870     currentDistanceAlongLine = std::numeric_limits< double >::max();
   873   double candidateLength;
   875   double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
   877   while ( currentDistanceAlongLine < totalLineLength - labelWidth )
   880     line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
   881     line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
   883     if ( currentDistanceAlongLine < 0 )
   886       candidateLength = std::sqrt( ( x[nbPoints - 1] - x[0] ) * ( x[nbPoints - 1] - x[0] )
   887                                    + ( y[nbPoints - 1] - y[0] ) * ( y[nbPoints - 1] - y[0] ) );
   891       candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
   894     cost = candidateLength / labelWidth;
   900       cost = ( 1 - cost ) / 100; 
   904     double costCenter = std::fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength; 
   905     cost += costCenter / 1000;  
   913       angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
   915     beta = angle + M_PI_2;
   920       bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
   930           const double candidateCost = cost + ( !reversed ? 0 : 0.001 ); 
   931           positions.append( 
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost, 
this, isRightToLeft ) ); 
   938           const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
   939           positions.append( 
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost, 
this, isRightToLeft ) ); 
   946           const double candidateCost = cost + 0.002;
   947           positions.append( 
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost, 
this, isRightToLeft ) ); 
   953       positions.append( 
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost, 
this ) ); 
   960     currentDistanceAlongLine += lineStepDistance;
   964     if ( lineStepDistance < 0 )
   970   delete[] segmentLengths;
   971   delete[] distanceToSegment;
   973   lPos.append( positions );
   980   double offsetAlongSegment = offsetAlongLine;
   983   while ( index < path_positions->
nbPoints && offsetAlongSegment > path_distances[index] )
   985     offsetAlongSegment -= path_distances[index];
   988   if ( index >= path_positions->
nbPoints )
   997   const double segment_length = path_distances[index];
  1004   if ( orientation == 0 )       
  1008     double _distance = offsetAlongSegment;
  1009     int endindex = index;
  1011     double startLabelX = 0;
  1012     double startLabelY = 0;
  1013     double endLabelX = 0;
  1014     double endLabelY = 0;
  1015     for ( 
int i = 0; i < li->
char_num; i++ )
  1018       double characterStartX, characterStartY;
  1019       if ( !
nextCharPosition( ci.
width, path_distances[endindex], path_positions, endindex, _distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
  1025         startLabelX = characterStartX;
  1026         startLabelY = characterStartY;
  1031     double dx = endLabelX - startLabelX;
  1032     double dy = endLabelY - startLabelY;
  1033     const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
  1035     bool isRightToLeft = ( lineAngle > 90 || lineAngle < -90 );
  1036     reversed = isRightToLeft;
  1037     orientation = isRightToLeft ? -1 : 1;
  1042     if ( orientation < 0 )
  1045       reversed = !reversed;
  1053   double old_x = path_positions->
x[index - 1];
  1054   double old_y = path_positions->
y[index - 1];
  1056   double new_x = path_positions->
x[index];
  1057   double new_y = path_positions->
y[index];
  1059   double dx = new_x - old_x;
  1060   double dy = new_y - old_y;
  1062   double angle = std::atan2( -dy, dx );
  1064   for ( 
int i = 0; i < li->
char_num; i++ )
  1066     double last_character_angle = 
angle;
  1074     double start_x, start_y, end_x, end_y;
  1075     if ( !
nextCharPosition( ci.
width, path_distances[index], path_positions, index, offsetAlongSegment, start_x, start_y, end_x, end_y ) )
  1082     angle = std::atan2( start_y - end_y, end_x - start_x );
  1087     double angle_delta = last_character_angle - 
angle;
  1089     while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
  1090     while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
  1094               && angle_delta < li->max_char_angle_outside * ( M_PI / 180 ) ) )
  1103     if ( orientation < 0 )
  1108     start_x += dist * std::cos( angle + M_PI_2 );
  1109     start_y -= dist * std::sin( angle + M_PI_2 );
  1111     double render_angle = 
angle;
  1113     double render_x = start_x;
  1114     double render_y = start_y;
  1120     if ( orientation < 0 )
  1123       render_x += ci.
width * std::cos( render_angle ); 
  1124       render_y -= ci.
width * std::sin( render_angle ); 
  1125       render_angle += M_PI;
  1137     while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
  1138     while ( render_angle < 0 ) render_angle += 2 * M_PI;
  1140     if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI )
  1163   std::unique_ptr< double [] > path_distances = qgis::make_unique<double[]>( mapShape->
nbPoints );
  1164   double total_distance = 0;
  1165   double old_x = -1.0, old_y = -1.0;
  1166   for ( 
int i = 0; i < mapShape->
nbPoints; i++ )
  1169       path_distances[i] = 0;
  1171       path_distances[i] = std::sqrt( std::pow( old_x - mapShape->
x[i], 2 ) + std::pow( old_y - mapShape->
y[i], 2 ) );
  1172     old_x = mapShape->
x[i];
  1173     old_y = mapShape->
y[i];
  1175     total_distance += path_distances[i];
  1183   double totalCharacterWidth = 0;
  1184   for ( 
int i = 0; i < li->
char_num; ++i )
  1187   if ( totalCharacterWidth > total_distance )
  1194   QLinkedList<LabelPosition *> positions;
  1202   for ( 
double distanceAlongLineToStartCandidate = 0; distanceAlongLineToStartCandidate < total_distance; distanceAlongLineToStartCandidate += delta )
  1206     bool reversed = 
false;
  1209     int orientation = 0;
  1228         orientation = -orientation;
  1229         slp = 
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
  1236     double angle_diff = 0.0, angle_last = 0.0, diff;
  1238     double sin_avg = 0, cos_avg = 0;
  1243         diff = std::fabs( tmp->
getAlpha() - angle_last );
  1244         if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
  1245         diff = std::min( diff, 2 * M_PI - diff ); 
  1249       sin_avg += std::sin( tmp->
getAlpha() );
  1250       cos_avg += std::cos( tmp->
getAlpha() );
  1255     double angle_diff_avg = li->
char_num > 1 ? ( angle_diff / ( li->
char_num - 1 ) ) : 0; 
  1256     double cost = angle_diff_avg / 100; 
  1257     if ( cost < 0.0001 ) cost = 0.0001;
  1260     double labelCenter = distanceAlongLineToStartCandidate + 
getLabelWidth() / 2;
  1261     double costCenter = std::fabs( total_distance / 2 - labelCenter ) / total_distance; 
  1262     cost += costCenter / 100;  
  1266     double angle_avg = std::atan2( sin_avg / li->
char_num, cos_avg / li->
char_num );
  1267     bool localreversed = flip ? !reversed : reversed;
  1269     for ( 
int i = 0; i <= 2; ++i )
  1276         p = _createCurvedCandidate( slp, angle_avg, 0 );
  1279       if ( i == 2 && ( ( !localreversed && ( flags & FLAG_BELOW_LINE ) ) || ( localreversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
  1289         while ( within && currentPos )
  1302         positions.append( p );
  1309   int nbp = positions.size();
  1310   for ( 
int i = 0; i < nbp; i++ )
  1312     lPos << positions.takeFirst();
  1338   QLinkedList<PointSet *> shapes_toProcess;
  1339   QLinkedList<PointSet *> shapes_final;
  1341   mapShape->
parent = 
nullptr;
  1343   shapes_toProcess.append( mapShape );
  1345   splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight );
  1349   if ( !shapes_final.isEmpty() )
  1351     QLinkedList<LabelPosition *> positions;
  1361     double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
  1367     while ( !shapes_final.isEmpty() )
  1369       PointSet *shape = shapes_final.takeFirst();
  1379     dx = labelWidth / 2.0;
  1380     dy = labelHeight / 2.0;
  1390       for ( bbid = 0; bbid < j; bbid++ )
  1411         bool enoughPlace = 
false;
  1415           px = ( box->
x[0] + box->
x[2] ) / 2 - labelWidth;
  1416           py = ( box->
y[0] + box->
y[2] ) / 2 - labelHeight;
  1422           for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
  1424             for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
  1428                 enoughPlace = 
false;
  1444         else if ( box->
length > 1.5 * labelWidth && box->
width > 1.5 * labelWidth )
  1446           if ( box->
alpha <= M_PI_4 )
  1452             alpha = box->
alpha - M_PI_2;
  1457           alpha = box->
alpha - M_PI_2;
  1464         beta  = std::atan2( labelHeight, labelWidth ) + alpha;
  1470         dlx = std::cos( beta ) * diago;
  1471         dly = std::sin( beta ) * diago;
  1475         px0 = box->
width / 2.0;
  1478         px0 -= std::ceil( px0 / dx ) * dx;
  1479         py0 -= std::ceil( py0 / dy ) * dy;
  1481         for ( px = px0; px <= box->
width; px += dx )
  1483           for ( py = py0; py <= box->
length; py += dy )
  1486             rx = std::cos( box->
alpha ) * px + std::cos( box->
alpha - M_PI_2 ) * py;
  1487             ry = std::sin( box->
alpha ) * px + std::sin( box->
alpha - M_PI_2 ) * py;
  1495             if ( candidateAcceptable )
  1498               positions.append( 
new LabelPosition( 
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001, 
this ) ); 
  1504       nbp = positions.size();
  1512     while ( nbp == 0 && numTry < maxTry );
  1514     nbp = positions.size();
  1516     for ( i = 0; i < nbp; i++ )
  1518       lPos << positions.takeFirst();
  1521     for ( bbid = 0; bbid < j; bbid++ )
  1537                                    const GEOSPreparedGeometry *mapBoundary,
  1538                                    PointSet *mapShape, RTree<LabelPosition *, double, 2, double> *candidates )
  1558       case GEOS_LINESTRING:
  1592   QMutableListIterator< LabelPosition *> i( lPos );
  1593   while ( i.hasNext() )
  1596     bool outside = 
false;
  1601       outside = !pos->
within( mapBoundary );
  1614   return lPos.count();
  1623   int geomType = GEOSGeomTypeId_r( ctxt, 
mGeos );
  1625   double sizeCost = 0;
  1626   if ( geomType == GEOS_LINESTRING )
  1631       if ( GEOSLength_r( ctxt, 
mGeos, &length ) != 1 )
  1634     catch ( GEOSException &e )
  1639     double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
  1640     if ( length >= bbox_length / 4 )
  1643     sizeCost = 1 - ( length / ( bbox_length / 4 ) ); 
  1645   else if ( geomType == GEOS_POLYGON )
  1650       if ( GEOSArea_r( ctxt, 
mGeos, &area ) != 1 )
  1653     catch ( GEOSException &e )
  1658     double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
  1659     if ( area >= bbox_area / 16 )
  1662     sizeCost = 1 - ( area / ( bbox_area / 16 ) ); 
  1668   for ( 
int i = 0; i < nbp; i++ )
  1670     lPos.at( i )->setCost( lPos.at( i )->cost() + sizeCost / 100 );
  1683   catch ( GEOSException &e )
  1694   if ( !other->
mGeos )
  1700     GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt, 
mGeos );
  1701     GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
  1702     GEOSGeometry *geoms[2] = { g1, g2 };
  1703     geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
  1706     if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
  1714     mGeos = gTmp.release();
  1723   catch ( GEOSException &e )
  1745   bool uprightLabel = 
false;
  1750       uprightLabel = 
true;
  1756         uprightLabel = 
true;
  1762       uprightLabel = 
true;
  1764   return uprightLabel;
  1768                                     double &characterStartX, 
double &characterStartY, 
double &characterEndX, 
double &characterEndY )
 const  1777   double segmentStartX = path_positions->
x[index - 1];
  1778   double segmentStartY = path_positions->
y[index - 1];
  1780   double segmentEndX = path_positions->
x[index];
  1781   double segmentEndY = path_positions->
y[index];
  1783   double segmentDx = segmentEndX - segmentStartX;
  1784   double segmentDy = segmentEndY - segmentStartY;
  1786   characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
  1787   characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
  1793   if ( segmentLength - currentDistanceAlongSegment >= charWidth )
  1796     currentDistanceAlongSegment += charWidth;
  1797     characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
  1798     characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
  1806       segmentStartX = segmentEndX;
  1807       segmentStartY = segmentEndY;
  1809       if ( index >= path_positions->
nbPoints ) 
  1813       segmentEndX = path_positions->
x[index];
  1814       segmentEndY = path_positions->
y[index];
  1816     while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); 
  1822     currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
 QgsPointXY positionOffset() const 
Applies only to "offset from point" placement strategy. 
 
Label below point, slightly right of center. 
 
pal::Layer * layer() const 
Gets PAL layer of the label feature. Should be only used internally in PAL. 
 
UpsideDownLabels upsidedownLabels() const 
Returns how upside down labels are handled within the layer. 
 
Label on bottom-left of point. 
 
int upsideDownCharCount() const 
Returns the number of upside down characters for this label position. 
 
static bool candidateSortGrow(const LabelPosition *c1, const LabelPosition *c2)
Sorts label candidates in ascending order of cost. 
 
bool hasFixedPosition() const 
Returns true if the feature's label has a fixed position. 
 
double max_char_angle_outside
 
bool alwaysShow() const 
Whether label should be always shown (sets very high label priority) 
 
double getLabelHeight() const 
 
double fixedAngle() const 
Returns the fixed angle for the feature's label. 
 
Label on top-left of point. 
 
void setCost(double newCost)
Sets the candidate label position's geographical cost. 
 
int incrementUpsideDownCharCount()
Increases the count of upside down characters for this label position. 
 
A set of features which influence the labeling process. 
 
PredefinedPointPosition
Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode...
 
int createCandidatesAlongLine(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for line feature. 
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference) 
 
const GEOSPreparedGeometry * permissibleZonePrepared() const 
Returns a GEOS prepared geometry representing the label's permissibleZone(). 
 
void offsetPosition(double xOffset, double yOffset)
Shift the label by specified offset. 
 
int createCandidatesAroundPoint(double x, double y, QList< LabelPosition * > &lPos, double angle)
Generate candidates for point feature, located around a specified point. 
 
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
 
double cost() const 
Returns the candidate label position's geographical cost. 
 
friend class LabelPosition
 
QgsRectangle boundingBox() const 
Returns the bounding box of the geometry. 
 
static void findLineCircleIntersection(double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes)
 
Label on top of point, slightly right of center. 
 
QPointF quadOffset() const 
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
 
void createGeosGeom() const 
 
double getLabelWidth() const 
 
double priority() const 
Returns the layer's priority, between 0 and 1. 
 
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged)...
 
bool centroidInside() const 
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
 
double priority() const 
Returns the feature's labeling priority. 
 
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part. 
 
void addSizePenalty(int nbp, QList< LabelPosition * > &lPos, double bbx[4], double bby[4])
 
void getCentroid(double &px, double &py, bool forceInside=false) const 
 
double left() const 
Returns the left margin. 
 
int createCandidates(QList< LabelPosition * > &lPos, const GEOSPreparedGeometry *mapBoundary, PointSet *mapShape, RTree< LabelPosition *, double, 2, double > *candidates)
Generic method to generate label candidates for the feature. 
 
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored) 
 
static GEOSContextHandle_t getGEOSHandler()
 
int createCurvedCandidatesAlongLine(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate curved candidates for line features. 
 
QgsPalLayerSettings::OffsetType offsetType() const 
Returns the offset type, which determines how offsets and distance to label behaves. 
 
Arranges candidates following the curvature of a line feature. Applies to line layers only...
 
CharacterInfo * char_info
 
pal::LabelInfo * curvedLabelInfo() const 
Gets additional infor required for curved label placement. Returns null if not set. 
 
QgsPalLayerSettings::Placement arrangement() const 
Returns the layer's arrangement policy. 
 
bool hasFixedPosition() const 
Whether the label should use a fixed position instead of being automatically placed. 
 
bool isClosed() const 
Returns true if pointset is closed. 
 
const GEOSPreparedGeometry * preparedGeom() const 
 
LabelPosition * curvedPlacementAtOffset(PointSet *path_positions, double *path_distances, int &orientation, double distance, bool &reversed, bool &flip)
Returns the label position for a curved label at a specific offset along a path. 
 
QgsGeometry permissibleZone() const 
Returns the label's permissible zone geometry. 
 
static bool containsCandidate(const GEOSPreparedGeometry *geom, double x, double y, double width, double height, double alpha)
Returns true if a GEOS prepared geometry totally contains a label candidate. 
 
bool getShowPartial()
Returns whether partial labels should be allowed. 
 
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py)
Gets a point a set distance along a line geometry. 
 
double calculatePriority() const 
Calculates the priority for the feature. 
 
bool hasSameLabelFeatureAs(FeaturePart *part) const 
Tests whether this feature part belongs to the same QgsLabelFeature as another feature part...
 
double getLabelDistance() const 
 
const QSizeF & symbolSize() const 
Returns the size of the rendered symbol associated with this feature, if applicable. 
 
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer. 
 
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary). 
 
bool nextCharPosition(double charWidth, double segmentLength, PointSet *path_positions, int &index, double ¤tDistanceAlongSegment, double &characterStartX, double &characterStartY, double &characterEndX, double &characterEndY) const 
Returns true if the next char position is found. The referenced parameters are updated. 
 
int createCandidatesAlongLineNearStraightSegments(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for line feature, by trying to place candidates towards the middle of the longest...
 
double getY(int i=0) const 
Returns the down-left y coordinate. 
 
bool hasFixedQuadrant() const 
Returns whether the quadrant for the label is fixed. 
 
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
 
Optional additional info about label (for curved labels) 
 
Layer * layer()
Returns the layer that feature belongs to. 
 
int createCandidatesOverPoint(double x, double y, QList< LabelPosition * > &lPos, double angle)
Generate one candidate over or offset the specified point. 
 
bool hasFixedRotation() const 
Returns true if the feature's label has a fixed rotation. 
 
double length() const 
Returns length of line geometry. 
 
void insertIntoIndex(RTree< LabelPosition *, double, 2, double > *index)
 
static double dist_euc2d(double x1, double y1, double x2, double y2)
 
bool showUprightLabels() const 
Returns true if feature's label must be displayed upright. 
 
LabelPosition * getNextPart() const 
 
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi. 
 
double getX(int i=0) const 
Returns the down-left x coordinate. 
 
Main class to handle feature. 
 
Offset distance applies from rendered symbol bounds. 
 
const QgsMargins & visualMargin() const 
Returns the visual margin for the label feature. 
 
bool containsPoint(double x, double y) const 
Tests whether point set contains a specified point. 
 
static void splitPolygons(QLinkedList< PointSet * > &shapes_toProcess, QLinkedList< PointSet * > &shapes_final, double xrm, double yrm)
Split a concave shape into several convex shapes. 
 
double getAlpha() const 
Returns the angle to rotate text (in rad). 
 
QgsPointXY fixedPosition() const 
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
 
int createCandidatesForPolygon(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for polygon features. 
 
void setNextPart(LabelPosition *next)
 
CHullBox * compute_chull_bbox()
 
int createCandidatesAlongLineNearMidpoint(QList< LabelPosition * > &lPos, PointSet *mapShape, double initialCost=0.0)
Generate candidates for line feature, by trying to place candidates as close as possible to the line'...
 
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
 
double right() const 
Returns the right margin. 
 
double bottom() const 
Returns the bottom margin. 
 
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
 
double top() const 
Returns the top margin. 
 
~FeaturePart() override
Delete the feature. 
 
int connectedFeatureId(QgsFeatureId featureId) const 
Returns the connected feature ID for a label feature ID, which is unique for all features which have ...
 
QList< FeaturePart * > mHoles
 
QVector< QgsPalLayerSettings::PredefinedPointPosition > predefinedPositionOrder() const 
Returns the priority ordered list of predefined positions for label candidates. 
 
double max_char_angle_inside
 
int createCandidatesAtOrderedPositionsOverPoint(double x, double y, QList< LabelPosition * > &lPos, double angle)
Generates candidates following a prioritized list of predefined positions around a point...
 
Label below point, slightly left of center. 
 
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom 
 
double distLabel() const 
Applies to "around point" placement strategy or linestring features. 
 
LabelPosition is a candidate feature label position. 
 
QString name() const 
Returns the layer's name. 
 
Label on top of point, slightly left of center. 
 
Quadrant
Position of label candidate relative to feature. 
 
static int reorderPolygon(int nbPoints, double *x, double *y)
Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside...
 
bool hasFixedAngle() const 
Whether the label should use a fixed angle instead of using angle from automatic placement. 
 
FeaturePart(QgsLabelFeature *lf, const GEOSGeometry *geom)
Creates a new generic feature. 
 
bool within(const GEOSPreparedGeometry *geometry)
Returns true if the label position is within a geometry. 
 
bool intersects(const GEOSPreparedGeometry *geometry)
Returns true if the label position intersects a geometry. 
 
double width() const 
Returns the width of the rectangle. 
 
bool isCurved() const 
Returns true if the layer has curved labels. 
 
double fixedAngle() const 
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true) ...
 
The QgsMargins class defines the four margins of a rectangle. 
 
double height() const 
Returns the height of the rectangle. 
 
QgsFeatureId id() const 
Identifier of the label (unique within the parent label provider) 
 
pal::LineArrangementFlags arrangementFlags() const 
Returns the feature's arrangement flags. 
 
QgsFeatureId featureId() const 
Returns the unique ID of the feature. 
 
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...