64 for (
int i = 0; i <
mHoles.count(); i++ )
74 , mTotalRepeats( other.mTotalRepeats )
75 , mCachedMaxLineCandidates( other.mCachedMaxLineCandidates )
76 , mCachedMaxPolygonCandidates( other.mCachedMaxPolygonCandidates )
81 mHoles.last()->holeOf =
this;
95 const GEOSCoordSequence *coordSeq =
nullptr;
98 type = GEOSGeomTypeId_r( geosctxt, geom );
100 if (
type == GEOS_POLYGON )
102 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
104 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
106 for (
int i = 0; i < numHoles; ++i )
108 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
120 geom = GEOSGetExteriorRing_r( geosctxt, geom );
129 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
130 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
133 xmin =
ymin = std::numeric_limits<double>::max();
134 xmax =
ymax = std::numeric_limits<double>::lowest();
141#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
142 GEOSCoordSeq_copyToArrays_r( geosctxt, coordSeq,
x.data(),
y.data(),
nullptr,
nullptr );
143 auto xminmax = std::minmax_element(
x.begin(),
x.end() );
144 xmin = *xminmax.first;
145 xmax = *xminmax.second;
146 auto yminmax = std::minmax_element(
y.begin(),
y.end() );
147 ymin = *yminmax.first;
148 ymax = *yminmax.second;
150 for (
int i = 0; i <
nbPoints; ++i )
152 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
180 if ( mCachedMaxLineCandidates > 0 )
181 return mCachedMaxLineCandidates;
183 const double l =
length();
188 if ( maxForLayer == 0 )
189 mCachedMaxLineCandidates = candidatesForLineLength;
191 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
195 mCachedMaxLineCandidates = 1;
197 return mCachedMaxLineCandidates;
202 if ( mCachedMaxPolygonCandidates > 0 )
203 return mCachedMaxPolygonCandidates;
205 const double a =
area();
210 if ( maxForLayer == 0 )
211 mCachedMaxPolygonCandidates = candidatesForArea;
213 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
217 mCachedMaxPolygonCandidates = 1;
219 return mCachedMaxPolygonCandidates;
241 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
243 if ( quadOffsetX < 0 )
245 if ( quadOffsetY < 0 )
249 else if ( quadOffsetY > 0 )
258 else if ( quadOffsetX > 0 )
260 if ( quadOffsetY < 0 )
264 else if ( quadOffsetY > 0 )
275 if ( quadOffsetY < 0 )
279 else if ( quadOffsetY > 0 )
292 return mTotalRepeats;
306 double cost = 0.00005;
307 int id = lPos.size();
309 double xdiff = -labelW / 2.0;
310 double ydiff = -labelH / 2.0;
314 double lx =
x + xdiff;
315 double ly =
y + ydiff;
325 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false,
LabelPosition::QuadrantOver ) );
335 double cost = 0.0001;
336 int id = lPos.size();
338 double xdiff = -labelW / 2.0;
339 double ydiff = -labelH / 2.0;
356 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
357 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
393 double lx =
x + xdiff;
394 double ly =
y + ydiff;
404 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false, quadrantFromOffset() ) );
417 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
418 unsigned int nPoints = 0;
419 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
422 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
425 catch ( GEOSException &e )
427 qWarning(
"GEOS exception: %s", e.what() );
435void 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 )
446 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
447 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
453 deltaX = -labelWidth / 4.0 - visualMargin.
left();
454 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
460 deltaX = -labelWidth / 2.0;
461 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
467 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
468 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
474 deltaX = - visualMargin.
left() + symbolWidthOffset;
475 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
481 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
482 deltaY = -labelHeight / 2.0;
488 deltaX = -visualMargin.
left() + symbolWidthOffset;
489 deltaY = -labelHeight / 2.0;
495 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
496 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
502 deltaX = -labelWidth / 4.0 - visualMargin.
left();
503 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
509 deltaX = -labelWidth / 2.0;
510 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
516 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
517 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
523 deltaX = -visualMargin.
left() + symbolWidthOffset;
524 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
530 QTransform transformRotation;
531 transformRotation.rotate( angle * 180 / M_PI );
532 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
535 double referenceX = std::cos( alpha ) * distanceToLabel + x;
536 double referenceY = std::sin( alpha ) * distanceToLabel + y;
538 labelX = referenceX + deltaX;
539 labelY = referenceY + deltaY;
435void 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 ) {
…}
550 double symbolWidthOffset{ 0 };
551 double symbolHeightOffset{ 0 };
559 symbolWidthOffset = (
mLF->
symbolSize().width() - geom.boundingBox().width() ) / 2.0;
560 symbolHeightOffset = (
mLF->
symbolSize().height() - geom.boundingBox().height() ) / 2.0;
569 double cost = 0.0001;
570 std::size_t i = lPos.size();
573 std::size_t created = 0;
580 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
584 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
588 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
604 if ( maxNumberCandidates == 0 )
605 maxNumberCandidates = 16;
609 int id = lPos.size();
611 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
616 double a270 = a180 + a90;
617 double a360 = 2 * M_PI;
619 double gamma1, gamma2;
621 if ( distanceToLabel > 0 )
623 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
624 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
628 gamma1 = gamma2 = a90 / 3.0;
631 if ( gamma1 > a90 / 3.0 )
634 if ( gamma2 > a90 / 3.0 )
637 std::size_t numberCandidatesGenerated = 0;
640 double angleToCandidate;
641 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
646 if ( angleToCandidate > a360 )
647 angleToCandidate -= a360;
651 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
653 deltaX = distanceToLabel;
654 double iota = ( angleToCandidate + gamma1 );
655 if ( iota > a360 - gamma1 )
659 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
663 else if ( angleToCandidate < a90 - gamma2 )
665 deltaX = distanceToLabel * std::cos( angleToCandidate );
666 deltaY = distanceToLabel * std::sin( angleToCandidate );
669 else if ( angleToCandidate < a90 + gamma2 )
672 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
673 deltaY = distanceToLabel;
676 else if ( angleToCandidate < a180 - gamma1 )
678 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
679 deltaY = distanceToLabel * std::sin( angleToCandidate );
682 else if ( angleToCandidate < a180 + gamma1 )
684 deltaX = -distanceToLabel - labelWidth;
686 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
689 else if ( angleToCandidate < a270 - gamma2 )
691 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
692 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
695 else if ( angleToCandidate < a270 + gamma2 )
697 deltaY = -distanceToLabel - labelHeight;
699 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
702 else if ( angleToCandidate < a360 )
704 deltaX = distanceToLabel * std::cos( angleToCandidate );
705 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
711 QTransform transformRotation;
712 transformRotation.rotate( angle * 180 / M_PI );
713 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
715 double labelX =
x + deltaX;
716 double labelY =
y + deltaY;
720 if ( maxNumberCandidates == 1 )
723 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
734 lPos.emplace_back( std::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
735 numberCandidatesGenerated++;
739 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
741 icost =
static_cast< int >( maxNumberCandidates ) - 1;
744 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
746 icost =
static_cast< int >( maxNumberCandidates ) - 2;
752 return numberCandidatesGenerated;
759 double shapeLength = mapShape->
length();
770 std::size_t candidates = 0;
776 if ( candidates < candidateTargetCount )
791 std::vector< double > &
x = line->
x;
792 std::vector< double > &
y = line->
y;
794 std::vector< double > segmentLengths(
nbPoints - 1 );
795 std::vector< double >distanceToSegment(
nbPoints );
797 double totalLineLength = 0.0;
798 for (
int i = 0; i < line->
nbPoints - 1; i++ )
801 distanceToSegment[i] = 0;
803 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
806 totalLineLength += segmentLengths[i];
808 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
811 double lineStepDistance = 0;
814 double currentDistanceAlongLine = lineStepDistance;
818 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
822 currentDistanceAlongLine = lineAnchorPoint;
823 lineStepDistance = -1;
829 double candidateCenterX, candidateCenterY;
831 while ( currentDistanceAlongLine <= totalLineLength )
833 if (
pal->isCanceled() )
838 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
841 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
848 labelX = candidateCenterX;
851 labelX = candidateCenterX - labelWidth / 2;
854 labelX = candidateCenterX - labelWidth;
860 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
862 currentDistanceAlongLine += lineStepDistance;
866 if ( lineStepDistance < 0 )
883 QVector< int > extremeAngleNodes;
886 std::vector< double > &
x = line->
x;
887 std::vector< double > &
y = line->
y;
891 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
893 double x1 =
x[i - 1];
895 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
896 double y1 =
y[i - 1];
898 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
903 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
907 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
908 extremeAngleNodes << i;
910 extremeAngleNodes << numberNodes - 1;
912 if ( extremeAngleNodes.isEmpty() )
919 std::vector< double > segmentLengths( numberNodes - 1 );
920 std::vector< double > distanceToSegment( numberNodes );
921 double totalLineLength = 0.0;
922 QVector< double > straightSegmentLengths;
923 QVector< double > straightSegmentAngles;
924 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
925 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
926 double currentStraightSegmentLength = 0;
927 double longestSegmentLength = 0;
928 double segmentStartX =
x[0];
929 double segmentStartY =
y[0];
930 for (
int i = 0; i < numberNodes - 1; i++ )
933 distanceToSegment[i] = 0;
935 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
938 totalLineLength += segmentLengths[i];
939 if ( extremeAngleNodes.contains( i ) )
942 straightSegmentLengths << currentStraightSegmentLength;
944 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
945 currentStraightSegmentLength = 0;
946 segmentStartX =
x[i];
947 segmentStartY =
y[i];
949 currentStraightSegmentLength += segmentLengths[i];
951 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
952 straightSegmentLengths << currentStraightSegmentLength;
954 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
957 if ( totalLineLength < labelWidth )
965 double lineStepDistance = ( totalLineLength - labelWidth );
966 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
968 double distanceToEndOfSegment = 0.0;
969 int lastNodeInSegment = 0;
971 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
973 currentStraightSegmentLength = straightSegmentLengths.at( i );
974 double currentSegmentAngle = straightSegmentAngles.at( i );
975 lastNodeInSegment = extremeAngleNodes.at( i );
976 double distanceToStartOfSegment = distanceToEndOfSegment;
977 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
978 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
980 if ( currentStraightSegmentLength < labelWidth )
984 double currentDistanceAlongLine = distanceToStartOfSegment;
985 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
986 double candidateLength = 0.0;
992 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
993 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
995 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
997 if (
pal->isCanceled() )
1003 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1004 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1012 cost = candidateLength / labelWidth;
1018 cost = ( 1 - cost ) / 100;
1021 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1022 double labelTextAnchor = 0;
1023 switch ( textPoint )
1026 labelTextAnchor = currentDistanceAlongLine;
1029 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1032 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1041 if ( placementIsFlexible )
1044 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1045 cost += costCenter * 0.0005;
1053 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1054 cost += costLineCenter * 0.0005;
1057 if ( placementIsFlexible )
1059 cost += segmentCost * 0.0005;
1060 cost += segmentAngleCost * 0.0001;
1068 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1072 beta = angle + M_PI_2;
1077 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1087 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1088 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 ) );
1095 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1096 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 ) );
1103 const double candidateCost = cost + 0.002;
1104 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 ) );
1110 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1117 currentDistanceAlongLine += lineStepDistance;
1140 std::vector< double > &
x = line->
x;
1141 std::vector< double > &
y = line->
y;
1143 std::vector< double > segmentLengths(
nbPoints - 1 );
1144 std::vector< double >distanceToSegment(
nbPoints );
1146 double totalLineLength = 0.0;
1147 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1150 distanceToSegment[i] = 0;
1152 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1155 totalLineLength += segmentLengths[i];
1157 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1159 double lineStepDistance = ( totalLineLength - labelWidth );
1160 double currentDistanceAlongLine = 0;
1166 if ( totalLineLength > labelWidth )
1168 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1172 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1173 lineStepDistance = -1;
1174 totalLineLength = labelWidth;
1179 currentDistanceAlongLine = std::numeric_limits< double >::max();
1190 switch ( textPoint )
1193 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1196 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1199 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1205 lineStepDistance = -1;
1209 double candidateLength;
1211 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1215 if (
pal->isCanceled() )
1221 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1222 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1224 if ( currentDistanceAlongLine < 0 )
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 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 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 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