44 #include <QLinkedList> 54 mGeos =
const_cast<GEOSGeometry *
>( geom );
60 for (
int i = 0; i <
mHoles.count(); i++ )
62 mHoles.at( i )->holeOf =
this;
74 mHoles.last()->holeOf =
this;
88 const GEOSCoordSequence *coordSeq =
nullptr;
91 type = GEOSGeomTypeId_r( geosctxt, geom );
93 if (
type == GEOS_POLYGON )
95 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
97 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
99 for (
int i = 0; i < numHoles; ++i )
101 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
113 geom = GEOSGetExteriorRing_r( geosctxt, geom );
122 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
123 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
126 xmin =
ymin = std::numeric_limits<double>::max();
127 xmax =
ymax = std::numeric_limits<double>::lowest();
134 for (
int i = 0; i <
nbPoints; ++i )
136 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8 137 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
139 GEOSCoordSeq_getX_r( geosctxt, coordSeq, i, &
x[i] );
140 GEOSCoordSeq_getY_r( geosctxt, coordSeq, i, &
y[i] );
168 if ( mCachedMaxLineCandidates > 0 )
169 return mCachedMaxLineCandidates;
171 const double l =
length();
176 if ( maxForLayer == 0 )
177 mCachedMaxLineCandidates = candidatesForLineLength;
179 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
183 mCachedMaxLineCandidates = 1;
185 return mCachedMaxLineCandidates;
190 if ( mCachedMaxPolygonCandidates > 0 )
191 return mCachedMaxPolygonCandidates;
193 const double a =
area();
198 if ( maxForLayer == 0 )
199 mCachedMaxPolygonCandidates = candidatesForArea;
201 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
205 mCachedMaxPolygonCandidates = 1;
207 return mCachedMaxPolygonCandidates;
229 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
231 if ( quadOffsetX < 0 )
233 if ( quadOffsetY < 0 )
237 else if ( quadOffsetY > 0 )
246 else if ( quadOffsetX > 0 )
248 if ( quadOffsetY < 0 )
252 else if ( quadOffsetY > 0 )
263 if ( quadOffsetY < 0 )
267 else if ( quadOffsetY > 0 )
280 return mTotalRepeats;
294 double cost = 0.00005;
295 int id = lPos.size();
297 double xdiff = -labelW / 2.0;
298 double ydiff = -labelH / 2.0;
302 double lx = x + xdiff;
303 double ly = y + ydiff;
313 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false,
LabelPosition::QuadrantOver ) );
323 double cost = 0.0001;
324 int id = lPos.size();
326 double xdiff = -labelW / 2.0;
327 double ydiff = -labelH / 2.0;
344 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
345 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
381 double lx = x + xdiff;
382 double ly = y + ydiff;
392 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false, quadrantFromOffset() ) );
405 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
406 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8 407 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
409 GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
410 GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
414 catch ( GEOSException &e )
434 double cost = 0.0001;
449 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
450 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
456 deltaX = -labelWidth / 4.0 - visualMargin.
left();
457 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
463 deltaX = -labelWidth / 2.0;
464 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
470 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
471 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
477 deltaX = - visualMargin.
left() + symbolWidthOffset;
478 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
484 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
485 deltaY = -labelHeight / 2.0;
491 deltaX = -visualMargin.
left() + symbolWidthOffset;
492 deltaY = -labelHeight / 2.0;
498 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
499 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
505 deltaX = -labelWidth / 4.0 - visualMargin.
left();
506 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
512 deltaX = -labelWidth / 2.0;
513 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
519 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
520 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
526 deltaX = -visualMargin.
left() + symbolWidthOffset;
527 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
532 double referenceX = std::cos( alpha ) * distanceToLabel +
x;
533 double referenceY = std::sin( alpha ) * distanceToLabel +
y;
535 double labelX = referenceX + deltaX;
536 double labelY = referenceY + deltaY;
540 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
543 if ( maxNumberCandidates > 0 && lPos.size() >= maxNumberCandidates )
559 if ( maxNumberCandidates == 0 )
560 maxNumberCandidates = 16;
564 int id = lPos.size();
566 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
571 double a270 = a180 + a90;
572 double a360 = 2 * M_PI;
574 double gamma1, gamma2;
576 if ( distanceToLabel > 0 )
578 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
579 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
583 gamma1 = gamma2 = a90 / 3.0;
586 if ( gamma1 > a90 / 3.0 )
589 if ( gamma2 > a90 / 3.0 )
592 std::size_t numberCandidatesGenerated = 0;
595 double angleToCandidate;
596 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
601 if ( angleToCandidate > a360 )
602 angleToCandidate -= a360;
606 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
608 labelX += distanceToLabel;
609 double iota = ( angleToCandidate + gamma1 );
610 if ( iota > a360 - gamma1 )
614 labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
618 else if ( angleToCandidate < a90 - gamma2 )
620 labelX += distanceToLabel * std::cos( angleToCandidate );
621 labelY += distanceToLabel * std::sin( angleToCandidate );
624 else if ( angleToCandidate < a90 + gamma2 )
627 labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
628 labelY += distanceToLabel;
631 else if ( angleToCandidate < a180 - gamma1 )
633 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
634 labelY += distanceToLabel * std::sin( angleToCandidate );
637 else if ( angleToCandidate < a180 + gamma1 )
639 labelX += -distanceToLabel - labelWidth;
641 labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
644 else if ( angleToCandidate < a270 - gamma2 )
646 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
647 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
650 else if ( angleToCandidate < a270 + gamma2 )
652 labelY += -distanceToLabel - labelHeight;
654 labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
657 else if ( angleToCandidate < a360 )
659 labelX += distanceToLabel * std::cos( angleToCandidate );
660 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
666 if ( maxNumberCandidates == 1 )
669 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
680 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
681 numberCandidatesGenerated++;
685 if ( icost == static_cast< int >( maxNumberCandidates ) )
687 icost =
static_cast< int >( maxNumberCandidates ) - 1;
690 else if ( icost > static_cast< int >( maxNumberCandidates ) )
692 icost =
static_cast< int >( maxNumberCandidates ) - 2;
698 return numberCandidatesGenerated;
705 double shapeLength = mapShape->
length();
719 if ( candidates < candidateTargetCount )
734 std::vector< double > &
x = line->
x;
735 std::vector< double > &
y = line->
y;
737 std::vector< double > segmentLengths( nbPoints - 1 );
738 std::vector< double >distanceToSegment( nbPoints );
740 double totalLineLength = 0.0;
741 for (
int i = 0; i < line->
nbPoints - 1; i++ )
744 distanceToSegment[i] = 0;
746 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
749 totalLineLength += segmentLengths[i];
751 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
754 const double lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
755 double currentDistanceAlongLine = lineStepDistance;
757 double candidateCenterX, candidateCenterY;
759 while ( currentDistanceAlongLine < totalLineLength )
766 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
769 double cost = std::fabs( totalLineLength / 2 - currentDistanceAlongLine ) / totalLineLength;
772 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateCenterX - labelWidth / 2, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
774 currentDistanceAlongLine += lineStepDistance;
778 if ( lineStepDistance < 0 )
795 QVector< int > extremeAngleNodes;
798 std::vector< double > &
x = line->
x;
799 std::vector< double > &
y = line->
y;
803 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
805 double x1 = x[i - 1];
807 double x3 = x[ i == numberNodes - 1 ? 1 : i + 1];
808 double y1 = y[i - 1];
810 double y3 = y[ i == numberNodes - 1 ? 1 : i + 1];
815 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
819 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
820 extremeAngleNodes << i;
822 extremeAngleNodes << numberNodes - 1;
824 if ( extremeAngleNodes.isEmpty() )
831 std::vector< double > segmentLengths( numberNodes - 1 );
832 std::vector< double > distanceToSegment( numberNodes );
833 double totalLineLength = 0.0;
834 QVector< double > straightSegmentLengths;
835 QVector< double > straightSegmentAngles;
836 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
837 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
838 double currentStraightSegmentLength = 0;
839 double longestSegmentLength = 0;
840 int segmentIndex = 0;
841 double segmentStartX = x[0];
842 double segmentStartY = y[0];
843 for (
int i = 0; i < numberNodes - 1; i++ )
846 distanceToSegment[i] = 0;
848 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
851 totalLineLength += segmentLengths[i];
852 if ( extremeAngleNodes.contains( i ) )
855 straightSegmentLengths << currentStraightSegmentLength;
857 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
859 currentStraightSegmentLength = 0;
860 segmentStartX = x[i];
861 segmentStartY = y[i];
863 currentStraightSegmentLength += segmentLengths[i];
865 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
866 straightSegmentLengths << currentStraightSegmentLength;
868 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
869 double middleOfLine = totalLineLength / 2.0;
871 if ( totalLineLength < labelWidth )
877 double lineStepDistance = ( totalLineLength - labelWidth );
878 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
880 double distanceToEndOfSegment = 0.0;
881 int lastNodeInSegment = 0;
883 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
885 currentStraightSegmentLength = straightSegmentLengths.at( i );
886 double currentSegmentAngle = straightSegmentAngles.at( i );
887 lastNodeInSegment = extremeAngleNodes.at( i );
888 double distanceToStartOfSegment = distanceToEndOfSegment;
889 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
890 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
892 if ( currentStraightSegmentLength < labelWidth )
896 double currentDistanceAlongLine = distanceToStartOfSegment;
897 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
898 double candidateLength = 0.0;
904 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
905 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
907 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
915 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
916 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
918 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
924 cost = candidateLength / labelWidth;
930 cost = ( 1 - cost ) / 100;
934 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
935 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
936 cost += costCenter * 0.0005;
943 double costLineCenter = 2 * std::fabs( labelCenter - middleOfLine ) / totalLineLength;
944 cost += costLineCenter * 0.0005;
947 cost += segmentCost * 0.0005;
948 cost += segmentAngleCost * 0.0001;
955 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
959 beta = angle + M_PI_2;
964 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
974 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
975 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
982 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
983 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
990 const double candidateCost = cost + 0.002;
991 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
997 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
1004 currentDistanceAlongLine += lineStepDistance;
1027 std::vector< double > &
x = line->
x;
1028 std::vector< double > &
y = line->
y;
1030 std::vector< double > segmentLengths( nbPoints - 1 );
1031 std::vector< double >distanceToSegment( nbPoints );
1033 double totalLineLength = 0.0;
1034 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1037 distanceToSegment[i] = 0;
1039 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1042 totalLineLength += segmentLengths[i];
1044 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1046 double lineStepDistance = ( totalLineLength - labelWidth );
1047 double currentDistanceAlongLine = 0;
1051 if ( totalLineLength > labelWidth )
1053 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1057 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1058 lineStepDistance = -1;
1059 totalLineLength = labelWidth;
1064 currentDistanceAlongLine = std::numeric_limits< double >::max();
1067 double candidateLength;
1069 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1071 while ( currentDistanceAlongLine < totalLineLength - labelWidth )
1079 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1080 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1082 if ( currentDistanceAlongLine < 0 )
1085 candidateLength = std::sqrt( ( x[nbPoints - 1] - x[0] ) * ( x[nbPoints - 1] - x[0] )
1086 + ( y[nbPoints - 1] - y[0] ) * ( y[nbPoints - 1] - y[0] ) );
1090 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1093 cost = candidateLength / labelWidth;
1099 cost = ( 1 - cost ) / 100;
1103 double costCenter = std::fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
1104 cost += costCenter / 1000;
1105 cost += initialCost;
1112 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1116 beta = angle + M_PI_2;
1121 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1131 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1132 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
1139 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1140 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
1147 const double candidateCost = cost + 0.002;
1148 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
1154 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
1161 currentDistanceAlongLine += lineStepDistance;
1165 if ( lineStepDistance < 0 )
1175 double offsetAlongSegment = offsetAlongLine;
1178 while ( index < path_positions->
nbPoints && offsetAlongSegment > path_distances[index] )
1180 offsetAlongSegment -= path_distances[index];
1183 if ( index >= path_positions->
nbPoints )
1192 const double segment_length = path_distances[index];
1199 if ( orientation == 0 )
1203 double _distance = offsetAlongSegment;
1204 int endindex = index;
1206 double startLabelX = 0;
1207 double startLabelY = 0;
1208 double endLabelX = 0;
1209 double endLabelY = 0;
1210 for (
int i = 0; i < li->
char_num; i++ )
1213 double characterStartX, characterStartY;
1214 if ( !
nextCharPosition( ci.
width, path_distances[endindex], path_positions, endindex, _distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
1220 startLabelX = characterStartX;
1221 startLabelY = characterStartY;
1226 double dx = endLabelX - startLabelX;
1227 double dy = endLabelY - startLabelY;
1228 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
1230 bool isRightToLeft = ( lineAngle > 90 || lineAngle < -90 );
1231 reversed = isRightToLeft;
1232 orientation = isRightToLeft ? -1 : 1;
1237 if ( orientation < 0 )
1240 reversed = !reversed;
1245 std::unique_ptr< LabelPosition > slp;
1248 double old_x = path_positions->
x[index - 1];
1249 double old_y = path_positions->
y[index - 1];
1251 double new_x = path_positions->
x[index];
1252 double new_y = path_positions->
y[index];
1254 double dx = new_x - old_x;
1255 double dy = new_y - old_y;
1257 double angle = std::atan2( -dy, dx );
1259 for (
int i = 0; i < li->
char_num; i++ )
1261 double last_character_angle =
angle;
1269 double start_x, start_y, end_x, end_y;
1270 if ( !
nextCharPosition( ci.
width, path_distances[index], path_positions, index, offsetAlongSegment, start_x, start_y, end_x, end_y ) )
1276 angle = std::atan2( start_y - end_y, end_x - start_x );
1281 double angle_delta = last_character_angle -
angle;
1283 while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
1284 while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
1288 && angle_delta < li->max_char_angle_outside * ( M_PI / 180 ) ) )
1296 if ( orientation < 0 )
1301 start_x += dist * std::cos( angle + M_PI_2 );
1302 start_y -= dist * std::sin( angle + M_PI_2 );
1304 double render_angle =
angle;
1306 double render_x = start_x;
1307 double render_y = start_y;
1313 if ( orientation < 0 )
1316 render_x += ci.
width * std::cos( render_angle );
1317 render_y -= ci.
width * std::sin( render_angle );
1318 render_angle += M_PI;
1321 std::unique_ptr< LabelPosition > tmp = qgis::make_unique< LabelPosition >( 0, render_x , render_y , ci.
width, string_height, -render_angle, 0.0001, this );
1322 tmp->setPartId( orientation > 0 ? i : li->
char_num - i - 1 );
1325 slp = std::move( tmp );
1331 while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
1332 while ( render_angle < 0 ) render_angle += 2 * M_PI;
1334 if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI )
1341 static std::unique_ptr< LabelPosition > _createCurvedCandidate(
LabelPosition *lp,
double angle,
double dist )
1343 std::unique_ptr< LabelPosition > newLp = qgis::make_unique< LabelPosition >( *lp );
1344 newLp->offsetPosition( dist * std::cos(
angle + M_PI_2 ), dist * std::sin(
angle + M_PI_2 ) );
1359 double totalCharacterWidth = 0;
1360 for (
int i = 0; i < li->
char_num; ++i )
1363 std::unique_ptr< PointSet > expanded;
1364 double shapeLength = mapShape->
length();
1367 allowOverrun =
false;
1373 if ( totalCharacterWidth > shapeLength )
1375 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1382 if ( allowOverrun && overrun > 0 )
1385 expanded = mapShape->
clone();
1387 mapShape = expanded.get();
1388 shapeLength = mapShape->
length();
1392 std::unique_ptr< double [] > path_distances = qgis::make_unique<double[]>( mapShape->
nbPoints );
1393 double total_distance = 0;
1394 double old_x = -1.0, old_y = -1.0;
1395 for (
int i = 0; i < mapShape->
nbPoints; i++ )
1398 path_distances[i] = 0;
1400 path_distances[i] = std::sqrt( std::pow( old_x - mapShape->
x[i], 2 ) + std::pow( old_y - mapShape->
y[i], 2 ) );
1401 old_x = mapShape->
x[i];
1402 old_y = mapShape->
y[i];
1404 total_distance += path_distances[i];
1415 std::vector< std::unique_ptr< LabelPosition >> positions;
1417 double delta = std::max( li->
label_height / 6, total_distance / candidateTargetCount );
1424 for (
double distanceAlongLineToStartCandidate = 0; distanceAlongLineToStartCandidate < total_distance; distanceAlongLineToStartCandidate += delta )
1428 bool reversed =
false;
1434 int orientation = 0;
1442 std::unique_ptr< LabelPosition > slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
1447 if ( slp->upsideDownCharCount() >= li->
char_num / 2.0 )
1452 orientation = -orientation;
1453 slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
1460 double angle_diff = 0.0, angle_last = 0.0, diff;
1462 double sin_avg = 0, cos_avg = 0;
1465 if ( tmp != slp.get() )
1467 diff = std::fabs( tmp->
getAlpha() - angle_last );
1468 if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
1469 diff = std::min( diff, 2 * M_PI - diff );
1473 sin_avg += std::sin( tmp->
getAlpha() );
1474 cos_avg += std::cos( tmp->
getAlpha() );
1479 double angle_diff_avg = li->
char_num > 1 ? ( angle_diff / ( li->
char_num - 1 ) ) : 0;
1480 double cost = angle_diff_avg / 100;
1481 if ( cost < 0.0001 ) cost = 0.0001;
1484 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1485 double costCenter = std::fabs( total_distance / 2 - labelCenter ) / total_distance;
1486 cost += costCenter / 100;
1487 slp->setCost( cost );
1490 double angle_avg = std::atan2( sin_avg / li->
char_num, cos_avg / li->
char_num );
1491 bool localreversed = flip ? !reversed : reversed;
1493 for (
int i = 0; i <= 2; ++i )
1495 std::unique_ptr< LabelPosition > p;
1500 p = _createCurvedCandidate( slp.get(), angle_avg, 0 );
1501 p->setCost( p->cost() + 0.002 );
1503 if ( i == 2 && ( ( !localreversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( localreversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) ) )
1506 p->setCost( p->cost() + 0.001 );
1513 while ( within && currentPos )
1516 currentPos = currentPos->
nextPart();
1525 positions.emplace_back( std::move( p ) );
1529 for ( std::unique_ptr< LabelPosition > &pos : positions )
1531 lPos.emplace_back( std::move( pos ) );
1534 return positions.size();
1558 QLinkedList<PointSet *> shapes_toProcess;
1559 QLinkedList<PointSet *> shapes_final;
1560 const double totalArea =
area();
1562 mapShape->
parent =
nullptr;
1567 shapes_toProcess.append( mapShape );
1569 splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight );
1571 std::size_t nbp = 0;
1573 if ( !shapes_final.isEmpty() )
1581 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1583 std::vector< CHullBox > boxes;
1584 boxes.reserve( shapes_final.size() );
1587 while ( !shapes_final.isEmpty() )
1589 PointSet *shape = shapes_final.takeFirst();
1600 double densityY = densityX;
1607 std::size_t numberCandidatesGenerated = 0;
1622 double dx = densityX;
1623 double dy = densityY;
1624 if ( numTry == 0 && maxPolygonCandidates > 0 )
1627 const double boxArea = box.width * box.length;
1628 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1629 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1634 return numberCandidatesGenerated;
1653 bool enoughPlace =
false;
1657 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1658 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1664 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1666 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1670 enoughPlace =
false;
1686 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1688 if ( box.alpha <= M_PI_4 )
1694 alpha = box.alpha - M_PI_2;
1697 else if ( box.length > box.width )
1699 alpha = box.alpha - M_PI_2;
1706 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1712 dlx = std::cos( beta ) * diago;
1713 dly = std::sin( beta ) * diago;
1715 double px0 = box.width / 2.0;
1716 double py0 = box.length / 2.0;
1718 px0 -= std::ceil( px0 / dx ) * dx;
1719 py0 -= std::ceil( py0 / dy ) * dy;
1721 for ( px = px0; px <= box.width; px += dx )
1726 for ( py = py0; py <= box.length; py += dy )
1729 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1730 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1740 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this ) );
1741 numberCandidatesGenerated++;
1752 std::unique_ptr< LabelPosition > potentialCandidate = qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001, this );
1754 lPos.emplace_back( std::move( potentialCandidate ) );
1755 numberCandidatesGenerated++;
1762 nbp = numberCandidatesGenerated;
1763 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1774 while ( numTry < maxTry );
1776 nbp = numberCandidatesGenerated;
1788 std::vector< std::unique_ptr< LabelPosition > > lPos;
1808 case GEOS_LINESTRING:
1858 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
1860 double sizeCost = 0;
1861 if ( geomType == GEOS_LINESTRING )
1863 const double l =
length();
1866 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
1867 if ( l >= bbox_length / 4 )
1870 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
1872 else if ( geomType == GEOS_POLYGON )
1874 const double a =
area();
1877 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
1878 if ( a >= bbox_area / 16 )
1881 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
1887 for ( std::unique_ptr< LabelPosition > &pos : lPos )
1889 pos->setCost( pos->cost() + sizeCost / 100 );
1902 catch ( GEOSException &e )
1913 if ( !other->
mGeos )
1919 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
1920 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
1921 GEOSGeometry *geoms[2] = { g1, g2 };
1922 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
1925 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
1933 mGeos = gTmp.release();
1942 catch ( GEOSException &e )
1964 bool uprightLabel =
false;
1969 uprightLabel =
true;
1975 uprightLabel =
true;
1981 uprightLabel =
true;
1983 return uprightLabel;
1987 double &characterStartX,
double &characterStartY,
double &characterEndX,
double &characterEndY )
const 1996 double segmentStartX = path_positions->
x[index - 1];
1997 double segmentStartY = path_positions->
y[index - 1];
1999 double segmentEndX = path_positions->
x[index];
2000 double segmentEndY = path_positions->
y[index];
2002 double segmentDx = segmentEndX - segmentStartX;
2003 double segmentDy = segmentEndY - segmentStartY;
2005 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2006 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2012 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
2015 currentDistanceAlongSegment += charWidth;
2016 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2017 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2025 segmentStartX = segmentEndX;
2026 segmentStartY = segmentEndY;
2028 if ( index >= path_positions->
nbPoints )
2032 segmentEndX = path_positions->
x[index];
2033 segmentEndY = path_positions->
y[index];
2035 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth );
2041 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
Label on bottom right of point.
QgsLabeling::LinePlacementFlags arrangementFlags() const
Returns the feature's arrangement flags.
double right() const
Returns the right margin.
bool centroidInside() const
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
Label on bottom-left of point.
Label on top of point, slightly left of center.
CHullBox compute_chull_bbox()
Computes a con???? hull.
double distLabel() const
Applies to "around point" placement strategy or linestring features.
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
double fixedAngle() const
Returns the fixed angle for the feature's label.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
std::size_t createCurvedCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate curved candidates for line features.
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)
std::size_t maximumPointLabelCandidates() const
Returns the maximum number of point label candidates to generate for features in this layer...
double max_char_angle_outside
QgsLabelFeature * feature()
Returns the parent feature.
double priority() const
Returns the feature's labeling priority.
void setTotalRepeats(int repeats)
Returns the total number of repeating labels associated with this label.
bool alwaysShow() const
Whether label should be always shown (sets very high label priority)
Label on top-left of point.
int incrementUpsideDownCharCount()
Increases the count of upside down characters for this label position.
void addSizePenalty(std::vector< std::unique_ptr< LabelPosition > > &lPos, double bbx[4], double bby[4])
Increases the cost of the label candidates for this feature, based on the size of the feature...
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
bool hasFixedRotation() const
Returns true if the feature's label has a fixed rotation.
double getY(int i=0) const
Returns the down-left y coordinate.
A set of features which influence the labeling process.
PredefinedPointPosition
Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode...
A class to represent a 2D point.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
void createGeosGeom() const
std::size_t maximumPolygonLabelCandidates() const
Returns the maximum number of polygon label candidates to generate for features in this layer...
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
const GEOSPreparedGeometry * permissibleZonePrepared() const
Returns a GEOS prepared geometry representing the label's permissibleZone().
static void findLineCircleIntersection(double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes)
std::size_t maximumLineLabelCandidates() const
Returns the maximum number of line label candidates to generate for features in this layer...
Label on top-right of point.
const QSizeF & symbolSize() const
Returns the size of the rendered symbol associated with this feature, if applicable.
bool isClosed() const
Returns true if pointset is closed.
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged)...
UpsideDownLabels upsidedownLabels() const
Returns how upside down labels are handled within the layer.
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
const QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
double getLabelDistance() const
Returns the distance from the anchor point to the label.
std::size_t createCandidatesForPolygon(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate candidates for polygon features.
Labels can be placed directly over a line 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)
std::unique_ptr< 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.
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
static GEOSContextHandle_t getGEOSHandler()
QgsPointXY positionOffset() const
Applies only to "offset from point" placement strategy.
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
CharacterInfo * char_info
double priority() const
Returns the layer's priority, between 0 and 1.
pal::LabelInfo * curvedLabelInfo() const
Gets additional info required for curved label placement. Returns nullptr if not set.
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
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.
QgsGeometry permissibleZone() const
Returns the label's permissible zone geometry.
std::unique_ptr< LabelPosition > createCandidatePointOnSurface(PointSet *mapShape)
Creates a single candidate using the "point on sruface" algorithm.
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py)
Gets a point a set distance along a line geometry.
static void splitPolygons(QLinkedList< PointSet *> &inputShapes, QLinkedList< PointSet *> &outputShapes, double xrm, double yrm)
Split a concave shape into several convex shapes.
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.
std::size_t createCandidatesAlongLineNearMidpoint(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, double initialCost=0.0, Pal *pal=nullptr)
Generate candidates for line feature, by trying to place candidates as close as possible to the line'...
double bottom() const
Returns the bottom margin.
double width() const
Returns the width of the rectangle.
bool isCanceled()
Check whether the job has been canceled.
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).
std::size_t createHorizontalCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate horizontal candidates for line feature.
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
void setAnchorPosition(const QgsPointXY &anchorPosition)
In case of quadrand or aligned positioning, this is set to the anchor point.
Optional additional info about label (for curved labels)
Layer * layer()
Returns the layer that feature belongs to.
double calculatePriority() const
Calculates the priority for the feature.
QgsPalLayerSettings::Placement arrangement() const
Returns the layer's arrangement policy.
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
double top() const
Returns the top margin.
static double dist_euc2d(double x1, double y1, double x2, double y2)
pal::Layer * layer() const
Gets PAL layer of the label feature. Should be only used internally in PAL.
double fixedAngle() const
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true) ...
Label below point, slightly right of center.
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
Main class to handle feature.
Offset distance applies from rendered symbol bounds.
std::size_t createCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate candidates for line feature.
bool hasFixedPosition() const
Returns true if the feature's label has a fixed position.
double maximumLineCandidatesPerMapUnit() const
Returns the maximum number of line label candidate positions per map unit.
std::size_t maximumLineCandidates() const
Returns the maximum number of line candidates to generate for this feature.
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
double maximumPolygonCandidatesPerMapUnitSquared() const
Returns the maximum number of polygon label candidate positions per map unit squared.
double area() const
Returns area of polygon geometry.
void setNextPart(std::unique_ptr< LabelPosition > next)
Sets the next part of this label position (i.e.
static int reorderPolygon(int nbPoints, std::vector< double > &x, std::vector< double > &y)
Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside...
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
std::unique_ptr< PointSet > clone() const
Returns a copy of the point set.
bool hasSameLabelFeatureAs(FeaturePart *part) const
Tests whether this feature part belongs to the same QgsLabelFeature as another feature part...
double getAlpha() const
Returns the angle to rotate text (in rad).
double length() const
Returns length of line geometry.
QgsPalLayerSettings::OffsetType offsetType() const
Returns the offset type, which determines how offsets and distance to label behaves.
Label below point, slightly left of center.
~FeaturePart() override
Delete the feature.
QList< FeaturePart * > mHoles
double getX(int i=0) const
Returns the down-left x coordinate.
double max_char_angle_inside
Label on top of point, slightly right of center.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Label directly below point.
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom
QString name() const
Returns the layer's name.
LabelPosition is a candidate feature label position.
std::size_t createCandidatesAtOrderedPositionsOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generates candidates following a prioritized list of predefined positions around a point...
double overrunSmoothDistance() const
Returns the distance (in map units) with which the ends of linear features are averaged over when cal...
Label directly above point.
std::size_t createCandidatesOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate one candidate over or offset the specified point.
Quadrant
Position of label candidate relative to feature.
const GEOSPreparedGeometry * preparedGeom() const
std::vector< std::unique_ptr< LabelPosition > > createCandidates(Pal *pal)
Generates a list of candidate positions for labels for this feature.
FeaturePart(QgsLabelFeature *lf, const GEOSGeometry *geom)
Creates a new generic feature.
QgsPointXY fixedPosition() const
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
QVector< QgsPalLayerSettings::PredefinedPointPosition > predefinedPositionOrder() const
Returns the priority ordered list of predefined positions for label candidates.
std::size_t maximumPointCandidates() const
Returns the maximum number of point candidates to generate for this feature.
void getCentroid(double &px, double &py, bool forceInside=false) const
std::size_t maximumPolygonCandidates() const
Returns the maximum number of polygon candidates to generate for this feature.
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only...
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
std::size_t createCandidatesAroundPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate candidates for point feature, located around a specified point.
double left() const
Returns the left margin.
bool isCurved() const
Returns true if the layer has curved labels.
double getLabelWidth(double angle=0.0) const
Returns the width of the label, optionally taking an angle into account.
double overrunDistance() const
Returns the permissible distance (in map units) which labels are allowed to overrun the start or end ...
bool showUprightLabels() const
Returns true if feature's label must be displayed upright.
double getLabelHeight(double angle=0.0) const
Returns the height of the label, optionally taking an angle into account.
The QgsMargins class defines the four margins of a rectangle.
std::size_t createCandidatesAlongLineNearStraightSegments(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate candidates for line feature, by trying to place candidates towards the middle of the longest...
double height() const
Returns the height of the rectangle.
int connectedFeatureId(QgsFeatureId featureId) const
Returns the connected feature ID for a label feature ID, which is unique for all features which have ...
int totalRepeats() const
Returns the total number of repeating labels associated with this label.
std::size_t createCandidateCenteredOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate one candidate centered over the specified point.
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...