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,
Qgis::LabelPredefinedPointPosition 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;
811 double candidateCenterX, candidateCenterY;
813 while ( currentDistanceAlongLine <= totalLineLength )
815 if (
pal->isCanceled() )
820 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
823 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
830 labelX = candidateCenterX;
833 labelX = candidateCenterX - labelWidth / 2;
836 labelX = candidateCenterX - labelWidth;
842 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
844 currentDistanceAlongLine += lineStepDistance;
848 if ( lineStepDistance < 0 )
862 flags = QgsLabeling::LinePlacementFlag::OnLine;
865 QVector< int > extremeAngleNodes;
868 std::vector< double > &
x = line->
x;
869 std::vector< double > &
y = line->
y;
873 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
875 double x1 =
x[i - 1];
877 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
878 double y1 =
y[i - 1];
880 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
885 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
889 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
890 extremeAngleNodes << i;
892 extremeAngleNodes << numberNodes - 1;
894 if ( extremeAngleNodes.isEmpty() )
901 std::vector< double > segmentLengths( numberNodes - 1 );
902 std::vector< double > distanceToSegment( numberNodes );
903 double totalLineLength = 0.0;
904 QVector< double > straightSegmentLengths;
905 QVector< double > straightSegmentAngles;
906 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
907 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
908 double currentStraightSegmentLength = 0;
909 double longestSegmentLength = 0;
910 int segmentIndex = 0;
911 double segmentStartX =
x[0];
912 double segmentStartY =
y[0];
913 for (
int i = 0; i < numberNodes - 1; i++ )
916 distanceToSegment[i] = 0;
918 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
921 totalLineLength += segmentLengths[i];
922 if ( extremeAngleNodes.contains( i ) )
925 straightSegmentLengths << currentStraightSegmentLength;
927 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
929 currentStraightSegmentLength = 0;
930 segmentStartX =
x[i];
931 segmentStartY =
y[i];
933 currentStraightSegmentLength += segmentLengths[i];
935 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
936 straightSegmentLengths << currentStraightSegmentLength;
938 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
941 if ( totalLineLength < labelWidth )
949 double lineStepDistance = ( totalLineLength - labelWidth );
950 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
952 double distanceToEndOfSegment = 0.0;
953 int lastNodeInSegment = 0;
955 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
957 currentStraightSegmentLength = straightSegmentLengths.at( i );
958 double currentSegmentAngle = straightSegmentAngles.at( i );
959 lastNodeInSegment = extremeAngleNodes.at( i );
960 double distanceToStartOfSegment = distanceToEndOfSegment;
961 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
962 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
964 if ( currentStraightSegmentLength < labelWidth )
968 double currentDistanceAlongLine = distanceToStartOfSegment;
969 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
970 double candidateLength = 0.0;
976 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
977 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
979 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
981 if (
pal->isCanceled() )
987 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
988 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
990 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
996 cost = candidateLength / labelWidth;
1002 cost = ( 1 - cost ) / 100;
1005 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1006 double labelTextAnchor = 0;
1007 switch ( textPoint )
1010 labelTextAnchor = currentDistanceAlongLine;
1013 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1016 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1025 if ( placementIsFlexible )
1028 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1029 cost += costCenter * 0.0005;
1037 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1038 cost += costLineCenter * 0.0005;
1041 if ( placementIsFlexible )
1043 cost += segmentCost * 0.0005;
1044 cost += segmentAngleCost * 0.0001;
1052 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1056 beta =
angle + M_PI_2;
1061 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1063 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1064 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1065 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1071 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1072 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 ) );
1079 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1080 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 ) );
1083 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1087 const double candidateCost = cost + 0.002;
1088 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 ) );
1094 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1101 currentDistanceAlongLine += lineStepDistance;
1120 flags = QgsLabeling::LinePlacementFlag::OnLine;
1124 std::vector< double > &
x = line->
x;
1125 std::vector< double > &
y = line->
y;
1127 std::vector< double > segmentLengths(
nbPoints - 1 );
1128 std::vector< double >distanceToSegment(
nbPoints );
1130 double totalLineLength = 0.0;
1131 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1134 distanceToSegment[i] = 0;
1136 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1139 totalLineLength += segmentLengths[i];
1141 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1143 double lineStepDistance = ( totalLineLength - labelWidth );
1144 double currentDistanceAlongLine = 0;
1150 if ( totalLineLength > labelWidth )
1152 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1156 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1157 lineStepDistance = -1;
1158 totalLineLength = labelWidth;
1163 currentDistanceAlongLine = std::numeric_limits< double >::max();
1174 switch ( textPoint )
1177 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1180 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1183 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1189 lineStepDistance = -1;
1193 double candidateLength;
1195 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1199 if (
pal->isCanceled() )
1205 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1206 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1208 if ( currentDistanceAlongLine < 0 )
1216 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1219 cost = candidateLength / labelWidth;
1225 cost = ( 1 - cost ) / 100;
1229 double textAnchorPoint = 0;
1230 switch ( textPoint )
1233 textAnchorPoint = currentDistanceAlongLine;
1236 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1239 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1245 double costCenter = std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength;
1246 cost += costCenter / 1000;
1247 cost += initialCost;
1254 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1258 beta =
angle + M_PI_2;
1263 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1265 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1266 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1267 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1273 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1274 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 ) );
1281 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1282 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 ) );
1285 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1289 const double candidateCost = cost + 0.002;
1290 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 ) );
1296 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1303 currentDistanceAlongLine += lineStepDistance;
1307 if ( lineStepDistance < 0 )
1317 Q_ASSERT( metrics );
1319 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1320 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1322 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1326 labeledLineSegmentIsRightToLeft = !uprightOnly ? placement->labeledLineSegmentIsRightToLeft : placement->flippedCharacterPlacementToGetUprightLabels;
1328 if ( placement->graphemePlacement.empty() )
1331 auto it = placement->graphemePlacement.constBegin();
1332 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 );
1333 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1334 firstPosition->setPartId( it->graphemeIndex );
1337 while ( it != placement->graphemePlacement.constEnd() )
1339 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 );
1340 position->setPartId( it->graphemeIndex );
1343 previousPosition->
setNextPart( std::move( position ) );
1344 previousPosition = nextPosition;
1348 return firstPosition;
1360 const int characterCount = li->
count();
1361 if ( characterCount == 0 )
1367 double totalCharacterWidth = 0;
1368 for (
int i = 0; i < characterCount; ++i )
1371 std::unique_ptr< PointSet > expanded;
1372 double shapeLength = mapShape->
length();
1375 allowOverrun =
false;
1393 if ( totalCharacterWidth > shapeLength )
1395 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1406 if ( allowOverrun && overrun > 0 )
1409 expanded = mapShape->
clone();
1411 mapShape = expanded.get();
1412 shapeLength += 2 * overrun;
1417 flags = QgsLabeling::LinePlacementFlag::OnLine;
1418 const bool hasAboveBelowLinePlacement = flags & QgsLabeling::LinePlacementFlag::AboveLine || flags & QgsLabeling::LinePlacementFlag::BelowLine;
1420 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1421 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1422 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1425 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1426 mapShapeOffsetPositive = mapShape->
clone();
1427 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1428 mapShapeOffsetNegative = mapShape->
clone();
1429 if ( offsetDistance >= 0.0 || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
1431 if ( mapShapeOffsetPositive )
1432 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1433 if ( mapShapeOffsetNegative )
1434 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1439 if ( flags & QgsLabeling::LinePlacementFlag::AboveLine
1440 && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1442 flags &= ~
QgsLabeling::LinePlacementFlag::AboveLine;
1443 flags |= QgsLabeling::LinePlacementFlag::BelowLine;
1445 else if ( flags & QgsLabeling::LinePlacementFlag::BelowLine
1446 && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1448 flags &= ~
QgsLabeling::LinePlacementFlag::BelowLine;
1449 flags |= QgsLabeling::LinePlacementFlag::AboveLine;
1451 if ( mapShapeOffsetPositive )
1452 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1453 if ( mapShapeOffsetNegative )
1454 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1460 std::vector< std::unique_ptr< LabelPosition >> positions;
1461 std::unique_ptr< LabelPosition > backupPlacement;
1464 PointSet *currentMapShape =
nullptr;
1467 currentMapShape = mapShapeOffsetPositive.get();
1469 if ( offset ==
NoOffset && flags & QgsLabeling::LinePlacementFlag::OnLine )
1471 currentMapShape = mapShape;
1475 currentMapShape = mapShapeOffsetNegative.get();
1477 if ( !currentMapShape )
1481 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1485 double lineAnchorPoint = 0;
1486 if ( originalPoint && offset !=
NoOffset )
1491 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1497 lineAnchorPoint = totalDistance - lineAnchorPoint;
1500 if (
pal->isCanceled() )
1504 double delta = std::max( li->
characterHeight() / 6, totalDistance / candidateTargetCount );
1507 double distanceAlongLineToStartCandidate = 0;
1508 bool singleCandidateOnly =
false;
1515 switch ( textPoint )
1518 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1521 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1524 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1530 singleCandidateOnly =
true;
1534 bool hasTestedFirstPlacement =
false;
1535 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1537 if ( singleCandidateOnly && hasTestedFirstPlacement )
1540 if (
pal->isCanceled() )
1543 hasTestedFirstPlacement =
true;
1545 bool labeledLineSegmentIsRightToLeft =
false;
1547 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly,
1548 onlyShowUprightLabels() && ( !singleCandidateOnly || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ) );
1550 if ( !labelPosition )
1555 bool isBackupPlacementOnly =
false;
1556 if ( flags & QgsLabeling::LinePlacementFlag::MapOrientation )
1558 if ( ( offset !=
NoOffset ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1561 isBackupPlacementOnly =
true;
1565 if ( ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1568 isBackupPlacementOnly =
true;
1574 backupPlacement.reset();
1577 const double angleDiff = labelPosition->angleDifferential();
1578 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1583 double cost = angleDiffAvg / 100;
1584 if ( cost < 0.0001 )
1588 double labelTextAnchor = 0;
1589 switch ( textPoint )
1592 labelTextAnchor = distanceAlongLineToStartCandidate;
1595 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1598 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1604 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1605 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1607 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1619 labelPosition->setCost( cost );
1621 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1626 while ( within && currentPos )
1629 currentPos = currentPos->
nextPart();
1639 if ( isBackupPlacementOnly )
1640 backupPlacement = std::move( p );
1642 positions.emplace_back( std::move( p ) );
1647 for ( std::unique_ptr< LabelPosition > &pos : positions )
1649 lPos.emplace_back( std::move( pos ) );
1652 if ( backupPlacement )
1653 lPos.emplace_back( std::move( backupPlacement ) );
1655 return positions.size();
1679 const double totalArea =
area();
1681 mapShape->
parent =
nullptr;
1683 if (
pal->isCanceled() )
1686 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1688 QgsDebugMsg( QStringLiteral(
"PAL split polygons resulted in:" ) );
1689 for (
PointSet *ps : shapes_final )
1695 std::size_t nbp = 0;
1697 if ( !shapes_final.isEmpty() )
1705 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1707 std::vector< OrientedConvexHullBoundingBox > boxes;
1708 boxes.reserve( shapes_final.size() );
1711 while ( !shapes_final.isEmpty() )
1713 PointSet *shape = shapes_final.takeFirst();
1717 boxes.emplace_back( box );
1723 if (
pal->isCanceled() )
1727 double densityY = densityX;
1734 std::size_t numberCandidatesGenerated = 0;
1749 double dx = densityX;
1750 double dy = densityY;
1751 if ( numTry == 0 && maxPolygonCandidates > 0 )
1754 const double boxArea = box.width * box.length;
1755 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1756 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1760 if (
pal->isCanceled() )
1761 return numberCandidatesGenerated;
1780 bool enoughPlace =
false;
1784 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1785 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1791 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1793 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1797 enoughPlace =
false;
1813 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1815 if ( box.alpha <= M_PI_4 )
1821 alpha = box.alpha - M_PI_2;
1824 else if ( box.length > box.width )
1826 alpha = box.alpha - M_PI_2;
1833 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1839 dlx = std::cos( beta ) * diago;
1840 dly = std::sin( beta ) * diago;
1842 double px0 = box.width / 2.0;
1843 double py0 = box.length / 2.0;
1845 px0 -= std::ceil( px0 / dx ) * dx;
1846 py0 -= std::ceil( py0 / dy ) * dy;
1848 for ( px = px0; px <= box.width; px += dx )
1850 if (
pal->isCanceled() )
1853 for ( py = py0; py <= box.length; py += dy )
1856 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1857 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1867 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1868 numberCandidatesGenerated++;
1879 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1881 lPos.emplace_back( std::move( potentialCandidate ) );
1882 numberCandidatesGenerated++;
1889 nbp = numberCandidatesGenerated;
1890 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1901 while ( numTry < maxTry );
1903 nbp = numberCandidatesGenerated;
1917 std::size_t candidatesCreated = 0;
1977 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1979 return candidatesCreated;
1983 return candidatesCreated;
1987 const double ringLength = ring->
length();
1988 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
1990 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
1993 const double delta = ringLength / targetPolygonCandidates;
1996 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
1997 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
1998 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2001 const double labelAngle = 0;
2003 std::size_t i = lPos.size();
2011 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2013 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
2014 if ( candidate->intersects( preparedBuffer.get() ) )
2032 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
2033 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2034 candidate->setCost( centroidCost );
2036 lPos.emplace_back( std::move( candidate ) );
2037 candidatesCreated++;
2042 double startSegmentX,
double startSegmentY,
double,
double,
2043 double endSegmentX,
double endSegmentY,
double,
double )
2046 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2056 else if (
angle <= 85 )
2060 else if (
angle <= 90 )
2066 else if (
angle <= 95 )
2071 else if (
angle <= 175 )
2075 else if (
angle <= 180 )
2081 else if (
angle <= 185 )
2086 else if (
angle <= 265 )
2090 else if (
angle <= 270 )
2095 else if (
angle <= 275 )
2100 else if (
angle <= 355 )
2110 return !
pal->isCanceled();
2113 return candidatesCreated;
2118 std::vector< std::unique_ptr< LabelPosition > > lPos;
2138 case GEOS_LINESTRING:
2152 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2153 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2161 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2162 std::fabs(
ymax -
ymin ) < labelHeight ) )
2169 std::size_t created = 0;
2227 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2229 double sizeCost = 0;
2230 if ( geomType == GEOS_LINESTRING )
2232 const double l =
length();
2235 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2236 if ( l >= bbox_length / 4 )
2239 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2241 else if ( geomType == GEOS_POLYGON )
2243 const double a =
area();
2246 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2247 if ( a >= bbox_area / 16 )
2250 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2256 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2258 pos->setCost( pos->cost() + sizeCost / 100 );
2269 const double x1first =
x.front();
2270 const double x1last =
x.back();
2271 const double x2first = p2->
x.front();
2272 const double x2last = p2->
x.back();
2273 const double y1first =
y.front();
2274 const double y1last =
y.back();
2275 const double y2first = p2->
y.front();
2276 const double y2last = p2->
y.back();
2284 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2290 const double p2otherX = p2startTouches ? x2last : x2first;
2291 const double p2otherY = p2startTouches ? y2last : y2first;
2295 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2296 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2297 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2299 GEOSCoordSeq_setX_r( geosctxt, coord, 0, p2otherX );
2300 GEOSCoordSeq_setY_r( geosctxt, coord, 0, p2otherY );
2306 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2308 catch ( GEOSException &e )
2310 qWarning(
"GEOS exception: %s", e.what() );
2320 if ( !other->
mGeos )
2326 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2327 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2328 GEOSGeometry *geoms[2] = { g1, g2 };
2329 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2332 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2340 mGeos = gTmp.release();
2349 catch ( GEOSException &e )
2351 qWarning(
"GEOS exception: %s", e.what() );
2372 bool result =
false;
2376 case Qgis::UpsideDownLabelHandling::FlipUpsideDownLabels:
2379 case Qgis::UpsideDownLabelHandling::AllowUpsideDownWhenRotationIsDefined:
2386 case Qgis::UpsideDownLabelHandling::AlwaysAllowUpsideDown:
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
@ 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'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
@ MiddleLeft
Label on left of point.
@ TopRight
Label on top-right of point.
@ MiddleRight
Label on right of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopMiddle
Label directly above point.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ TopLeft
Label on top-left of point.
@ BottomMiddle
Label directly below point.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
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...
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.
QVector< Qgis::LabelPredefinedPointPosition > predefinedPositionOrder() const
Returns the priority ordered list of predefined positions for label candidates.
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.
QgsLabelLineSettings::AnchorTextPoint lineAnchorTextPoint() const
Returns the line anchor text point, which dictates which part of the label text should be placed at t...
const GEOSPreparedGeometry * permissibleZonePrepared() const
Returns a GEOS prepared geometry representing the label's permissibleZone().
Qgis::LabelOffsetType offsetType() const
Returns the offset type, which determines how offsets and distance to label behaves.
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...
AnchorTextPoint
Anchor point of label text.
@ EndOfText
Anchor using end of text.
@ StartOfText
Anchor using start of text.
@ CenterOfText
Anchor using center of text.
@ FollowPlacement
Automatically set the anchor point based on the lineAnchorPercent() value. Values <25% will use the s...
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).
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.
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::unique_ptr< LabelPosition > curvedPlacementAtOffset(PointSet *mapShape, const std::vector< double > &pathDistances, QgsTextRendererUtils::LabelLineDirection direction, double distance, bool &labeledLineSegmentIsRightToLeft, bool applyAngleConstraints, bool uprightOnly)
Returns the label position for a curved label at a specific offset along a path.
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 addSizePenalty(std::vector< std::unique_ptr< LabelPosition > > &lPos, double bbx[4], double bby[4]) const
Increases the cost of the label candidates for this feature, based on the size of the feature.
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.
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 ...
Qgis::LabelPlacement arrangement() const
Returns the layer's arrangement policy.
std::size_t maximumPointLabelCandidates() const
Returns the maximum number of point label candidates to generate for features in this layer.
Qgis::UpsideDownLabelHandling upsidedownLabels() const
Returns how upside down labels are handled within the layer.
bool centroidInside() const
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
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 getPointByDistance(double *d, double *ad, double dl, double *px, double *py) const
Gets a point a set distance along a line geometry.
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 invalidateGeos() const
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, Qgis::LabelPredefinedPointPosition 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.