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 = !uprightOnly ? placement->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();
1328 shapeLength += 2 * overrun;
1333 flags = QgsLabeling::LinePlacementFlag::OnLine;
1334 const bool hasAboveBelowLinePlacement = flags & QgsLabeling::LinePlacementFlag::AboveLine || flags & QgsLabeling::LinePlacementFlag::BelowLine;
1336 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1337 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1338 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1341 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1342 mapShapeOffsetPositive = mapShape->
clone();
1343 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1344 mapShapeOffsetNegative = mapShape->
clone();
1345 if ( offsetDistance >= 0.0 || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
1347 if ( mapShapeOffsetPositive )
1348 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1349 if ( mapShapeOffsetNegative )
1350 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1355 if ( flags & QgsLabeling::LinePlacementFlag::AboveLine
1356 && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1358 flags &= ~
QgsLabeling::LinePlacementFlag::AboveLine;
1359 flags |= QgsLabeling::LinePlacementFlag::BelowLine;
1361 else if ( flags & QgsLabeling::LinePlacementFlag::BelowLine
1362 && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1364 flags &= ~
QgsLabeling::LinePlacementFlag::BelowLine;
1365 flags |= QgsLabeling::LinePlacementFlag::AboveLine;
1367 if ( mapShapeOffsetPositive )
1368 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1369 if ( mapShapeOffsetNegative )
1370 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1377 std::vector< std::unique_ptr< LabelPosition >> positions;
1380 PointSet *currentMapShape =
nullptr;
1383 currentMapShape = mapShapeOffsetPositive.get();
1385 if ( offset ==
NoOffset && flags & QgsLabeling::LinePlacementFlag::OnLine )
1387 currentMapShape = mapShape;
1391 currentMapShape = mapShapeOffsetNegative.get();
1393 if ( !currentMapShape )
1397 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1401 double lineAnchorPoint = 0;
1402 if ( originalPoint && offset !=
NoOffset )
1407 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1413 lineAnchorPoint = totalDistance - lineAnchorPoint;
1416 if (
pal->isCanceled() )
1420 double delta = std::max( li->
characterHeight() / 6, totalDistance / candidateTargetCount );
1423 double distanceAlongLineToStartCandidate = 0;
1424 bool singleCandidateOnly =
false;
1431 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.99 -
getLabelWidth() );
1432 singleCandidateOnly =
true;
1436 bool hasTestedFirstPlacement =
false;
1437 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1439 if ( singleCandidateOnly && hasTestedFirstPlacement )
1442 if (
pal->isCanceled() )
1445 hasTestedFirstPlacement =
true;
1447 bool labeledLineSegmentIsRightToLeft =
false;
1449 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly );
1451 if ( !labelPosition )
1453 if ( flags & QgsLabeling::LinePlacementFlag::MapOrientation )
1455 if ( ( offset !=
NoOffset ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1457 if ( ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1462 const double angleDiff = labelPosition->angleDifferential();
1463 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1468 double cost = angleDiffAvg / 100;
1469 if ( cost < 0.0001 )
1473 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1474 double costCenter = std::fabs( lineAnchorPoint - labelCenter ) / totalDistance;
1475 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1477 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1489 labelPosition->setCost( cost );
1491 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1496 while ( within && currentPos )
1499 currentPos = currentPos->
nextPart();
1508 positions.emplace_back( std::move( p ) );
1512 for ( std::unique_ptr< LabelPosition > &pos : positions )
1514 lPos.emplace_back( std::move( pos ) );
1517 return positions.size();
1541 const double totalArea =
area();
1543 mapShape->
parent =
nullptr;
1545 if (
pal->isCanceled() )
1548 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1550 QgsDebugMsg( QStringLiteral(
"PAL split polygons resulted in:" ) );
1551 for (
PointSet *ps : shapes_final )
1557 std::size_t nbp = 0;
1559 if ( !shapes_final.isEmpty() )
1567 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1569 std::vector< OrientedConvexHullBoundingBox > boxes;
1570 boxes.reserve( shapes_final.size() );
1573 while ( !shapes_final.isEmpty() )
1575 PointSet *shape = shapes_final.takeFirst();
1579 boxes.emplace_back( box );
1585 if (
pal->isCanceled() )
1589 double densityY = densityX;
1596 std::size_t numberCandidatesGenerated = 0;
1611 double dx = densityX;
1612 double dy = densityY;
1613 if ( numTry == 0 && maxPolygonCandidates > 0 )
1616 const double boxArea = box.width * box.length;
1617 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1618 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1622 if (
pal->isCanceled() )
1623 return numberCandidatesGenerated;
1642 bool enoughPlace =
false;
1646 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1647 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1653 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1655 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1659 enoughPlace =
false;
1675 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1677 if ( box.alpha <= M_PI_4 )
1683 alpha = box.alpha - M_PI_2;
1686 else if ( box.length > box.width )
1688 alpha = box.alpha - M_PI_2;
1695 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1701 dlx = std::cos( beta ) * diago;
1702 dly = std::sin( beta ) * diago;
1704 double px0 = box.width / 2.0;
1705 double py0 = box.length / 2.0;
1707 px0 -= std::ceil( px0 / dx ) * dx;
1708 py0 -= std::ceil( py0 / dy ) * dy;
1710 for ( px = px0; px <= box.width; px += dx )
1712 if (
pal->isCanceled() )
1715 for ( py = py0; py <= box.length; py += dy )
1718 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1719 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1729 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1730 numberCandidatesGenerated++;
1741 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1743 lPos.emplace_back( std::move( potentialCandidate ) );
1744 numberCandidatesGenerated++;
1751 nbp = numberCandidatesGenerated;
1752 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1763 while ( numTry < maxTry );
1765 nbp = numberCandidatesGenerated;
1779 std::size_t candidatesCreated = 0;
1839 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1841 return candidatesCreated;
1845 return candidatesCreated;
1849 const double ringLength = ring->
length();
1850 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
1852 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
1855 const double delta = ringLength / targetPolygonCandidates;
1858 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
1859 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
1860 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
1863 const double labelAngle = 0;
1865 std::size_t i = lPos.size();
1873 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
1875 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
1876 if ( candidate->intersects( preparedBuffer.get() ) )
1894 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
1895 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
1896 candidate->setCost( centroidCost );
1898 lPos.emplace_back( std::move( candidate ) );
1899 candidatesCreated++;
1904 double startSegmentX,
double startSegmentY,
double,
double,
1905 double endSegmentX,
double endSegmentY,
double,
double )
1908 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
1918 else if (
angle <= 85 )
1922 else if (
angle <= 90 )
1928 else if (
angle <= 95 )
1933 else if (
angle <= 175 )
1937 else if (
angle <= 180 )
1943 else if (
angle <= 185 )
1948 else if (
angle <= 265 )
1952 else if (
angle <= 270 )
1957 else if (
angle <= 275 )
1962 else if (
angle <= 355 )
1972 return !
pal->isCanceled();
1975 return candidatesCreated;
1980 std::vector< std::unique_ptr< LabelPosition > > lPos;
2000 case GEOS_LINESTRING:
2014 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2015 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2023 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2024 std::fabs(
ymax -
ymin ) < labelHeight ) )
2031 std::size_t created = 0;
2089 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2091 double sizeCost = 0;
2092 if ( geomType == GEOS_LINESTRING )
2094 const double l =
length();
2097 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2098 if ( l >= bbox_length / 4 )
2101 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2103 else if ( geomType == GEOS_POLYGON )
2105 const double a =
area();
2108 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2109 if ( a >= bbox_area / 16 )
2112 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2118 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2120 pos->setCost( pos->cost() + sizeCost / 100 );
2131 const double x1first =
x.front();
2132 const double x1last =
x.back();
2133 const double x2first = p2->
x.front();
2134 const double x2last = p2->
x.back();
2135 const double y1first =
y.front();
2136 const double y1last =
y.back();
2137 const double y2first = p2->
y.front();
2138 const double y2last = p2->
y.back();
2146 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2152 const double p2otherX = p2startTouches ? x2last : x2first;
2153 const double p2otherY = p2startTouches ? y2last : y2first;
2157 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2158 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2159 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2161 GEOSCoordSeq_setX_r( geosctxt, coord, 0, p2otherX );
2162 GEOSCoordSeq_setY_r( geosctxt, coord, 0, p2otherY );
2168 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2170 catch ( GEOSException &e )
2172 qWarning(
"GEOS exception: %s", e.what() );
2182 if ( !other->
mGeos )
2188 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2189 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2190 GEOSGeometry *geoms[2] = { g1, g2 };
2191 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2194 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2202 mGeos = gTmp.release();
2211 catch ( GEOSException &e )
2213 qWarning(
"GEOS exception: %s", e.what() );
2234 bool uprightLabel =
false;
2239 uprightLabel =
true;
2245 uprightLabel =
true;
2251 uprightLabel =
true;
2253 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.
geos::unique_ptr interpolatePoint(double distance) const
Returns a GEOS geometry representing the point interpolated on the shape by distance.
std::unique_ptr< PointSet > clone() const
Returns a copy of the point set.
double lineLocatePoint(const GEOSGeometry *point) const
Returns the distance along the geometry closest to the specified GEOS point.
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.