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 )
443 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
444 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
450 deltaX = -labelWidth / 4.0 - visualMargin.
left();
451 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
457 deltaX = -labelWidth / 2.0;
458 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
464 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
465 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
471 deltaX = - visualMargin.
left() + symbolWidthOffset;
472 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
478 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
479 deltaY = -labelHeight / 2.0;
485 deltaX = -visualMargin.
left() + symbolWidthOffset;
486 deltaY = -labelHeight / 2.0;
492 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
493 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
499 deltaX = -labelWidth / 4.0 - visualMargin.
left();
500 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
506 deltaX = -labelWidth / 2.0;
507 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
513 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
514 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
520 deltaX = -visualMargin.
left() + symbolWidthOffset;
521 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
526 double referenceX = std::cos( alpha ) * distanceToLabel + x;
527 double referenceY = std::sin( alpha ) * distanceToLabel + y;
529 labelX = referenceX + deltaX;
530 labelY = referenceY + deltaY;
544 double cost = 0.0001;
545 std::size_t i = lPos.size();
548 std::size_t created = 0;
555 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset );
559 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
563 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
579 if ( maxNumberCandidates == 0 )
580 maxNumberCandidates = 16;
584 int id = lPos.size();
586 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
591 double a270 = a180 + a90;
592 double a360 = 2 * M_PI;
594 double gamma1, gamma2;
596 if ( distanceToLabel > 0 )
598 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
599 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
603 gamma1 = gamma2 = a90 / 3.0;
606 if ( gamma1 > a90 / 3.0 )
609 if ( gamma2 > a90 / 3.0 )
612 std::size_t numberCandidatesGenerated = 0;
615 double angleToCandidate;
616 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
621 if ( angleToCandidate > a360 )
622 angleToCandidate -= a360;
626 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
628 labelX += distanceToLabel;
629 double iota = ( angleToCandidate + gamma1 );
630 if ( iota > a360 - gamma1 )
634 labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
638 else if ( angleToCandidate < a90 - gamma2 )
640 labelX += distanceToLabel * std::cos( angleToCandidate );
641 labelY += distanceToLabel * std::sin( angleToCandidate );
644 else if ( angleToCandidate < a90 + gamma2 )
647 labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
648 labelY += distanceToLabel;
651 else if ( angleToCandidate < a180 - gamma1 )
653 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
654 labelY += distanceToLabel * std::sin( angleToCandidate );
657 else if ( angleToCandidate < a180 + gamma1 )
659 labelX += -distanceToLabel - labelWidth;
661 labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
664 else if ( angleToCandidate < a270 - gamma2 )
666 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
667 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
670 else if ( angleToCandidate < a270 + gamma2 )
672 labelY += -distanceToLabel - labelHeight;
674 labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
677 else if ( angleToCandidate < a360 )
679 labelX += distanceToLabel * std::cos( angleToCandidate );
680 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
686 if ( maxNumberCandidates == 1 )
689 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
700 lPos.emplace_back( std::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
701 numberCandidatesGenerated++;
705 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
707 icost =
static_cast< int >( maxNumberCandidates ) - 1;
710 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
712 icost =
static_cast< int >( maxNumberCandidates ) - 2;
718 return numberCandidatesGenerated;
725 double shapeLength = mapShape->
length();
736 std::size_t candidates = 0;
742 if ( candidates < candidateTargetCount )
757 std::vector< double > &
x = line->
x;
758 std::vector< double > &
y = line->
y;
760 std::vector< double > segmentLengths(
nbPoints - 1 );
761 std::vector< double >distanceToSegment(
nbPoints );
763 double totalLineLength = 0.0;
764 for (
int i = 0; i < line->
nbPoints - 1; i++ )
767 distanceToSegment[i] = 0;
769 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
772 totalLineLength += segmentLengths[i];
774 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
777 double lineStepDistance = 0;
780 double currentDistanceAlongLine = lineStepDistance;
784 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
788 currentDistanceAlongLine = lineAnchorPoint;
789 lineStepDistance = -1;
793 double candidateCenterX, candidateCenterY;
795 while ( currentDistanceAlongLine <= totalLineLength )
797 if (
pal->isCanceled() )
802 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
805 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
808 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateCenterX - labelWidth / 2, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
810 currentDistanceAlongLine += lineStepDistance;
814 if ( lineStepDistance < 0 )
828 flags = QgsLabeling::LinePlacementFlag::OnLine;
831 QVector< int > extremeAngleNodes;
834 std::vector< double > &
x = line->
x;
835 std::vector< double > &
y = line->
y;
839 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
841 double x1 =
x[i - 1];
843 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
844 double y1 =
y[i - 1];
846 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
851 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
855 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
856 extremeAngleNodes << i;
858 extremeAngleNodes << numberNodes - 1;
860 if ( extremeAngleNodes.isEmpty() )
867 std::vector< double > segmentLengths( numberNodes - 1 );
868 std::vector< double > distanceToSegment( numberNodes );
869 double totalLineLength = 0.0;
870 QVector< double > straightSegmentLengths;
871 QVector< double > straightSegmentAngles;
872 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
873 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
874 double currentStraightSegmentLength = 0;
875 double longestSegmentLength = 0;
876 int segmentIndex = 0;
877 double segmentStartX =
x[0];
878 double segmentStartY =
y[0];
879 for (
int i = 0; i < numberNodes - 1; i++ )
882 distanceToSegment[i] = 0;
884 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
887 totalLineLength += segmentLengths[i];
888 if ( extremeAngleNodes.contains( i ) )
891 straightSegmentLengths << currentStraightSegmentLength;
893 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
895 currentStraightSegmentLength = 0;
896 segmentStartX =
x[i];
897 segmentStartY =
y[i];
899 currentStraightSegmentLength += segmentLengths[i];
901 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
902 straightSegmentLengths << currentStraightSegmentLength;
904 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
907 if ( totalLineLength < labelWidth )
913 double lineStepDistance = ( totalLineLength - labelWidth );
914 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
916 double distanceToEndOfSegment = 0.0;
917 int lastNodeInSegment = 0;
919 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
921 currentStraightSegmentLength = straightSegmentLengths.at( i );
922 double currentSegmentAngle = straightSegmentAngles.at( i );
923 lastNodeInSegment = extremeAngleNodes.at( i );
924 double distanceToStartOfSegment = distanceToEndOfSegment;
925 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
926 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
928 if ( currentStraightSegmentLength < labelWidth )
932 double currentDistanceAlongLine = distanceToStartOfSegment;
933 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
934 double candidateLength = 0.0;
940 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
941 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
943 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
945 if (
pal->isCanceled() )
951 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
952 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
954 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
960 cost = candidateLength / labelWidth;
966 cost = ( 1 - cost ) / 100;
970 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
972 if ( placementIsFlexible )
975 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
976 cost += costCenter * 0.0005;
984 double costLineCenter = 2 * std::fabs( labelCenter - lineAnchorPoint ) / totalLineLength;
985 cost += costLineCenter * 0.0005;
988 if ( placementIsFlexible )
990 cost += segmentCost * 0.0005;
991 cost += segmentAngleCost * 0.0001;
999 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1003 beta =
angle + M_PI_2;
1008 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1010 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1011 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1012 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1018 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1019 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 ) );
1026 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1027 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 ) );
1030 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1034 const double candidateCost = cost + 0.002;
1035 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 ) );
1041 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1048 currentDistanceAlongLine += lineStepDistance;
1067 flags = QgsLabeling::LinePlacementFlag::OnLine;
1071 std::vector< double > &
x = line->
x;
1072 std::vector< double > &
y = line->
y;
1074 std::vector< double > segmentLengths(
nbPoints - 1 );
1075 std::vector< double >distanceToSegment(
nbPoints );
1077 double totalLineLength = 0.0;
1078 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1081 distanceToSegment[i] = 0;
1083 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1086 totalLineLength += segmentLengths[i];
1088 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1090 double lineStepDistance = ( totalLineLength - labelWidth );
1091 double currentDistanceAlongLine = 0;
1095 if ( totalLineLength > labelWidth )
1097 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1101 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1102 lineStepDistance = -1;
1103 totalLineLength = labelWidth;
1108 currentDistanceAlongLine = std::numeric_limits< double >::max();
1119 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1120 lineStepDistance = -1;
1124 double candidateLength;
1126 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1130 if (
pal->isCanceled() )
1136 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1137 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1139 if ( currentDistanceAlongLine < 0 )
1147 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1150 cost = candidateLength / labelWidth;
1156 cost = ( 1 - cost ) / 100;
1160 double costCenter = std::fabs( lineAnchorPoint - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
1161 cost += costCenter / 1000;
1162 cost += initialCost;
1169 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1173 beta =
angle + M_PI_2;
1178 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1180 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1181 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1182 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1188 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1189 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 ) );
1196 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1197 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 ) );
1200 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1204 const double candidateCost = cost + 0.002;
1205 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 ) );
1211 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1218 currentDistanceAlongLine += lineStepDistance;
1222 if ( lineStepDistance < 0 )
1232 Q_ASSERT( metrics );
1235 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1236 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1238 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1242 labeledLineSegmentIsRightToLeft = placement->flippedCharacterPlacementToGetUprightLabels;
1244 if ( placement->graphemePlacement.empty() )
1247 auto it = placement->graphemePlacement.constBegin();
1248 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 );
1249 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1250 firstPosition->setPartId( it->graphemeIndex );
1253 while ( it != placement->graphemePlacement.constEnd() )
1255 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 );
1256 position->setPartId( it->graphemeIndex );
1259 previousPosition->
setNextPart( std::move( position ) );
1260 previousPosition = nextPosition;
1264 return firstPosition;
1276 const int characterCount = li->
count();
1277 if ( characterCount == 0 )
1283 double totalCharacterWidth = 0;
1284 for (
int i = 0; i < characterCount; ++i )
1287 std::unique_ptr< PointSet > expanded;
1288 double shapeLength = mapShape->
length();
1291 allowOverrun =
false;
1297 if ( totalCharacterWidth > shapeLength )
1299 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1306 if ( allowOverrun && overrun > 0 )
1309 expanded = mapShape->
clone();
1311 mapShape = expanded.get();
1316 flags = QgsLabeling::LinePlacementFlag::OnLine;
1317 const bool hasAboveBelowLinePlacement = flags & QgsLabeling::LinePlacementFlag::AboveLine || flags & QgsLabeling::LinePlacementFlag::BelowLine;
1319 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1320 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1321 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1324 mapShapeOffsetPositive = mapShape->
clone();
1325 mapShapeOffsetNegative = mapShape->
clone();
1326 if ( offsetDistance >= 0.0 )
1328 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1329 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1334 if ( flags & QgsLabeling::LinePlacementFlag::AboveLine
1335 && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1337 flags &= ~
QgsLabeling::LinePlacementFlag::AboveLine;
1338 flags |= QgsLabeling::LinePlacementFlag::BelowLine;
1340 else if ( flags & QgsLabeling::LinePlacementFlag::BelowLine
1341 && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1343 flags &= ~
QgsLabeling::LinePlacementFlag::BelowLine;
1344 flags |= QgsLabeling::LinePlacementFlag::AboveLine;
1346 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1347 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1351 std::vector< std::unique_ptr< LabelPosition >> positions;
1354 PointSet *currentMapShape =
nullptr;
1357 currentMapShape = mapShapeOffsetPositive.get();
1359 if ( offset ==
NoOffset && flags & QgsLabeling::LinePlacementFlag::OnLine )
1361 currentMapShape = mapShape;
1365 currentMapShape = mapShapeOffsetNegative.get();
1367 if ( !currentMapShape )
1371 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1377 if (
pal->isCanceled() )
1381 double delta = std::max( li->
characterHeight() / 6, totalDistance / candidateTargetCount );
1384 double distanceAlongLineToStartCandidate = 0;
1385 bool singleCandidateOnly =
false;
1392 distanceAlongLineToStartCandidate = std::min( lineAnchorPoint, totalDistance * 0.99 -
getLabelWidth() );
1393 singleCandidateOnly =
true;
1397 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1399 if (
pal->isCanceled() )
1403 bool labeledLineSegmentIsRightToLeft =
false;
1405 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly );
1407 if ( !labelPosition )
1409 if ( ( offset !=
NoOffset ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1411 if ( ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1415 const double angleDiff = labelPosition->angleDifferential();
1416 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1421 double cost = angleDiffAvg / 100;
1422 if ( cost < 0.0001 )
1426 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1427 double costCenter = std::fabs( lineAnchorPoint - labelCenter ) / totalDistance;
1428 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1430 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1442 labelPosition->setCost( cost );
1444 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1449 while ( within && currentPos )
1452 currentPos = currentPos->
nextPart();
1461 positions.emplace_back( std::move( p ) );
1463 if ( singleCandidateOnly )
1468 for ( std::unique_ptr< LabelPosition > &pos : positions )
1470 lPos.emplace_back( std::move( pos ) );
1473 return positions.size();
1497 const double totalArea =
area();
1499 mapShape->
parent =
nullptr;
1501 if (
pal->isCanceled() )
1504 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1506 QgsDebugMsg( QStringLiteral(
"PAL split polygons resulted in:" ) );
1507 for (
PointSet *ps : shapes_final )
1513 std::size_t nbp = 0;
1515 if ( !shapes_final.isEmpty() )
1523 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1525 std::vector< OrientedConvexHullBoundingBox > boxes;
1526 boxes.reserve( shapes_final.size() );
1529 while ( !shapes_final.isEmpty() )
1531 PointSet *shape = shapes_final.takeFirst();
1535 boxes.emplace_back( box );
1541 if (
pal->isCanceled() )
1545 double densityY = densityX;
1552 std::size_t numberCandidatesGenerated = 0;
1567 double dx = densityX;
1568 double dy = densityY;
1569 if ( numTry == 0 && maxPolygonCandidates > 0 )
1572 const double boxArea = box.width * box.length;
1573 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1574 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1578 if (
pal->isCanceled() )
1579 return numberCandidatesGenerated;
1598 bool enoughPlace =
false;
1602 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1603 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1609 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1611 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1615 enoughPlace =
false;
1631 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1633 if ( box.alpha <= M_PI_4 )
1639 alpha = box.alpha - M_PI_2;
1642 else if ( box.length > box.width )
1644 alpha = box.alpha - M_PI_2;
1651 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1657 dlx = std::cos( beta ) * diago;
1658 dly = std::sin( beta ) * diago;
1660 double px0 = box.width / 2.0;
1661 double py0 = box.length / 2.0;
1663 px0 -= std::ceil( px0 / dx ) * dx;
1664 py0 -= std::ceil( py0 / dy ) * dy;
1666 for ( px = px0; px <= box.width; px += dx )
1668 if (
pal->isCanceled() )
1671 for ( py = py0; py <= box.length; py += dy )
1674 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1675 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1685 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1686 numberCandidatesGenerated++;
1697 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1699 lPos.emplace_back( std::move( potentialCandidate ) );
1700 numberCandidatesGenerated++;
1707 nbp = numberCandidatesGenerated;
1708 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1719 while ( numTry < maxTry );
1721 nbp = numberCandidatesGenerated;
1735 std::size_t candidatesCreated = 0;
1795 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1797 return candidatesCreated;
1801 return candidatesCreated;
1805 const double ringLength = ring->
length();
1806 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
1808 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
1811 const double delta = ringLength / targetPolygonCandidates;
1814 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
1815 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
1816 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
1819 const double labelAngle = 0;
1821 std::size_t i = lPos.size();
1829 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0 );
1831 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
1832 if ( candidate->intersects( preparedBuffer.get() ) )
1850 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
1851 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
1852 candidate->setCost( centroidCost );
1854 lPos.emplace_back( std::move( candidate ) );
1855 candidatesCreated++;
1860 double startSegmentX,
double startSegmentY,
double,
double,
1861 double endSegmentX,
double endSegmentY,
double,
double )
1864 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
1874 else if (
angle <= 85 )
1878 else if (
angle <= 90 )
1884 else if (
angle <= 95 )
1889 else if (
angle <= 175 )
1893 else if (
angle <= 180 )
1899 else if (
angle <= 185 )
1904 else if (
angle <= 265 )
1908 else if (
angle <= 270 )
1913 else if (
angle <= 275 )
1918 else if (
angle <= 355 )
1928 return !
pal->isCanceled();
1931 return candidatesCreated;
1936 std::vector< std::unique_ptr< LabelPosition > > lPos;
1956 case GEOS_LINESTRING:
1970 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
1971 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
1979 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
1980 std::fabs(
ymax -
ymin ) < labelHeight ) )
1987 std::size_t created = 0;
2045 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2047 double sizeCost = 0;
2048 if ( geomType == GEOS_LINESTRING )
2050 const double l =
length();
2053 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2054 if ( l >= bbox_length / 4 )
2057 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2059 else if ( geomType == GEOS_POLYGON )
2061 const double a =
area();
2064 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2065 if ( a >= bbox_area / 16 )
2068 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2074 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2076 pos->setCost( pos->cost() + sizeCost / 100 );
2087 const double x1first =
x.front();
2088 const double x1last =
x.back();
2089 const double x2first = p2->
x.front();
2090 const double x2last = p2->
x.back();
2091 const double y1first =
y.front();
2092 const double y1last =
y.back();
2093 const double y2first = p2->
y.front();
2094 const double y2last = p2->
y.back();
2102 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2108 const double p2otherX = p2startTouches ? x2last : x2first;
2109 const double p2otherY = p2startTouches ? y2last : y2first;
2113 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2114 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2115 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2117 GEOSCoordSeq_setX_r( geosctxt, coord, 0, p2otherX );
2118 GEOSCoordSeq_setY_r( geosctxt, coord, 0, p2otherY );
2124 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2126 catch ( GEOSException &e )
2128 qWarning(
"GEOS exception: %s", e.what() );
2138 if ( !other->
mGeos )
2144 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2145 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2146 GEOSGeometry *geoms[2] = { g1, g2 };
2147 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2150 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2158 mGeos = gTmp.release();
2167 catch ( GEOSException &e )
2169 qWarning(
"GEOS exception: %s", e.what() );
2190 bool uprightLabel =
false;
2195 uprightLabel =
true;
2201 uprightLabel =
true;
2207 uprightLabel =
true;
2209 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 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.