62 for (
int i = 0; i <
mHoles.count(); i++ )
72 , mTotalRepeats( other.mTotalRepeats )
73 , mCachedMaxLineCandidates( other.mCachedMaxLineCandidates )
74 , mCachedMaxPolygonCandidates( other.mCachedMaxPolygonCandidates )
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#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
140 GEOSCoordSeq_copyToArrays_r( geosctxt, coordSeq,
x.data(),
y.data(),
nullptr,
nullptr );
141 auto xminmax = std::minmax_element(
x.begin(),
x.end() );
142 xmin = *xminmax.first;
143 xmax = *xminmax.second;
144 auto yminmax = std::minmax_element(
y.begin(),
y.end() );
145 ymin = *yminmax.first;
146 ymax = *yminmax.second;
148 for (
int i = 0; i <
nbPoints; ++i )
150 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
178 if ( mCachedMaxLineCandidates > 0 )
179 return mCachedMaxLineCandidates;
181 const double l =
length();
186 if ( maxForLayer == 0 )
187 mCachedMaxLineCandidates = candidatesForLineLength;
189 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
193 mCachedMaxLineCandidates = 1;
195 return mCachedMaxLineCandidates;
200 if ( mCachedMaxPolygonCandidates > 0 )
201 return mCachedMaxPolygonCandidates;
203 const double a =
area();
208 if ( maxForLayer == 0 )
209 mCachedMaxPolygonCandidates = candidatesForArea;
211 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
215 mCachedMaxPolygonCandidates = 1;
217 return mCachedMaxPolygonCandidates;
239 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
241 if ( quadOffsetX < 0 )
243 if ( quadOffsetY < 0 )
247 else if ( quadOffsetY > 0 )
256 else if ( quadOffsetX > 0 )
258 if ( quadOffsetY < 0 )
262 else if ( quadOffsetY > 0 )
273 if ( quadOffsetY < 0 )
277 else if ( quadOffsetY > 0 )
290 return mTotalRepeats;
304 double cost = 0.00005;
305 int id = lPos.size();
307 double xdiff = -labelW / 2.0;
308 double ydiff = -labelH / 2.0;
312 double lx =
x + xdiff;
313 double ly =
y + ydiff;
333 double cost = 0.0001;
334 int id = lPos.size();
336 double xdiff = -labelW / 2.0;
337 double ydiff = -labelH / 2.0;
354 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
355 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
391 double lx =
x + xdiff;
392 double ly =
y + ydiff;
412 geos::unique_ptr pointGeom( GEOSPointOnSurface_r( geosctxt, mapShape->
geos() ) );
415 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
416 unsigned int nPoints = 0;
417 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
420 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
423 catch ( GEOSException &e )
425 qWarning(
"GEOS exception: %s", e.what() );
433void createCandidateAtOrderedPositionOverPoint(
double &labelX,
double &labelY,
Qgis::LabelQuadrantPosition &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;
529 deltaX = -labelWidth / 2.0;
530 deltaY = -labelHeight / 2.0;
536 QTransform transformRotation;
537 transformRotation.rotate( angle * 180 / M_PI );
538 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
541 double referenceX = std::cos( alpha ) * distanceToLabel + x;
542 double referenceY = std::sin( alpha ) * distanceToLabel + y;
544 labelX = referenceX + deltaX;
545 labelY = referenceY + deltaY;
558 double symbolWidthOffset{ 0 };
559 double symbolHeightOffset{ 0 };
567 symbolWidthOffset = (
mLF->
symbolSize().width() - geom.boundingBox().width() ) / 2.0;
568 symbolHeightOffset = (
mLF->
symbolSize().height() - geom.boundingBox().height() ) / 2.0;
577 int candidatesPerPosition = 1;
578 double distanceStep = 0;
579 if ( maximumDistanceToLabel > distanceToLabel && !
qgsDoubleNear( maximumDistanceToLabel, 0 ) )
583 const double rayLength = maximumDistanceToLabel - distanceToLabel;
587 distanceStep = rayLength / ( candidatesPerPosition - 1 );
590 double cost = 0.0001;
591 std::size_t i = lPos.size();
595 std::size_t created = 0;
597 auto addCandidate = [
this,
x,
y, labelWidth, labelHeight, angle, visualMargin, symbolWidthOffset, symbolHeightOffset, &created, &cost, &lPos, &i, maxNumberCandidates](
Qgis::LabelPredefinedPointPosition position,
double distance ) ->
bool
603 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distance, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
612 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
618 switch ( prioritization )
626 double currentDistance = distanceToLabel;
627 for (
int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
629 if ( !addCandidate( position, currentDistance ) )
638 double currentDistance = distanceToLabel;
639 for (
int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
643 if ( !addCandidate( position, currentDistance ) )
662 QTransform transformRotation;
663 transformRotation.rotate( angle * 180 / M_PI );
669 int candidatesPerRay = 0;
670 double rayStepDelta = 0;
671 if ( maximumDistanceToLabel > distanceToLabel && !
qgsDoubleNear( maximumDistanceToLabel, 0 ) )
675 const double rayLength = maximumDistanceToLabel - distanceToLabel;
679 rayStepDelta = rayLength / ( candidatesPerRay - 1 );
683 candidatesPerRay = 1;
686 int id =
static_cast< int >( lPos.size() );
688 const double candidateAngleIncrement = 2 * M_PI /
static_cast< double >( rayCount );
691 constexpr double a90 = M_PI_2;
692 constexpr double a180 = M_PI;
693 constexpr double a270 = a180 + a90;
694 constexpr double a360 = 2 * M_PI;
696 double gamma1, gamma2;
698 if ( distanceToLabel > 0 )
700 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
701 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
705 gamma1 = gamma2 = a90 / 3.0;
708 if ( gamma1 > a90 / 3.0 )
711 if ( gamma2 > a90 / 3.0 )
714 std::size_t numberCandidatesGenerated = 0;
716 double angleToCandidate = M_PI_4;
718 int integerRayCost = 0;
719 int integerRayCostIncrement = 2;
721 for (
int rayIndex = 0; rayIndex < rayCount; ++rayIndex, angleToCandidate += candidateAngleIncrement )
726 if ( angleToCandidate > a360 )
727 angleToCandidate -= a360;
729 double rayDistance = distanceToLabel;
731 constexpr double RAY_ANGLE_COST_FACTOR = 0.0020;
735 const double scaledRayAngleCost = RAY_ANGLE_COST_FACTOR *
static_cast< double >( integerRayCost )
736 /
static_cast< double >( rayCount - 1 );
738 for (
int j = 0; j < candidatesPerRay; ++j, rayDistance += rayStepDelta )
742 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
744 deltaX = rayDistance;
745 double iota = ( angleToCandidate + gamma1 );
746 if ( iota > a360 - gamma1 )
749 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
753 else if ( angleToCandidate < a90 - gamma2 )
755 deltaX = rayDistance * std::cos( angleToCandidate );
756 deltaY = rayDistance * std::sin( angleToCandidate );
759 else if ( angleToCandidate < a90 + gamma2 )
761 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
762 deltaY = rayDistance;
765 else if ( angleToCandidate < a180 - gamma1 )
767 deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
768 deltaY = rayDistance * std::sin( angleToCandidate );
771 else if ( angleToCandidate < a180 + gamma1 )
773 deltaX = -rayDistance - labelWidth;
774 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
777 else if ( angleToCandidate < a270 - gamma2 )
779 deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
780 deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
783 else if ( angleToCandidate < a270 + gamma2 )
785 deltaY = -rayDistance - labelHeight;
786 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
789 else if ( angleToCandidate < a360 )
791 deltaX = rayDistance * std::cos( angleToCandidate );
792 deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
796 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
798 double labelX =
x + deltaX;
799 double labelY =
y + deltaY;
806 cost = 0.0001 + scaledRayAngleCost;
813 cost += j * RAY_ANGLE_COST_FACTOR + RAY_ANGLE_COST_FACTOR / rayCount;
826 numberCandidatesGenerated++;
829 integerRayCost += integerRayCostIncrement;
831 if ( integerRayCost ==
static_cast< int >( rayCount ) )
833 integerRayCost =
static_cast< int >( rayCount ) - 1;
834 integerRayCostIncrement = -2;
836 else if ( integerRayCost >
static_cast< int >( rayCount ) )
838 integerRayCost =
static_cast< int >( rayCount ) - 2;
839 integerRayCostIncrement = -2;
843 return numberCandidatesGenerated;
850 double shapeLength = mapShape->
length();
861 std::size_t candidates = 0;
867 if ( candidates < candidateTargetCount )
882 std::vector< double > &
x = line->
x;
883 std::vector< double > &
y = line->
y;
885 std::vector< double > segmentLengths(
nbPoints - 1 );
886 std::vector< double >distanceToSegment(
nbPoints );
888 double totalLineLength = 0.0;
889 for (
int i = 0; i < line->
nbPoints - 1; i++ )
892 distanceToSegment[i] = 0;
894 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
897 totalLineLength += segmentLengths[i];
899 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
902 double lineStepDistance = 0;
905 double currentDistanceAlongLine = lineStepDistance;
909 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
913 currentDistanceAlongLine = lineAnchorPoint;
914 lineStepDistance = -1;
920 double candidateCenterX, candidateCenterY;
922 while ( currentDistanceAlongLine <= totalLineLength )
924 if (
pal->isCanceled() )
929 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
932 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
939 labelX = candidateCenterX;
942 labelX = candidateCenterX - labelWidth / 2;
945 labelX = candidateCenterX - labelWidth;
953 currentDistanceAlongLine += lineStepDistance;
957 if ( lineStepDistance < 0 )
974 QVector< int > extremeAngleNodes;
977 std::vector< double > &
x = line->
x;
978 std::vector< double > &
y = line->
y;
982 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
984 double x1 =
x[i - 1];
986 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
987 double y1 =
y[i - 1];
989 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
994 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
998 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
999 extremeAngleNodes << i;
1001 extremeAngleNodes << numberNodes - 1;
1003 if ( extremeAngleNodes.isEmpty() )
1010 std::vector< double > segmentLengths( numberNodes - 1 );
1011 std::vector< double > distanceToSegment( numberNodes );
1012 double totalLineLength = 0.0;
1013 QVector< double > straightSegmentLengths;
1014 QVector< double > straightSegmentAngles;
1015 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
1016 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
1017 double currentStraightSegmentLength = 0;
1018 double longestSegmentLength = 0;
1019 double segmentStartX =
x[0];
1020 double segmentStartY =
y[0];
1021 for (
int i = 0; i < numberNodes - 1; i++ )
1024 distanceToSegment[i] = 0;
1026 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1029 totalLineLength += segmentLengths[i];
1030 if ( extremeAngleNodes.contains( i ) )
1033 straightSegmentLengths << currentStraightSegmentLength;
1035 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
1036 currentStraightSegmentLength = 0;
1037 segmentStartX =
x[i];
1038 segmentStartY =
y[i];
1040 currentStraightSegmentLength += segmentLengths[i];
1042 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1043 straightSegmentLengths << currentStraightSegmentLength;
1045 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
1048 if ( totalLineLength < labelWidth )
1056 double lineStepDistance = ( totalLineLength - labelWidth );
1057 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1059 double distanceToEndOfSegment = 0.0;
1060 int lastNodeInSegment = 0;
1062 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
1064 currentStraightSegmentLength = straightSegmentLengths.at( i );
1065 double currentSegmentAngle = straightSegmentAngles.at( i );
1066 lastNodeInSegment = extremeAngleNodes.at( i );
1067 double distanceToStartOfSegment = distanceToEndOfSegment;
1068 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
1069 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
1071 if ( currentStraightSegmentLength < labelWidth )
1075 double currentDistanceAlongLine = distanceToStartOfSegment;
1076 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1077 double candidateLength = 0.0;
1083 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
1084 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
1086 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
1088 if (
pal->isCanceled() )
1094 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1095 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1103 cost = candidateLength / labelWidth;
1109 cost = ( 1 - cost ) / 100;
1112 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1113 double labelTextAnchor = 0;
1114 switch ( textPoint )
1117 labelTextAnchor = currentDistanceAlongLine;
1120 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1123 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1132 if ( placementIsFlexible )
1135 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1136 cost += costCenter * 0.0005;
1144 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1145 cost += costLineCenter * 0.0005;
1148 if ( placementIsFlexible )
1150 cost += segmentCost * 0.0005;
1151 cost += segmentAngleCost * 0.0001;
1159 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1163 beta = angle + M_PI_2;
1168 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1178 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1186 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1194 const double candidateCost = cost + 0.002;
1208 currentDistanceAlongLine += lineStepDistance;
1231 std::vector< double > &
x = line->
x;
1232 std::vector< double > &
y = line->
y;
1234 std::vector< double > segmentLengths(
nbPoints - 1 );
1235 std::vector< double >distanceToSegment(
nbPoints );
1237 double totalLineLength = 0.0;
1238 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1241 distanceToSegment[i] = 0;
1243 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1246 totalLineLength += segmentLengths[i];
1248 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1250 double lineStepDistance = ( totalLineLength - labelWidth );
1251 double currentDistanceAlongLine = 0;
1257 if ( totalLineLength > labelWidth )
1259 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1263 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1264 lineStepDistance = -1;
1265 totalLineLength = labelWidth;
1270 currentDistanceAlongLine = std::numeric_limits< double >::max();
1281 switch ( textPoint )
1284 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1287 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1290 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1296 lineStepDistance = -1;
1300 double candidateLength;
1302 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1306 if (
pal->isCanceled() )
1312 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1313 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1315 if ( currentDistanceAlongLine < 0 )
1325 cost = candidateLength / labelWidth;
1331 cost = ( 1 - cost ) / 100;
1335 double textAnchorPoint = 0;
1336 switch ( textPoint )
1339 textAnchorPoint = currentDistanceAlongLine;
1342 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1345 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1351 double costCenter = std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength;
1352 cost += costCenter / 1000;
1353 cost += initialCost;
1360 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1364 beta = angle + M_PI_2;
1369 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1379 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1387 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1395 const double candidateCost = cost + 0.002;
1409 currentDistanceAlongLine += lineStepDistance;
1413 if ( lineStepDistance < 0 )
1423 Q_ASSERT( metrics );
1425 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1426 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1428 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1434 if ( placement->graphemePlacement.empty() )
1437 auto it = placement->graphemePlacement.constBegin();
1439 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1440 firstPosition->setPartId( it->graphemeIndex );
1443 while ( it != placement->graphemePlacement.constEnd() )
1446 position->setPartId( it->graphemeIndex );
1449 previousPosition->
setNextPart( std::move( position ) );
1450 previousPosition = nextPosition;
1454 return firstPosition;
1466 const int characterCount = li->
count();
1467 if ( characterCount == 0 )
1473 double totalCharacterWidth = 0;
1474 for (
int i = 0; i < characterCount; ++i )
1477 std::unique_ptr< PointSet > expanded;
1478 double shapeLength = mapShape->
length();
1481 allowOverrun =
false;
1499 if ( totalCharacterWidth > shapeLength )
1501 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1512 if ( allowOverrun && overrun > 0 )
1515 expanded = mapShape->
clone();
1517 mapShape = expanded.get();
1518 shapeLength += 2 * overrun;
1526 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1527 bool positiveShapeHasNegativeDistance =
false;
1528 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1529 bool negativeShapeHasNegativeDistance =
false;
1530 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1534 mapShapeOffsetPositive = mapShape->
clone();
1536 mapShapeOffsetNegative = mapShape->
clone();
1539 if ( mapShapeOffsetPositive )
1540 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1541 positiveShapeHasNegativeDistance = offsetDistance < 0;
1542 if ( mapShapeOffsetNegative )
1543 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1544 negativeShapeHasNegativeDistance = offsetDistance > 0;
1561 if ( mapShapeOffsetPositive )
1562 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1563 positiveShapeHasNegativeDistance = offsetDistance > 0;
1564 if ( mapShapeOffsetNegative )
1565 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1566 negativeShapeHasNegativeDistance = offsetDistance < 0;
1572 std::vector< std::unique_ptr< LabelPosition >> positions;
1573 std::unique_ptr< LabelPosition > backupPlacement;
1576 PointSet *currentMapShape =
nullptr;
1579 currentMapShape = mapShapeOffsetPositive.get();
1583 currentMapShape = mapShape;
1587 currentMapShape = mapShapeOffsetNegative.get();
1589 if ( !currentMapShape )
1593 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1597 double lineAnchorPoint = 0;
1598 if ( originalPoint && offset !=
NoOffset )
1603 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1609 lineAnchorPoint = totalDistance - lineAnchorPoint;
1612 if (
pal->isCanceled() )
1616 double delta = std::max( li->
characterHeight( 0 ) / 6, totalDistance / candidateTargetCount );
1619 double distanceAlongLineToStartCandidate = 0;
1620 bool singleCandidateOnly =
false;
1627 switch ( textPoint )
1630 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1633 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1636 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1642 singleCandidateOnly =
true;
1646 bool hasTestedFirstPlacement =
false;
1647 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1649 if ( singleCandidateOnly && hasTestedFirstPlacement )
1652 if (
pal->isCanceled() )
1655 hasTestedFirstPlacement =
true;
1657 bool labeledLineSegmentIsRightToLeft =
false;
1663 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly, curvedTextFlags );
1664 if ( !labelPosition )
1670 bool isBackupPlacementOnly =
false;
1673 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1674 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1676 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1682 isBackupPlacementOnly =
true;
1689 isBackupPlacementOnly =
true;
1695 backupPlacement.reset();
1698 const double angleDiff = labelPosition->angleDifferential();
1699 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1704 double cost = angleDiffAvg / 100;
1705 if ( cost < 0.0001 )
1709 double labelTextAnchor = 0;
1710 switch ( textPoint )
1713 labelTextAnchor = distanceAlongLineToStartCandidate;
1716 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1719 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1725 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1726 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1728 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1740 labelPosition->setCost( cost );
1742 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1747 while ( within && currentPos )
1750 currentPos = currentPos->
nextPart();
1760 if ( isBackupPlacementOnly )
1761 backupPlacement = std::move( p );
1763 positions.emplace_back( std::move( p ) );
1768 for ( std::unique_ptr< LabelPosition > &pos : positions )
1770 lPos.emplace_back( std::move( pos ) );
1773 if ( backupPlacement )
1774 lPos.emplace_back( std::move( backupPlacement ) );
1776 return positions.size();
1800 const double totalArea =
area();
1802 mapShape->
parent =
nullptr;
1804 if (
pal->isCanceled() )
1807 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1809 QgsDebugMsgLevel( QStringLiteral(
"PAL split polygons resulted in:" ), 2 );
1810 for (
PointSet *ps : shapes_final )
1816 std::size_t nbp = 0;
1818 if ( !shapes_final.isEmpty() )
1826 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1828 std::vector< OrientedConvexHullBoundingBox > boxes;
1829 boxes.reserve( shapes_final.size() );
1832 while ( !shapes_final.isEmpty() )
1834 PointSet *shape = shapes_final.takeFirst();
1838 boxes.emplace_back( box );
1844 if (
pal->isCanceled() )
1848 double densityY = densityX;
1855 std::size_t numberCandidatesGenerated = 0;
1870 double dx = densityX;
1871 double dy = densityY;
1872 if ( numTry == 0 && maxPolygonCandidates > 0 )
1875 const double boxArea = box.width * box.length;
1876 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1877 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1881 if (
pal->isCanceled() )
1882 return numberCandidatesGenerated;
1901 bool enoughPlace =
false;
1905 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1906 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1912 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1914 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1918 enoughPlace =
false;
1934 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1936 if ( box.alpha <= M_PI_4 )
1942 alpha = box.alpha - M_PI_2;
1945 else if ( box.length > box.width )
1947 alpha = box.alpha - M_PI_2;
1954 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1960 dlx = std::cos( beta ) * diago;
1961 dly = std::sin( beta ) * diago;
1963 double px0 = box.width / 2.0;
1964 double py0 = box.length / 2.0;
1966 px0 -= std::ceil( px0 / dx ) * dx;
1967 py0 -= std::ceil( py0 / dy ) * dy;
1969 for ( px = px0; px <= box.width; px += dx )
1971 if (
pal->isCanceled() )
1974 for ( py = py0; py <= box.length; py += dy )
1977 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1978 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1989 numberCandidatesGenerated++;
2002 lPos.emplace_back( std::move( potentialCandidate ) );
2003 numberCandidatesGenerated++;
2010 nbp = numberCandidatesGenerated;
2011 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
2022 while ( numTry < maxTry );
2024 nbp = numberCandidatesGenerated;
2038 std::size_t candidatesCreated = 0;
2093 geos::unique_ptr buffer( GEOSBuffer_r( ctxt,
geos(), distanceToLabel * 0.5, 1 ) );
2096 geos::prepared_unique_ptr preparedBuffer( GEOSPrepare_r( ctxt, buffer.get() ) );
2098 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
2100 return candidatesCreated;
2104 return candidatesCreated;
2108 const double ringLength = ring->
length();
2109 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2111 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2114 const double delta = ringLength / targetPolygonCandidates;
2115 geos::unique_ptr geosPoint;
2117 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2118 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2119 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2122 const double labelAngle = 0;
2124 std::size_t i = lPos.size();
2132 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2135 if ( candidate->intersects( preparedBuffer.get() ) )
2153 const double centroidDistance = candidate->getDistanceToPoint( cx, cy,
false );
2154 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2155 candidate->setCost( centroidCost );
2157 lPos.emplace_back( std::move( candidate ) );
2158 candidatesCreated++;
2163 double startSegmentX,
double startSegmentY,
double,
double,
2164 double endSegmentX,
double endSegmentY,
double,
double )
2167 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2172 if ( angle >= 0 && angle <= 5 )
2177 else if ( angle <= 85 )
2181 else if ( angle <= 90 )
2187 else if ( angle <= 95 )
2192 else if ( angle <= 175 )
2196 else if ( angle <= 180 )
2202 else if ( angle <= 185 )
2207 else if ( angle <= 265 )
2211 else if ( angle <= 270 )
2216 else if ( angle <= 275 )
2221 else if ( angle <= 355 )
2231 return !
pal->isCanceled();
2234 return candidatesCreated;
2239 std::vector< std::unique_ptr< LabelPosition > > lPos;
2259 case GEOS_LINESTRING:
2282 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2283 std::fabs(
ymax -
ymin ) < labelHeight ) )
2290 std::size_t created = 0;
2348 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2350 double sizeCost = 0;
2351 if ( geomType == GEOS_LINESTRING )
2353 const double l =
length();
2356 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2357 if ( l >= bbox_length / 4 )
2360 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2362 else if ( geomType == GEOS_POLYGON )
2364 const double a =
area();
2367 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2368 if ( a >= bbox_area / 16 )
2371 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2377 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2379 pos->setCost( pos->cost() + sizeCost / 100 );
2390 const double x1first =
x.front();
2391 const double x1last =
x.back();
2392 const double x2first = p2->
x.front();
2393 const double x2last = p2->
x.back();
2394 const double y1first =
y.front();
2395 const double y1last =
y.back();
2396 const double y2first = p2->
y.front();
2397 const double y2last = p2->
y.back();
2405 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2411 const double p2otherX = p2startTouches ? x2last : x2first;
2412 const double p2otherY = p2startTouches ? y2last : y2first;
2418#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
2419 return ( GEOSPreparedIntersectsXY_r( geosctxt,
preparedGeom(), p2otherX, p2otherY ) != 1 );
2421 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2422 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2423 geos::unique_ptr p2OtherEnd( GEOSGeom_createPoint_r( geosctxt, coord ) );
2424 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2427 catch ( GEOSException &e )
2429 qWarning(
"GEOS exception: %s", e.what() );
2439 if ( !other->
mGeos )
2448 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2449 geos::unique_ptr gTmp( GEOSLineMerge_r( ctxt, g.get() ) );
2451 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2459 mGeos = gTmp.release();
2468 catch ( GEOSException &e )
2470 qWarning(
"GEOS exception: %s", e.what() );
2491 bool result =
false;
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
LabelPrioritization
Label prioritization.
@ PreferCloser
Prefer closer labels, falling back to alternate positions before larger distances.
@ PreferPositionOrdering
Prefer labels follow position ordering, falling back to more distance labels before alternate positio...
@ 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.
@ AllowPlacementInsideOfPolygon
Labels can be placed inside a polygon feature.
@ AllowPlacementOutsideOfPolygon
Labels can be placed outside of a polygon feature.
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
LabelQuadrantPosition
Label quadrant positions.
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
@ OverPoint
Label directly centered over point.
@ 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.
@ FlipUpsideDownLabels
Upside-down labels (90 <= angle < 270) are shown upright.
@ AlwaysAllowUpsideDown
Show upside down for all labels, including dynamic ones.
@ AllowUpsideDownWhenRotationIsDefined
Show upside down when rotation is layer- or data-defined.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool hasNext() const
Find out whether there are more parts.
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
A geometry is the spatial representation of a feature.
QgsGeometryConstPartIterator constParts() const
Returns Java-style iterator for traversal of parts of the geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
double maximumDistance() const
Returns the maximum distance which labels are allowed to be from their corresponding points.
double overrunSmoothDistance() const
Returns the distance (in map units) with which the ends of linear features are averaged over when cal...
Qgis::LabelPolygonPlacementFlags polygonPlacementFlags() const
Returns the polygon placement flags, which dictate how polygon labels can be placed.
double fixedAngle() const
Angle in radians 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.
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.
pal::Layer * layer() const
Gets PAL layer of the label feature. Should be only used internally in PAL.
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 GEOSPreparedGeometry * permissibleZonePrepared() const
Returns a GEOS prepared geometry representing the label's permissibleZone().
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.
GEOSGeometry * geometry() const
Gets access to the associated geometry.
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
Qgis::LabelPrioritization prioritization() const
Returns the label prioritization technique.
void setAnchorPosition(const QgsPointXY &anchorPosition)
In case of quadrand or aligned positioning, this is set to the anchor point.
QgsFeature feature() const
Returns the original feature associated with this label.
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 QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
Qgis::LabelLinePlacementFlags arrangementFlags() const
Returns the feature's arrangement flags.
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...
Line string geometry type, with support for z-dimension and m-values.
double length() const override
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.
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 characterHeight(int position) const
Returns the character height of the character at the specified position (actually font metrics height...
double width() const
Returns the width of the rectangle.
double height() const
Returns the height 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 ...
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, CurvedTextFlags flags=CurvedTextFlags())
Calculates curved text placement properties.
Main class to handle feature.
FeaturePart(QgsLabelFeature *lf, const GEOSGeometry *geom)
Creates a new generic 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 (in radians) 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::unique_ptr< LabelPosition > curvedPlacementAtOffset(PointSet *mapShape, const std::vector< double > &pathDistances, QgsTextRendererUtils::LabelLineDirection direction, double distance, bool &labeledLineSegmentIsRightToLeft, bool applyAngleConstraints, QgsTextRendererUtils::CurvedTextFlags flags)
Returns the label position for a curved label at a specific offset along a path.
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 (in radians) into account.
QgsLabelFeature * feature()
Returns the parent feature.
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.
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 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 radians).
void setNextPart(std::unique_ptr< LabelPosition > next)
Sets 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.
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
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.
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
OrientedConvexHullBoundingBox computeConvexHullOrientedBoundingBox(bool &ok) const
Computes an oriented bounding box for the shape's convex hull.
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, Qgis::LabelQuadrantPosition &quadrant, double x, double y, double labelWidth, double labelHeight, Qgis::LabelPredefinedPointPosition position, double distanceToLabel, const QgsMargins &visualMargin, double symbolWidthOffset, double symbolHeightOffset, double angle)
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
#define QgsDebugMsgLevel(str, level)
Represents the minimum area, oriented bounding box surrounding a convex hull.
struct GEOSGeom_t GEOSGeometry