63 for (
int i = 0; i <
mHoles.count(); i++ )
73 , mTotalRepeats( other.mTotalRepeats )
74 , mCachedMaxLineCandidates( other.mCachedMaxLineCandidates )
75 , mCachedMaxPolygonCandidates( other.mCachedMaxPolygonCandidates )
80 mHoles.last()->holeOf =
this;
94 const GEOSCoordSequence *coordSeq =
nullptr;
97 type = GEOSGeomTypeId_r( geosctxt, geom );
99 if (
type == GEOS_POLYGON )
101 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
103 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
105 for (
int i = 0; i < numHoles; ++i )
107 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
119 geom = GEOSGetExteriorRing_r( geosctxt, geom );
128 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
129 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
132 xmin =
ymin = std::numeric_limits<double>::max();
133 xmax =
ymax = std::numeric_limits<double>::lowest();
140#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
141 GEOSCoordSeq_copyToArrays_r( geosctxt, coordSeq,
x.data(),
y.data(),
nullptr,
nullptr );
142 auto xminmax = std::minmax_element(
x.begin(),
x.end() );
143 xmin = *xminmax.first;
144 xmax = *xminmax.second;
145 auto yminmax = std::minmax_element(
y.begin(),
y.end() );
146 ymin = *yminmax.first;
147 ymax = *yminmax.second;
149 for (
int i = 0; i <
nbPoints; ++i )
151 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
179 if ( mCachedMaxLineCandidates > 0 )
180 return mCachedMaxLineCandidates;
182 const double l =
length();
187 if ( maxForLayer == 0 )
188 mCachedMaxLineCandidates = candidatesForLineLength;
190 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
194 mCachedMaxLineCandidates = 1;
196 return mCachedMaxLineCandidates;
201 if ( mCachedMaxPolygonCandidates > 0 )
202 return mCachedMaxPolygonCandidates;
204 const double a =
area();
209 if ( maxForLayer == 0 )
210 mCachedMaxPolygonCandidates = candidatesForArea;
212 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
216 mCachedMaxPolygonCandidates = 1;
218 return mCachedMaxPolygonCandidates;
240 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
242 if ( quadOffsetX < 0 )
244 if ( quadOffsetY < 0 )
248 else if ( quadOffsetY > 0 )
257 else if ( quadOffsetX > 0 )
259 if ( quadOffsetY < 0 )
263 else if ( quadOffsetY > 0 )
274 if ( quadOffsetY < 0 )
278 else if ( quadOffsetY > 0 )
291 return mTotalRepeats;
305 double cost = 0.00005;
306 int id = lPos.size();
308 double xdiff = -labelW / 2.0;
309 double ydiff = -labelH / 2.0;
313 double lx =
x + xdiff;
314 double ly =
y + ydiff;
324 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false,
LabelPosition::QuadrantOver ) );
334 double cost = 0.0001;
335 int id = lPos.size();
337 double xdiff = -labelW / 2.0;
338 double ydiff = -labelH / 2.0;
355 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
356 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
392 double lx =
x + xdiff;
393 double ly =
y + ydiff;
403 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false, quadrantFromOffset() ) );
416 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
417 unsigned int nPoints = 0;
418 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
421 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
424 catch ( GEOSException &e )
426 qWarning(
"GEOS exception: %s", e.what() );
434void 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 )
445 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
446 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
452 deltaX = -labelWidth / 4.0 - visualMargin.
left();
453 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
459 deltaX = -labelWidth / 2.0;
460 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
466 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
467 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
473 deltaX = - visualMargin.
left() + symbolWidthOffset;
474 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
480 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
481 deltaY = -labelHeight / 2.0;
487 deltaX = -visualMargin.
left() + symbolWidthOffset;
488 deltaY = -labelHeight / 2.0;
494 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
495 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
501 deltaX = -labelWidth / 4.0 - visualMargin.
left();
502 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
508 deltaX = -labelWidth / 2.0;
509 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
515 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
516 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
522 deltaX = -visualMargin.
left() + symbolWidthOffset;
523 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
529 QTransform transformRotation;
530 transformRotation.rotate( angle * 180 / M_PI );
531 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
534 double referenceX = std::cos( alpha ) * distanceToLabel + x;
535 double referenceY = std::sin( alpha ) * distanceToLabel + y;
537 labelX = referenceX + deltaX;
538 labelY = referenceY + deltaY;
549 double symbolWidthOffset{ 0 };
550 double symbolHeightOffset{ 0 };
558 symbolWidthOffset = (
mLF->
symbolSize().width() - geom.boundingBox().width() ) / 2.0;
559 symbolHeightOffset = (
mLF->
symbolSize().height() - geom.boundingBox().height() ) / 2.0;
568 double cost = 0.0001;
569 std::size_t i = lPos.size();
572 std::size_t created = 0;
579 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
583 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
587 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
603 if ( maxNumberCandidates == 0 )
604 maxNumberCandidates = 16;
608 int id = lPos.size();
610 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
615 double a270 = a180 + a90;
616 double a360 = 2 * M_PI;
618 double gamma1, gamma2;
620 if ( distanceToLabel > 0 )
622 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
623 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
627 gamma1 = gamma2 = a90 / 3.0;
630 if ( gamma1 > a90 / 3.0 )
633 if ( gamma2 > a90 / 3.0 )
636 std::size_t numberCandidatesGenerated = 0;
639 double angleToCandidate;
640 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
645 if ( angleToCandidate > a360 )
646 angleToCandidate -= a360;
650 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
652 deltaX = distanceToLabel;
653 double iota = ( angleToCandidate + gamma1 );
654 if ( iota > a360 - gamma1 )
658 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
662 else if ( angleToCandidate < a90 - gamma2 )
664 deltaX = distanceToLabel * std::cos( angleToCandidate );
665 deltaY = distanceToLabel * std::sin( angleToCandidate );
668 else if ( angleToCandidate < a90 + gamma2 )
671 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
672 deltaY = distanceToLabel;
675 else if ( angleToCandidate < a180 - gamma1 )
677 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
678 deltaY = distanceToLabel * std::sin( angleToCandidate );
681 else if ( angleToCandidate < a180 + gamma1 )
683 deltaX = -distanceToLabel - labelWidth;
685 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
688 else if ( angleToCandidate < a270 - gamma2 )
690 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
691 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
694 else if ( angleToCandidate < a270 + gamma2 )
696 deltaY = -distanceToLabel - labelHeight;
698 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
701 else if ( angleToCandidate < a360 )
703 deltaX = distanceToLabel * std::cos( angleToCandidate );
704 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
710 QTransform transformRotation;
711 transformRotation.rotate( angle * 180 / M_PI );
712 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
714 double labelX =
x + deltaX;
715 double labelY =
y + deltaY;
719 if ( maxNumberCandidates == 1 )
722 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
733 lPos.emplace_back( std::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
734 numberCandidatesGenerated++;
738 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
740 icost =
static_cast< int >( maxNumberCandidates ) - 1;
743 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
745 icost =
static_cast< int >( maxNumberCandidates ) - 2;
751 return numberCandidatesGenerated;
758 double shapeLength = mapShape->
length();
769 std::size_t candidates = 0;
775 if ( candidates < candidateTargetCount )
790 std::vector< double > &
x = line->
x;
791 std::vector< double > &
y = line->
y;
793 std::vector< double > segmentLengths(
nbPoints - 1 );
794 std::vector< double >distanceToSegment(
nbPoints );
796 double totalLineLength = 0.0;
797 for (
int i = 0; i < line->
nbPoints - 1; i++ )
800 distanceToSegment[i] = 0;
802 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
805 totalLineLength += segmentLengths[i];
807 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
810 double lineStepDistance = 0;
813 double currentDistanceAlongLine = lineStepDistance;
817 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
821 currentDistanceAlongLine = lineAnchorPoint;
822 lineStepDistance = -1;
828 double candidateCenterX, candidateCenterY;
830 while ( currentDistanceAlongLine <= totalLineLength )
832 if (
pal->isCanceled() )
837 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
840 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
847 labelX = candidateCenterX;
850 labelX = candidateCenterX - labelWidth / 2;
853 labelX = candidateCenterX - labelWidth;
859 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
861 currentDistanceAlongLine += lineStepDistance;
865 if ( lineStepDistance < 0 )
882 QVector< int > extremeAngleNodes;
885 std::vector< double > &
x = line->
x;
886 std::vector< double > &
y = line->
y;
890 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
892 double x1 =
x[i - 1];
894 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
895 double y1 =
y[i - 1];
897 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
902 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
906 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
907 extremeAngleNodes << i;
909 extremeAngleNodes << numberNodes - 1;
911 if ( extremeAngleNodes.isEmpty() )
918 std::vector< double > segmentLengths( numberNodes - 1 );
919 std::vector< double > distanceToSegment( numberNodes );
920 double totalLineLength = 0.0;
921 QVector< double > straightSegmentLengths;
922 QVector< double > straightSegmentAngles;
923 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
924 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
925 double currentStraightSegmentLength = 0;
926 double longestSegmentLength = 0;
927 double segmentStartX =
x[0];
928 double segmentStartY =
y[0];
929 for (
int i = 0; i < numberNodes - 1; i++ )
932 distanceToSegment[i] = 0;
934 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
937 totalLineLength += segmentLengths[i];
938 if ( extremeAngleNodes.contains( i ) )
941 straightSegmentLengths << currentStraightSegmentLength;
943 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
944 currentStraightSegmentLength = 0;
945 segmentStartX =
x[i];
946 segmentStartY =
y[i];
948 currentStraightSegmentLength += segmentLengths[i];
950 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
951 straightSegmentLengths << currentStraightSegmentLength;
953 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
956 if ( totalLineLength < labelWidth )
964 double lineStepDistance = ( totalLineLength - labelWidth );
965 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
967 double distanceToEndOfSegment = 0.0;
968 int lastNodeInSegment = 0;
970 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
972 currentStraightSegmentLength = straightSegmentLengths.at( i );
973 double currentSegmentAngle = straightSegmentAngles.at( i );
974 lastNodeInSegment = extremeAngleNodes.at( i );
975 double distanceToStartOfSegment = distanceToEndOfSegment;
976 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
977 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
979 if ( currentStraightSegmentLength < labelWidth )
983 double currentDistanceAlongLine = distanceToStartOfSegment;
984 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
985 double candidateLength = 0.0;
991 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
992 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
994 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
996 if (
pal->isCanceled() )
1002 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1003 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1005 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1011 cost = candidateLength / labelWidth;
1017 cost = ( 1 - cost ) / 100;
1020 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1021 double labelTextAnchor = 0;
1022 switch ( textPoint )
1025 labelTextAnchor = currentDistanceAlongLine;
1028 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1031 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1040 if ( placementIsFlexible )
1043 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1044 cost += costCenter * 0.0005;
1052 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1053 cost += costLineCenter * 0.0005;
1056 if ( placementIsFlexible )
1058 cost += segmentCost * 0.0005;
1059 cost += segmentAngleCost * 0.0001;
1067 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1071 beta = angle + M_PI_2;
1076 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1086 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1087 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 ) );
1094 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1095 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 ) );
1102 const double candidateCost = cost + 0.002;
1103 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 ) );
1109 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1116 currentDistanceAlongLine += lineStepDistance;
1139 std::vector< double > &
x = line->
x;
1140 std::vector< double > &
y = line->
y;
1142 std::vector< double > segmentLengths(
nbPoints - 1 );
1143 std::vector< double >distanceToSegment(
nbPoints );
1145 double totalLineLength = 0.0;
1146 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1149 distanceToSegment[i] = 0;
1151 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1154 totalLineLength += segmentLengths[i];
1156 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1158 double lineStepDistance = ( totalLineLength - labelWidth );
1159 double currentDistanceAlongLine = 0;
1165 if ( totalLineLength > labelWidth )
1167 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1171 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1172 lineStepDistance = -1;
1173 totalLineLength = labelWidth;
1178 currentDistanceAlongLine = std::numeric_limits< double >::max();
1189 switch ( textPoint )
1192 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1195 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1198 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1204 lineStepDistance = -1;
1208 double candidateLength;
1210 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1214 if (
pal->isCanceled() )
1220 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1221 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1223 if ( currentDistanceAlongLine < 0 )
1231 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1234 cost = candidateLength / labelWidth;
1240 cost = ( 1 - cost ) / 100;
1244 double textAnchorPoint = 0;
1245 switch ( textPoint )
1248 textAnchorPoint = currentDistanceAlongLine;
1251 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1254 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1260 double costCenter = std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength;
1261 cost += costCenter / 1000;
1262 cost += initialCost;
1269 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1273 beta = angle + M_PI_2;
1278 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1288 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1289 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 ) );
1296 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1297 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 ) );
1304 const double candidateCost = cost + 0.002;
1305 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 ) );
1311 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1318 currentDistanceAlongLine += lineStepDistance;
1322 if ( lineStepDistance < 0 )
1332 Q_ASSERT( metrics );
1334 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1335 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1337 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1343 if ( placement->graphemePlacement.empty() )
1346 auto it = placement->graphemePlacement.constBegin();
1347 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 );
1348 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1349 firstPosition->setPartId( it->graphemeIndex );
1352 while ( it != placement->graphemePlacement.constEnd() )
1354 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 );
1355 position->setPartId( it->graphemeIndex );
1358 previousPosition->
setNextPart( std::move( position ) );
1359 previousPosition = nextPosition;
1363 return firstPosition;
1375 const int characterCount = li->
count();
1376 if ( characterCount == 0 )
1382 double totalCharacterWidth = 0;
1383 for (
int i = 0; i < characterCount; ++i )
1386 std::unique_ptr< PointSet > expanded;
1387 double shapeLength = mapShape->
length();
1390 allowOverrun =
false;
1408 if ( totalCharacterWidth > shapeLength )
1410 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1421 if ( allowOverrun && overrun > 0 )
1424 expanded = mapShape->
clone();
1426 mapShape = expanded.get();
1427 shapeLength += 2 * overrun;
1435 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1436 bool positiveShapeHasNegativeDistance =
false;
1437 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1438 bool negativeShapeHasNegativeDistance =
false;
1439 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1443 mapShapeOffsetPositive = mapShape->
clone();
1445 mapShapeOffsetNegative = mapShape->
clone();
1448 if ( mapShapeOffsetPositive )
1449 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1450 positiveShapeHasNegativeDistance = offsetDistance < 0;
1451 if ( mapShapeOffsetNegative )
1452 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1453 negativeShapeHasNegativeDistance = offsetDistance > 0;
1470 if ( mapShapeOffsetPositive )
1471 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1472 positiveShapeHasNegativeDistance = offsetDistance > 0;
1473 if ( mapShapeOffsetNegative )
1474 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1475 negativeShapeHasNegativeDistance = offsetDistance < 0;
1481 std::vector< std::unique_ptr< LabelPosition >> positions;
1482 std::unique_ptr< LabelPosition > backupPlacement;
1485 PointSet *currentMapShape =
nullptr;
1488 currentMapShape = mapShapeOffsetPositive.get();
1492 currentMapShape = mapShape;
1496 currentMapShape = mapShapeOffsetNegative.get();
1498 if ( !currentMapShape )
1502 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1506 double lineAnchorPoint = 0;
1507 if ( originalPoint && offset !=
NoOffset )
1512 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1518 lineAnchorPoint = totalDistance - lineAnchorPoint;
1521 if (
pal->isCanceled() )
1525 double delta = std::max( li->
characterHeight( 0 ) / 6, totalDistance / candidateTargetCount );
1528 double distanceAlongLineToStartCandidate = 0;
1529 bool singleCandidateOnly =
false;
1536 switch ( textPoint )
1539 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1542 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1545 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1551 singleCandidateOnly =
true;
1555 bool hasTestedFirstPlacement =
false;
1556 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1558 if ( singleCandidateOnly && hasTestedFirstPlacement )
1561 if (
pal->isCanceled() )
1564 hasTestedFirstPlacement =
true;
1566 bool labeledLineSegmentIsRightToLeft =
false;
1568 QgsTextRendererUtils::CurvedTextFlags curvedTextFlags;
1572 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly, curvedTextFlags );
1573 if ( !labelPosition )
1579 bool isBackupPlacementOnly =
false;
1582 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1583 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1585 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1591 isBackupPlacementOnly =
true;
1598 isBackupPlacementOnly =
true;
1604 backupPlacement.reset();
1607 const double angleDiff = labelPosition->angleDifferential();
1608 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1613 double cost = angleDiffAvg / 100;
1614 if ( cost < 0.0001 )
1618 double labelTextAnchor = 0;
1619 switch ( textPoint )
1622 labelTextAnchor = distanceAlongLineToStartCandidate;
1625 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1628 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1634 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1635 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1637 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1649 labelPosition->setCost( cost );
1651 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1656 while ( within && currentPos )
1659 currentPos = currentPos->
nextPart();
1669 if ( isBackupPlacementOnly )
1670 backupPlacement = std::move( p );
1672 positions.emplace_back( std::move( p ) );
1677 for ( std::unique_ptr< LabelPosition > &pos : positions )
1679 lPos.emplace_back( std::move( pos ) );
1682 if ( backupPlacement )
1683 lPos.emplace_back( std::move( backupPlacement ) );
1685 return positions.size();
1709 const double totalArea =
area();
1711 mapShape->
parent =
nullptr;
1713 if (
pal->isCanceled() )
1716 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1718 QgsDebugMsgLevel( QStringLiteral(
"PAL split polygons resulted in:" ), 2 );
1719 for (
PointSet *ps : shapes_final )
1725 std::size_t nbp = 0;
1727 if ( !shapes_final.isEmpty() )
1735 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1737 std::vector< OrientedConvexHullBoundingBox > boxes;
1738 boxes.reserve( shapes_final.size() );
1741 while ( !shapes_final.isEmpty() )
1743 PointSet *shape = shapes_final.takeFirst();
1747 boxes.emplace_back( box );
1753 if (
pal->isCanceled() )
1757 double densityY = densityX;
1764 std::size_t numberCandidatesGenerated = 0;
1779 double dx = densityX;
1780 double dy = densityY;
1781 if ( numTry == 0 && maxPolygonCandidates > 0 )
1784 const double boxArea = box.width * box.length;
1785 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1786 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1790 if (
pal->isCanceled() )
1791 return numberCandidatesGenerated;
1810 bool enoughPlace =
false;
1814 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1815 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1821 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1823 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1827 enoughPlace =
false;
1843 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1845 if ( box.alpha <= M_PI_4 )
1851 alpha = box.alpha - M_PI_2;
1854 else if ( box.length > box.width )
1856 alpha = box.alpha - M_PI_2;
1863 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1869 dlx = std::cos( beta ) * diago;
1870 dly = std::sin( beta ) * diago;
1872 double px0 = box.width / 2.0;
1873 double py0 = box.length / 2.0;
1875 px0 -= std::ceil( px0 / dx ) * dx;
1876 py0 -= std::ceil( py0 / dy ) * dy;
1878 for ( px = px0; px <= box.width; px += dx )
1880 if (
pal->isCanceled() )
1883 for ( py = py0; py <= box.length; py += dy )
1886 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1887 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1897 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1898 numberCandidatesGenerated++;
1909 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1911 lPos.emplace_back( std::move( potentialCandidate ) );
1912 numberCandidatesGenerated++;
1919 nbp = numberCandidatesGenerated;
1920 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1931 while ( numTry < maxTry );
1933 nbp = numberCandidatesGenerated;
1947 std::size_t candidatesCreated = 0;
2007 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
2009 return candidatesCreated;
2013 return candidatesCreated;
2017 const double ringLength = ring->
length();
2018 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2020 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2023 const double delta = ringLength / targetPolygonCandidates;
2026 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2027 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2028 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2031 const double labelAngle = 0;
2033 std::size_t i = lPos.size();
2041 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2043 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
2044 if ( candidate->intersects( preparedBuffer.get() ) )
2062 const double centroidDistance = candidate->getDistanceToPoint( cx, cy,
false );
2063 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2064 candidate->setCost( centroidCost );
2066 lPos.emplace_back( std::move( candidate ) );
2067 candidatesCreated++;
2072 double startSegmentX,
double startSegmentY,
double,
double,
2073 double endSegmentX,
double endSegmentY,
double,
double )
2076 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2081 if ( angle >= 0 && angle <= 5 )
2086 else if ( angle <= 85 )
2090 else if ( angle <= 90 )
2096 else if ( angle <= 95 )
2101 else if ( angle <= 175 )
2105 else if ( angle <= 180 )
2111 else if ( angle <= 185 )
2116 else if ( angle <= 265 )
2120 else if ( angle <= 270 )
2125 else if ( angle <= 275 )
2130 else if ( angle <= 355 )
2140 return !
pal->isCanceled();
2143 return candidatesCreated;
2148 std::vector< std::unique_ptr< LabelPosition > > lPos;
2168 case GEOS_LINESTRING:
2191 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2192 std::fabs(
ymax -
ymin ) < labelHeight ) )
2199 std::size_t created = 0;
2257 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2259 double sizeCost = 0;
2260 if ( geomType == GEOS_LINESTRING )
2262 const double l =
length();
2265 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2266 if ( l >= bbox_length / 4 )
2269 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2271 else if ( geomType == GEOS_POLYGON )
2273 const double a =
area();
2276 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2277 if ( a >= bbox_area / 16 )
2280 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2286 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2288 pos->setCost( pos->cost() + sizeCost / 100 );
2299 const double x1first =
x.front();
2300 const double x1last =
x.back();
2301 const double x2first = p2->
x.front();
2302 const double x2last = p2->
x.back();
2303 const double y1first =
y.front();
2304 const double y1last =
y.back();
2305 const double y2first = p2->
y.front();
2306 const double y2last = p2->
y.back();
2314 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2320 const double p2otherX = p2startTouches ? x2last : x2first;
2321 const double p2otherY = p2startTouches ? y2last : y2first;
2325 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2326 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2331 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2333 catch ( GEOSException &e )
2335 qWarning(
"GEOS exception: %s", e.what() );
2345 if ( !other->
mGeos )
2354 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2357 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2365 mGeos = gTmp.release();
2374 catch ( GEOSException &e )
2376 qWarning(
"GEOS exception: %s", e.what() );
2397 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.
@ 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.
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.
@ 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 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 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...
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...
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.
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())
Flags controlling behavior of curved text generation.
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 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 radians).
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.
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.
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)
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
#define QgsDebugMsgLevel(str, level)
Represents the minimum area, oriented bounding box surrounding a convex hull.
struct GEOSGeom_t GEOSGeometry