49 #include <QLinkedList>
59 mGeos =
const_cast<GEOSGeometry *
>( geom );
65 for (
int i = 0; i <
mHoles.count(); i++ )
67 mHoles.at( i )->holeOf =
this;
79 mHoles.last()->holeOf =
this;
93 const GEOSCoordSequence *coordSeq =
nullptr;
96 type = GEOSGeomTypeId_r( geosctxt, geom );
98 if (
type == GEOS_POLYGON )
100 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
102 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
104 for (
int i = 0; i < numHoles; ++i )
106 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
118 geom = GEOSGetExteriorRing_r( geosctxt, geom );
127 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
128 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
131 xmin =
ymin = std::numeric_limits<double>::max();
132 xmax =
ymax = std::numeric_limits<double>::lowest();
139 for (
int i = 0; i <
nbPoints; ++i )
141 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
142 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
144 GEOSCoordSeq_getX_r( geosctxt, coordSeq, i, &
x[i] );
145 GEOSCoordSeq_getY_r( geosctxt, coordSeq, i, &
y[i] );
173 if ( mCachedMaxLineCandidates > 0 )
174 return mCachedMaxLineCandidates;
176 const double l =
length();
181 if ( maxForLayer == 0 )
182 mCachedMaxLineCandidates = candidatesForLineLength;
184 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
188 mCachedMaxLineCandidates = 1;
190 return mCachedMaxLineCandidates;
195 if ( mCachedMaxPolygonCandidates > 0 )
196 return mCachedMaxPolygonCandidates;
198 const double a =
area();
203 if ( maxForLayer == 0 )
204 mCachedMaxPolygonCandidates = candidatesForArea;
206 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
210 mCachedMaxPolygonCandidates = 1;
212 return mCachedMaxPolygonCandidates;
234 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
236 if ( quadOffsetX < 0 )
238 if ( quadOffsetY < 0 )
242 else if ( quadOffsetY > 0 )
251 else if ( quadOffsetX > 0 )
253 if ( quadOffsetY < 0 )
257 else if ( quadOffsetY > 0 )
268 if ( quadOffsetY < 0 )
272 else if ( quadOffsetY > 0 )
285 return mTotalRepeats;
299 double cost = 0.00005;
300 int id = lPos.size();
302 double xdiff = -labelW / 2.0;
303 double ydiff = -labelH / 2.0;
307 double lx =
x + xdiff;
308 double ly =
y + ydiff;
328 double cost = 0.0001;
329 int id = lPos.size();
331 double xdiff = -labelW / 2.0;
332 double ydiff = -labelH / 2.0;
349 double xd = xdiff * std::cos(
angle ) - ydiff * std::sin(
angle );
350 double yd = xdiff * std::sin(
angle ) + ydiff * std::cos(
angle );
386 double lx =
x + xdiff;
387 double ly =
y + ydiff;
397 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH,
angle, cost,
this,
false, quadrantFromOffset() ) );
410 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
411 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
412 unsigned int nPoints = 0;
413 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
416 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
418 GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
419 GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
423 catch ( GEOSException &e )
425 qWarning(
"GEOS exception: %s", e.what() );
433 void createCandidateAtOrderedPositionOverPoint(
double &labelX,
double &labelY,
LabelPosition::Quadrant &quadrant,
double x,
double y,
double labelWidth,
double labelHeight,
QgsPalLayerSettings::PredefinedPointPosition position,
double distanceToLabel,
const QgsMargins &visualMargin,
double symbolWidthOffset,
double symbolHeightOffset,
double angle )
444 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
445 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
451 deltaX = -labelWidth / 4.0 - visualMargin.
left();
452 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
458 deltaX = -labelWidth / 2.0;
459 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
465 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
466 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
472 deltaX = - visualMargin.
left() + symbolWidthOffset;
473 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
479 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
480 deltaY = -labelHeight / 2.0;
486 deltaX = -visualMargin.
left() + symbolWidthOffset;
487 deltaY = -labelHeight / 2.0;
493 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
494 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
500 deltaX = -labelWidth / 4.0 - visualMargin.
left();
501 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
507 deltaX = -labelWidth / 2.0;
508 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
514 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
515 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
521 deltaX = -visualMargin.
left() + symbolWidthOffset;
522 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
528 QTransform transformRotation;
529 transformRotation.rotate(
angle * 180 / M_PI );
530 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
533 double referenceX = std::cos( alpha ) * distanceToLabel + x;
534 double referenceY = std::sin( alpha ) * distanceToLabel + y;
536 labelX = referenceX + deltaX;
537 labelY = referenceY + deltaY;
551 double cost = 0.0001;
552 std::size_t i = lPos.size();
555 std::size_t created = 0;
562 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset,
angle );
566 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
570 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
586 if ( maxNumberCandidates == 0 )
587 maxNumberCandidates = 16;
591 int id = lPos.size();
593 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
598 double a270 = a180 + a90;
599 double a360 = 2 * M_PI;
601 double gamma1, gamma2;
603 if ( distanceToLabel > 0 )
605 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
606 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
610 gamma1 = gamma2 = a90 / 3.0;
613 if ( gamma1 > a90 / 3.0 )
616 if ( gamma2 > a90 / 3.0 )
619 std::size_t numberCandidatesGenerated = 0;
622 double angleToCandidate;
623 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
628 if ( angleToCandidate > a360 )
629 angleToCandidate -= a360;
633 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
635 deltaX = distanceToLabel;
636 double iota = ( angleToCandidate + gamma1 );
637 if ( iota > a360 - gamma1 )
641 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
645 else if ( angleToCandidate < a90 - gamma2 )
647 deltaX = distanceToLabel * std::cos( angleToCandidate );
648 deltaY = distanceToLabel * std::sin( angleToCandidate );
651 else if ( angleToCandidate < a90 + gamma2 )
654 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
655 deltaY = distanceToLabel;
658 else if ( angleToCandidate < a180 - gamma1 )
660 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
661 deltaY = distanceToLabel * std::sin( angleToCandidate );
664 else if ( angleToCandidate < a180 + gamma1 )
666 deltaX = -distanceToLabel - labelWidth;
668 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
671 else if ( angleToCandidate < a270 - gamma2 )
673 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
674 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
677 else if ( angleToCandidate < a270 + gamma2 )
679 deltaY = -distanceToLabel - labelHeight;
681 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
684 else if ( angleToCandidate < a360 )
686 deltaX = distanceToLabel * std::cos( angleToCandidate );
687 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
693 QTransform transformRotation;
694 transformRotation.rotate(
angle * 180 / M_PI );
695 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
697 double labelX =
x + deltaX;
698 double labelY =
y + deltaY;
702 if ( maxNumberCandidates == 1 )
705 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
716 lPos.emplace_back( std::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
717 numberCandidatesGenerated++;
721 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
723 icost =
static_cast< int >( maxNumberCandidates ) - 1;
726 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
728 icost =
static_cast< int >( maxNumberCandidates ) - 2;
734 return numberCandidatesGenerated;
741 double shapeLength = mapShape->
length();
752 std::size_t candidates = 0;
758 if ( candidates < candidateTargetCount )
773 std::vector< double > &
x = line->
x;
774 std::vector< double > &
y = line->
y;
776 std::vector< double > segmentLengths(
nbPoints - 1 );
777 std::vector< double >distanceToSegment(
nbPoints );
779 double totalLineLength = 0.0;
780 for (
int i = 0; i < line->
nbPoints - 1; i++ )
783 distanceToSegment[i] = 0;
785 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
788 totalLineLength += segmentLengths[i];
790 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
793 double lineStepDistance = 0;
796 double currentDistanceAlongLine = lineStepDistance;
800 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
804 currentDistanceAlongLine = lineAnchorPoint;
805 lineStepDistance = -1;
809 double candidateCenterX, candidateCenterY;
811 while ( currentDistanceAlongLine <= totalLineLength )
813 if (
pal->isCanceled() )
818 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
821 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
824 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateCenterX - labelWidth / 2, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
826 currentDistanceAlongLine += lineStepDistance;
830 if ( lineStepDistance < 0 )
844 flags = QgsLabeling::LinePlacementFlag::OnLine;
847 QVector< int > extremeAngleNodes;
850 std::vector< double > &
x = line->
x;
851 std::vector< double > &
y = line->
y;
855 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
857 double x1 =
x[i - 1];
859 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
860 double y1 =
y[i - 1];
862 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
867 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
871 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
872 extremeAngleNodes << i;
874 extremeAngleNodes << numberNodes - 1;
876 if ( extremeAngleNodes.isEmpty() )
883 std::vector< double > segmentLengths( numberNodes - 1 );
884 std::vector< double > distanceToSegment( numberNodes );
885 double totalLineLength = 0.0;
886 QVector< double > straightSegmentLengths;
887 QVector< double > straightSegmentAngles;
888 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
889 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
890 double currentStraightSegmentLength = 0;
891 double longestSegmentLength = 0;
892 int segmentIndex = 0;
893 double segmentStartX =
x[0];
894 double segmentStartY =
y[0];
895 for (
int i = 0; i < numberNodes - 1; i++ )
898 distanceToSegment[i] = 0;
900 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
903 totalLineLength += segmentLengths[i];
904 if ( extremeAngleNodes.contains( i ) )
907 straightSegmentLengths << currentStraightSegmentLength;
909 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
911 currentStraightSegmentLength = 0;
912 segmentStartX =
x[i];
913 segmentStartY =
y[i];
915 currentStraightSegmentLength += segmentLengths[i];
917 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
918 straightSegmentLengths << currentStraightSegmentLength;
920 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
923 if ( totalLineLength < labelWidth )
929 double lineStepDistance = ( totalLineLength - labelWidth );
930 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
932 double distanceToEndOfSegment = 0.0;
933 int lastNodeInSegment = 0;
935 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
937 currentStraightSegmentLength = straightSegmentLengths.at( i );
938 double currentSegmentAngle = straightSegmentAngles.at( i );
939 lastNodeInSegment = extremeAngleNodes.at( i );
940 double distanceToStartOfSegment = distanceToEndOfSegment;
941 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
942 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
944 if ( currentStraightSegmentLength < labelWidth )
948 double currentDistanceAlongLine = distanceToStartOfSegment;
949 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
950 double candidateLength = 0.0;
956 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
957 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
959 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
961 if (
pal->isCanceled() )
967 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
968 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
970 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
976 cost = candidateLength / labelWidth;
982 cost = ( 1 - cost ) / 100;
986 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
988 if ( placementIsFlexible )
991 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
992 cost += costCenter * 0.0005;
1000 double costLineCenter = 2 * std::fabs( labelCenter - lineAnchorPoint ) / totalLineLength;
1001 cost += costLineCenter * 0.0005;
1004 if ( placementIsFlexible )
1006 cost += segmentCost * 0.0005;
1007 cost += segmentAngleCost * 0.0001;
1015 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1019 beta =
angle + M_PI_2;
1024 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1026 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1027 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1028 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1034 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1035 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1042 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1043 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1046 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1050 const double candidateCost = cost + 0.002;
1051 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1057 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1064 currentDistanceAlongLine += lineStepDistance;
1083 flags = QgsLabeling::LinePlacementFlag::OnLine;
1087 std::vector< double > &
x = line->
x;
1088 std::vector< double > &
y = line->
y;
1090 std::vector< double > segmentLengths(
nbPoints - 1 );
1091 std::vector< double >distanceToSegment(
nbPoints );
1093 double totalLineLength = 0.0;
1094 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1097 distanceToSegment[i] = 0;
1099 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1102 totalLineLength += segmentLengths[i];
1104 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1106 double lineStepDistance = ( totalLineLength - labelWidth );
1107 double currentDistanceAlongLine = 0;
1111 if ( totalLineLength > labelWidth )
1113 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1117 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1118 lineStepDistance = -1;
1119 totalLineLength = labelWidth;
1124 currentDistanceAlongLine = std::numeric_limits< double >::max();
1135 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1136 lineStepDistance = -1;
1140 double candidateLength;
1142 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1146 if (
pal->isCanceled() )
1152 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1153 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1155 if ( currentDistanceAlongLine < 0 )
1163 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1166 cost = candidateLength / labelWidth;
1172 cost = ( 1 - cost ) / 100;
1176 double costCenter = std::fabs( lineAnchorPoint - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
1177 cost += costCenter / 1000;
1178 cost += initialCost;
1185 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1189 beta =
angle + M_PI_2;
1194 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1196 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1197 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1198 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1204 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1205 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1212 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1213 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1216 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1220 const double candidateCost = cost + 0.002;
1221 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1227 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1234 currentDistanceAlongLine += lineStepDistance;
1238 if ( lineStepDistance < 0 )
1248 Q_ASSERT( metrics );
1251 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1252 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1254 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1258 labeledLineSegmentIsRightToLeft = placement->flippedCharacterPlacementToGetUprightLabels;
1260 if ( placement->graphemePlacement.empty() )
1263 auto it = placement->graphemePlacement.constBegin();
1264 std::unique_ptr< LabelPosition > firstPosition = std::make_unique< LabelPosition >( 0, it->x, it->y, it->width, it->height, it->angle, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1265 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1266 firstPosition->setPartId( it->graphemeIndex );
1269 while ( it != placement->graphemePlacement.constEnd() )
1271 std::unique_ptr< LabelPosition > position = std::make_unique< LabelPosition >( 0, it->x, it->y, it->width, it->height, it->angle, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1272 position->setPartId( it->graphemeIndex );
1275 previousPosition->
setNextPart( std::move( position ) );
1276 previousPosition = nextPosition;
1280 return firstPosition;
1292 const int characterCount = li->
count();
1293 if ( characterCount == 0 )
1299 double totalCharacterWidth = 0;
1300 for (
int i = 0; i < characterCount; ++i )
1303 std::unique_ptr< PointSet > expanded;
1304 double shapeLength = mapShape->
length();
1307 allowOverrun =
false;
1313 if ( totalCharacterWidth > shapeLength )
1315 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1322 if ( allowOverrun && overrun > 0 )
1325 expanded = mapShape->
clone();
1327 mapShape = expanded.get();
1332 flags = QgsLabeling::LinePlacementFlag::OnLine;
1333 const bool hasAboveBelowLinePlacement = flags & QgsLabeling::LinePlacementFlag::AboveLine || flags & QgsLabeling::LinePlacementFlag::BelowLine;
1335 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1336 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1337 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1340 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1341 mapShapeOffsetPositive = mapShape->
clone();
1342 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1343 mapShapeOffsetNegative = mapShape->
clone();
1344 if ( offsetDistance >= 0.0 || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
1346 if ( mapShapeOffsetPositive )
1347 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1348 if ( mapShapeOffsetNegative )
1349 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1354 if ( flags & QgsLabeling::LinePlacementFlag::AboveLine
1355 && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1357 flags &= ~
QgsLabeling::LinePlacementFlag::AboveLine;
1358 flags |= QgsLabeling::LinePlacementFlag::BelowLine;
1360 else if ( flags & QgsLabeling::LinePlacementFlag::BelowLine
1361 && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1363 flags &= ~
QgsLabeling::LinePlacementFlag::BelowLine;
1364 flags |= QgsLabeling::LinePlacementFlag::AboveLine;
1366 if ( mapShapeOffsetPositive )
1367 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1368 if ( mapShapeOffsetNegative )
1369 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1373 std::vector< std::unique_ptr< LabelPosition >> positions;
1376 PointSet *currentMapShape =
nullptr;
1379 currentMapShape = mapShapeOffsetPositive.get();
1381 if ( offset ==
NoOffset && flags & QgsLabeling::LinePlacementFlag::OnLine )
1383 currentMapShape = mapShape;
1387 currentMapShape = mapShapeOffsetNegative.get();
1389 if ( !currentMapShape )
1393 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1399 if (
pal->isCanceled() )
1403 double delta = std::max( li->
characterHeight() / 6, totalDistance / candidateTargetCount );
1406 double distanceAlongLineToStartCandidate = 0;
1407 bool singleCandidateOnly =
false;
1414 distanceAlongLineToStartCandidate = std::min( lineAnchorPoint, totalDistance * 0.99 -
getLabelWidth() );
1415 singleCandidateOnly =
true;
1419 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1421 if (
pal->isCanceled() )
1425 bool labeledLineSegmentIsRightToLeft =
false;
1427 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly );
1429 if ( !labelPosition )
1431 if ( flags & QgsLabeling::LinePlacementFlag::MapOrientation )
1433 if ( ( offset !=
NoOffset ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1435 if ( ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1440 const double angleDiff = labelPosition->angleDifferential();
1441 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1446 double cost = angleDiffAvg / 100;
1447 if ( cost < 0.0001 )
1451 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1452 double costCenter = std::fabs( lineAnchorPoint - labelCenter ) / totalDistance;
1453 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1455 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1467 labelPosition->setCost( cost );
1469 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1474 while ( within && currentPos )
1477 currentPos = currentPos->
nextPart();
1486 positions.emplace_back( std::move( p ) );
1488 if ( singleCandidateOnly )
1493 for ( std::unique_ptr< LabelPosition > &pos : positions )
1495 lPos.emplace_back( std::move( pos ) );
1498 return positions.size();
1522 const double totalArea =
area();
1524 mapShape->
parent =
nullptr;
1526 if (
pal->isCanceled() )
1529 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1531 QgsDebugMsg( QStringLiteral(
"PAL split polygons resulted in:" ) );
1532 for (
PointSet *ps : shapes_final )
1538 std::size_t nbp = 0;
1540 if ( !shapes_final.isEmpty() )
1548 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1550 std::vector< OrientedConvexHullBoundingBox > boxes;
1551 boxes.reserve( shapes_final.size() );
1554 while ( !shapes_final.isEmpty() )
1556 PointSet *shape = shapes_final.takeFirst();
1560 boxes.emplace_back( box );
1566 if (
pal->isCanceled() )
1570 double densityY = densityX;
1577 std::size_t numberCandidatesGenerated = 0;
1592 double dx = densityX;
1593 double dy = densityY;
1594 if ( numTry == 0 && maxPolygonCandidates > 0 )
1597 const double boxArea = box.width * box.length;
1598 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1599 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1603 if (
pal->isCanceled() )
1604 return numberCandidatesGenerated;
1623 bool enoughPlace =
false;
1627 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1628 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1634 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1636 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1640 enoughPlace =
false;
1656 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1658 if ( box.alpha <= M_PI_4 )
1664 alpha = box.alpha - M_PI_2;
1667 else if ( box.length > box.width )
1669 alpha = box.alpha - M_PI_2;
1676 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1682 dlx = std::cos( beta ) * diago;
1683 dly = std::sin( beta ) * diago;
1685 double px0 = box.width / 2.0;
1686 double py0 = box.length / 2.0;
1688 px0 -= std::ceil( px0 / dx ) * dx;
1689 py0 -= std::ceil( py0 / dy ) * dy;
1691 for ( px = px0; px <= box.width; px += dx )
1693 if (
pal->isCanceled() )
1696 for ( py = py0; py <= box.length; py += dy )
1699 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1700 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1710 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1711 numberCandidatesGenerated++;
1722 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1724 lPos.emplace_back( std::move( potentialCandidate ) );
1725 numberCandidatesGenerated++;
1732 nbp = numberCandidatesGenerated;
1733 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1744 while ( numTry < maxTry );
1746 nbp = numberCandidatesGenerated;
1760 std::size_t candidatesCreated = 0;
1820 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1822 return candidatesCreated;
1826 return candidatesCreated;
1830 const double ringLength = ring->
length();
1831 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
1833 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
1836 const double delta = ringLength / targetPolygonCandidates;
1839 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
1840 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
1841 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
1844 const double labelAngle = 0;
1846 std::size_t i = lPos.size();
1854 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
1856 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
1857 if ( candidate->intersects( preparedBuffer.get() ) )
1875 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
1876 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
1877 candidate->setCost( centroidCost );
1879 lPos.emplace_back( std::move( candidate ) );
1880 candidatesCreated++;
1885 double startSegmentX,
double startSegmentY,
double,
double,
1886 double endSegmentX,
double endSegmentY,
double,
double )
1889 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
1899 else if (
angle <= 85 )
1903 else if (
angle <= 90 )
1909 else if (
angle <= 95 )
1914 else if (
angle <= 175 )
1918 else if (
angle <= 180 )
1924 else if (
angle <= 185 )
1929 else if (
angle <= 265 )
1933 else if (
angle <= 270 )
1938 else if (
angle <= 275 )
1943 else if (
angle <= 355 )
1953 return !
pal->isCanceled();
1956 return candidatesCreated;
1961 std::vector< std::unique_ptr< LabelPosition > > lPos;
1981 case GEOS_LINESTRING:
1995 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
1996 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2004 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2005 std::fabs(
ymax -
ymin ) < labelHeight ) )
2012 std::size_t created = 0;
2070 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2072 double sizeCost = 0;
2073 if ( geomType == GEOS_LINESTRING )
2075 const double l =
length();
2078 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2079 if ( l >= bbox_length / 4 )
2082 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2084 else if ( geomType == GEOS_POLYGON )
2086 const double a =
area();
2089 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2090 if ( a >= bbox_area / 16 )
2093 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2099 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2101 pos->setCost( pos->cost() + sizeCost / 100 );
2112 const double x1first =
x.front();
2113 const double x1last =
x.back();
2114 const double x2first = p2->
x.front();
2115 const double x2last = p2->
x.back();
2116 const double y1first =
y.front();
2117 const double y1last =
y.back();
2118 const double y2first = p2->
y.front();
2119 const double y2last = p2->
y.back();
2127 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2133 const double p2otherX = p2startTouches ? x2last : x2first;
2134 const double p2otherY = p2startTouches ? y2last : y2first;
2138 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2139 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2140 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2142 GEOSCoordSeq_setX_r( geosctxt, coord, 0, p2otherX );
2143 GEOSCoordSeq_setY_r( geosctxt, coord, 0, p2otherY );
2149 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2151 catch ( GEOSException &e )
2153 qWarning(
"GEOS exception: %s", e.what() );
2163 if ( !other->
mGeos )
2169 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2170 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2171 GEOSGeometry *geoms[2] = { g1, g2 };
2172 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2175 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2183 mGeos = gTmp.release();
2192 catch ( GEOSException &e )
2194 qWarning(
"GEOS exception: %s", e.what() );
2215 bool uprightLabel =
false;
2220 uprightLabel =
true;
2226 uprightLabel =
true;
2232 uprightLabel =
true;
2234 return uprightLabel;
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
static double normalizedAngle(double angle) SIP_HOLDGIL
Ensures that an angle is in the range 0 <= angle < 2 pi.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
static GEOSContextHandle_t getGEOSHandler()
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
double overrunSmoothDistance() const
Returns the distance (in map units) with which the ends of linear features are averaged over when cal...
QVector< QgsPalLayerSettings::PredefinedPointPosition > predefinedPositionOrder() const
Returns the priority ordered list of predefined positions for label candidates.
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)
const QSizeF & symbolSize() const
Returns the size of the rendered symbol associated with this feature, if applicable.
QgsPalLayerSettings::OffsetType offsetType() const
Returns the offset type, which determines how offsets and distance to label behaves.
QgsLabeling::PolygonPlacementFlags polygonPlacementFlags() const
Returns the polygon placement flags, which dictate how polygon labels can be placed.
QgsPointXY positionOffset() const
Applies only to "offset from point" placement strategy.
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
QgsLabeling::LinePlacementFlags arrangementFlags() const
Returns the feature's arrangement flags.
bool alwaysShow() const
Whether label should be always shown (sets very high label priority)
double lineAnchorPercent() const
Returns the percent along the line at which labels should be placed, for line labels only.
const QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
QgsLabelLineSettings::AnchorType lineAnchorType() const
Returns the line anchor type, which dictates how the lineAnchorPercent() setting is handled.
double distLabel() const
Applies to "around point" placement strategy or linestring features.
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
void setAnchorPosition(const QgsPointXY &anchorPosition)
In case of quadrand or aligned positioning, this is set to the anchor point.
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)
double overrunDistance() const
Returns the permissible distance (in map units) which labels are allowed to overrun the start or end ...
double priority() const
Returns the feature's labeling priority.
QgsGeometry permissibleZone() const
Returns the label's permissible zone geometry.
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
const GEOSPreparedGeometry * permissibleZonePrepared() const
Returns a GEOS prepared geometry representing the label's permissibleZone().
QgsPointXY fixedPosition() const
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true)
@ Strict
Line anchor is a strict placement, and other placements are not permitted.
@ HintOnly
Line anchor is a hint for preferred placement only, but other placements close to the hint are permit...
Contains constants and enums relating to labeling.
Line string geometry type, with support for z-dimension and m-values.
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
The QgsMargins class defines the four margins of a rectangle.
double top() const
Returns the top margin.
double right() const
Returns the right margin.
double bottom() const
Returns the bottom margin.
double left() const
Returns the left margin.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
PredefinedPointPosition
Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomMiddle
Label directly below point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ MiddleLeft
Label on left of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
@ MiddleRight
Label on right of point.
@ TopMiddle
Label directly above point.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ TopRight
Label on top-right of point.
@ TopLeft
Label on top-left of point.
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
A class to represent a 2D point.
Contains precalculated properties regarding text metrics for text to be renderered at a later stage.
double characterHeight() const
Character height (actually font metrics height, not individual character height).
int count() const
Returns the total number of characters.
double characterWidth(int position) const
Returns the width of the character at the specified position.
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
LabelLineDirection
Controls behavior of curved text with respect to line directions.
@ FollowLineDirection
Curved text placement will respect the line direction and ignore painter orientation.
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
static CurvePlacementProperties * generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const double *x, const double *y, int numPoints, const std::vector< double > &pathDistances, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, bool uprightOnly=true)
Calculates curved text placement properties.
Main class to handle feature.
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.
std::size_t createCandidatesOutsidePolygon(std::vector< std::unique_ptr< LabelPosition > > &lPos, Pal *pal)
Generate candidates outside of polygon features.
bool hasFixedRotation() const
Returns true if the feature's label has a fixed rotation.
double getLabelHeight(double angle=0.0) const
Returns the height of the label, optionally taking an angle into account.
QList< FeaturePart * > mHoles
double getLabelDistance() const
Returns the distance from the anchor point to the label.
~FeaturePart() override
Deletes the feature.
bool hasFixedPosition() const
Returns true if the feature's label has a fixed position.
std::size_t createCandidatesForPolygon(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate candidates for polygon features.
void setTotalRepeats(int repeats)
Returns the total number of repeating labels associated with this label.
std::size_t maximumPolygonCandidates() const
Returns the maximum number of polygon candidates to generate for this feature.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
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...
bool hasSameLabelFeatureAs(FeaturePart *part) const
Tests whether this feature part belongs to the same QgsLabelFeature as another feature part.
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.
std::unique_ptr< LabelPosition > curvedPlacementAtOffset(PointSet *mapShape, const std::vector< double > &pathDistances, QgsTextRendererUtils::LabelLineDirection direction, double distance, bool &labeledLineSegmentIsRightToLeft, bool applyAngleConstraints)
Returns the label position for a curved label at a specific offset along a path.
double fixedAngle() const
Returns the fixed angle for the feature's label.
std::size_t maximumLineCandidates() const
Returns the maximum number of line candidates to generate for this feature.
std::size_t createHorizontalCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate horizontal candidates for line feature.
std::size_t createCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate candidates for line feature.
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged).
std::size_t createCurvedCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate curved candidates for line features.
bool onlyShowUprightLabels() const
Returns true if feature's label must be displayed upright.
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.
std::unique_ptr< LabelPosition > createCandidatePointOnSurface(PointSet *mapShape)
Creates a single candidate using the "point on sruface" algorithm.
double getLabelWidth(double angle=0.0) const
Returns the width of the label, optionally taking an angle into account.
std::vector< std::unique_ptr< LabelPosition > > createCandidates(Pal *pal)
Generates a list of candidate positions for labels for this feature.
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
Layer * layer()
Returns the layer that feature belongs to.
PathOffset
Path offset variances used in curved placement.
int totalRepeats() const
Returns the total number of repeating labels associated with this label.
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'...
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom
double calculatePriority() const
Calculates the priority for the feature.
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.
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.
QgsLabelFeature * feature()
Returns the parent feature.
std::size_t maximumPointCandidates() const
Returns the maximum number of point candidates to generate for this feature.
static bool reorderPolygon(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.
static double dist_euc2d(double x1, double y1, double x2, double y2)
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.
LabelPosition is a candidate feature label position.
double getAlpha() const
Returns the angle to rotate text (in rad).
Quadrant
Position of label candidate relative to feature.
void setNextPart(std::unique_ptr< LabelPosition > next)
Sets the next part of this label position (i.e.
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
double getX(int i=0) const
Returns the down-left x coordinate.
double getY(int i=0) const
Returns the down-left y coordinate.
A set of features which influence the labeling process.
QgsPalLayerSettings::Placement arrangement() const
Returns the layer's arrangement policy.
QString name() const
Returns the layer's name.
std::size_t maximumPolygonLabelCandidates() const
Returns the maximum number of polygon label candidates to generate for features in this layer.
int connectedFeatureId(QgsFeatureId featureId) const
Returns the connected feature ID for a label feature ID, which is unique for all features which have ...
std::size_t maximumPointLabelCandidates() const
Returns the maximum number of point label candidates to generate for features in this layer.
bool centroidInside() const
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
UpsideDownLabels upsidedownLabels() const
Returns how upside down labels are handled within the layer.
bool isCurved() const
Returns true if the layer has curved labels.
double priority() const
Returns the layer's priority, between 0 and 1.
std::size_t maximumLineLabelCandidates() const
Returns the maximum number of line label candidates to generate for features in this layer.
double maximumLineCandidatesPerMapUnit() const
Returns the maximum number of line label candidate positions per map unit.
double maximumPolygonCandidatesPerMapUnitSquared() const
Returns the maximum number of polygon label candidate positions per map unit squared.
The underlying raw pal geometry class.
std::unique_ptr< PointSet > clone() const
Returns a copy of the point set.
OrientedConvexHullBoundingBox computeConvexHullOrientedBoundingBox(bool &ok)
Computes an oriented bounding box for the shape's convex hull.
double length() const
Returns length of line geometry.
double area() const
Returns area of polygon geometry.
bool isClosed() const
Returns true if pointset is closed.
void createGeosGeom() const
void getCentroid(double &px, double &py, bool forceInside=false) const
const GEOSPreparedGeometry * preparedGeom() const
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py)
Gets a point a set distance along a line geometry.
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
std::tuple< std::vector< double >, double > edgeDistances() const
Returns a vector of edge distances as well as its total length.
static QLinkedList< PointSet * > splitPolygons(PointSet *inputShape, double labelWidth, double labelHeight)
Split a polygon using some random logic into some other polygons.
void createCandidateAtOrderedPositionOverPoint(double &labelX, double &labelY, LabelPosition::Quadrant &quadrant, double x, double y, double labelWidth, double labelHeight, QgsPalLayerSettings::PredefinedPointPosition position, double distanceToLabel, const QgsMargins &visualMargin, double symbolWidthOffset, double symbolHeightOffset, double angle)
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< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Represents the minimum area, oriented bounding box surrounding a convex hull.