57 mGeos =
const_cast<GEOSGeometry *
>( geom );
63 for (
int i = 0; i <
mHoles.count(); i++ )
77 mHoles.last()->holeOf =
this;
91 const GEOSCoordSequence *coordSeq =
nullptr;
94 type = GEOSGeomTypeId_r( geosctxt, geom );
96 if (
type == GEOS_POLYGON )
98 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
100 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
102 for (
int i = 0; i < numHoles; ++i )
104 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
116 geom = GEOSGetExteriorRing_r( geosctxt, geom );
125 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
126 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
129 xmin =
ymin = std::numeric_limits<double>::max();
130 xmax =
ymax = std::numeric_limits<double>::lowest();
137#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
138 GEOSCoordSeq_copyToArrays_r( geosctxt, coordSeq,
x.data(),
y.data(),
nullptr,
nullptr );
139 auto xminmax = std::minmax_element(
x.begin(),
x.end() );
140 xmin = *xminmax.first;
141 xmax = *xminmax.second;
142 auto yminmax = std::minmax_element(
y.begin(),
y.end() );
143 ymin = *yminmax.first;
144 ymax = *yminmax.second;
146 for (
int i = 0; i <
nbPoints; ++i )
148 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
176 if ( mCachedMaxLineCandidates > 0 )
177 return mCachedMaxLineCandidates;
179 const double l =
length();
184 if ( maxForLayer == 0 )
185 mCachedMaxLineCandidates = candidatesForLineLength;
187 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
191 mCachedMaxLineCandidates = 1;
193 return mCachedMaxLineCandidates;
198 if ( mCachedMaxPolygonCandidates > 0 )
199 return mCachedMaxPolygonCandidates;
201 const double a =
area();
206 if ( maxForLayer == 0 )
207 mCachedMaxPolygonCandidates = candidatesForArea;
209 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
213 mCachedMaxPolygonCandidates = 1;
215 return mCachedMaxPolygonCandidates;
237 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
239 if ( quadOffsetX < 0 )
241 if ( quadOffsetY < 0 )
245 else if ( quadOffsetY > 0 )
254 else if ( quadOffsetX > 0 )
256 if ( quadOffsetY < 0 )
260 else if ( quadOffsetY > 0 )
271 if ( quadOffsetY < 0 )
275 else if ( quadOffsetY > 0 )
288 return mTotalRepeats;
302 double cost = 0.00005;
303 int id = lPos.size();
305 double xdiff = -labelW / 2.0;
306 double ydiff = -labelH / 2.0;
310 double lx =
x + xdiff;
311 double ly =
y + ydiff;
321 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false,
LabelPosition::QuadrantOver ) );
331 double cost = 0.0001;
332 int id = lPos.size();
334 double xdiff = -labelW / 2.0;
335 double ydiff = -labelH / 2.0;
352 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
353 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
389 double lx =
x + xdiff;
390 double ly =
y + ydiff;
400 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH, angle, cost,
this,
false, quadrantFromOffset() ) );
413 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
414 unsigned int nPoints = 0;
415 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
418 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
421 catch ( GEOSException &e )
423 qWarning(
"GEOS exception: %s", e.what() );
431void 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 )
442 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
443 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
449 deltaX = -labelWidth / 4.0 - visualMargin.
left();
450 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
456 deltaX = -labelWidth / 2.0;
457 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
463 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
464 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
470 deltaX = - visualMargin.
left() + symbolWidthOffset;
471 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
477 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
478 deltaY = -labelHeight / 2.0;
484 deltaX = -visualMargin.
left() + symbolWidthOffset;
485 deltaY = -labelHeight / 2.0;
491 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
492 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
498 deltaX = -labelWidth / 4.0 - visualMargin.
left();
499 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
505 deltaX = -labelWidth / 2.0;
506 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
512 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
513 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
519 deltaX = -visualMargin.
left() + symbolWidthOffset;
520 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
526 QTransform transformRotation;
527 transformRotation.rotate( angle * 180 / M_PI );
528 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
531 double referenceX = std::cos( alpha ) * distanceToLabel + x;
532 double referenceY = std::sin( alpha ) * distanceToLabel + y;
534 labelX = referenceX + deltaX;
535 labelY = referenceY + deltaY;
546 double symbolWidthOffset{ 0 };
547 double symbolHeightOffset{ 0 };
555 symbolWidthOffset = (
mLF->
symbolSize().width() - geom.boundingBox().width() ) / 2.0;
556 symbolHeightOffset = (
mLF->
symbolSize().height() - geom.boundingBox().height() ) / 2.0;
565 double cost = 0.0001;
566 std::size_t i = lPos.size();
569 std::size_t created = 0;
576 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
580 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
584 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
600 if ( maxNumberCandidates == 0 )
601 maxNumberCandidates = 16;
605 int id = lPos.size();
607 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
612 double a270 = a180 + a90;
613 double a360 = 2 * M_PI;
615 double gamma1, gamma2;
617 if ( distanceToLabel > 0 )
619 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
620 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
624 gamma1 = gamma2 = a90 / 3.0;
627 if ( gamma1 > a90 / 3.0 )
630 if ( gamma2 > a90 / 3.0 )
633 std::size_t numberCandidatesGenerated = 0;
636 double angleToCandidate;
637 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
642 if ( angleToCandidate > a360 )
643 angleToCandidate -= a360;
647 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
649 deltaX = distanceToLabel;
650 double iota = ( angleToCandidate + gamma1 );
651 if ( iota > a360 - gamma1 )
655 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
659 else if ( angleToCandidate < a90 - gamma2 )
661 deltaX = distanceToLabel * std::cos( angleToCandidate );
662 deltaY = distanceToLabel * std::sin( angleToCandidate );
665 else if ( angleToCandidate < a90 + gamma2 )
668 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
669 deltaY = distanceToLabel;
672 else if ( angleToCandidate < a180 - gamma1 )
674 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
675 deltaY = distanceToLabel * std::sin( angleToCandidate );
678 else if ( angleToCandidate < a180 + gamma1 )
680 deltaX = -distanceToLabel - labelWidth;
682 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
685 else if ( angleToCandidate < a270 - gamma2 )
687 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
688 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
691 else if ( angleToCandidate < a270 + gamma2 )
693 deltaY = -distanceToLabel - labelHeight;
695 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
698 else if ( angleToCandidate < a360 )
700 deltaX = distanceToLabel * std::cos( angleToCandidate );
701 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
707 QTransform transformRotation;
708 transformRotation.rotate( angle * 180 / M_PI );
709 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
711 double labelX =
x + deltaX;
712 double labelY =
y + deltaY;
716 if ( maxNumberCandidates == 1 )
719 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
730 lPos.emplace_back( std::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant ) );
731 numberCandidatesGenerated++;
735 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
737 icost =
static_cast< int >( maxNumberCandidates ) - 1;
740 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
742 icost =
static_cast< int >( maxNumberCandidates ) - 2;
748 return numberCandidatesGenerated;
755 double shapeLength = mapShape->
length();
766 std::size_t candidates = 0;
772 if ( candidates < candidateTargetCount )
787 std::vector< double > &
x = line->
x;
788 std::vector< double > &
y = line->
y;
790 std::vector< double > segmentLengths(
nbPoints - 1 );
791 std::vector< double >distanceToSegment(
nbPoints );
793 double totalLineLength = 0.0;
794 for (
int i = 0; i < line->
nbPoints - 1; i++ )
797 distanceToSegment[i] = 0;
799 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
802 totalLineLength += segmentLengths[i];
804 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
807 double lineStepDistance = 0;
810 double currentDistanceAlongLine = lineStepDistance;
814 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
818 currentDistanceAlongLine = lineAnchorPoint;
819 lineStepDistance = -1;
825 double candidateCenterX, candidateCenterY;
827 while ( currentDistanceAlongLine <= totalLineLength )
829 if (
pal->isCanceled() )
834 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
837 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
844 labelX = candidateCenterX;
847 labelX = candidateCenterX - labelWidth / 2;
850 labelX = candidateCenterX - labelWidth;
856 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
858 currentDistanceAlongLine += lineStepDistance;
862 if ( lineStepDistance < 0 )
879 QVector< int > extremeAngleNodes;
882 std::vector< double > &
x = line->
x;
883 std::vector< double > &
y = line->
y;
887 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
889 double x1 =
x[i - 1];
891 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
892 double y1 =
y[i - 1];
894 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
899 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
903 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
904 extremeAngleNodes << i;
906 extremeAngleNodes << numberNodes - 1;
908 if ( extremeAngleNodes.isEmpty() )
915 std::vector< double > segmentLengths( numberNodes - 1 );
916 std::vector< double > distanceToSegment( numberNodes );
917 double totalLineLength = 0.0;
918 QVector< double > straightSegmentLengths;
919 QVector< double > straightSegmentAngles;
920 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
921 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
922 double currentStraightSegmentLength = 0;
923 double longestSegmentLength = 0;
924 int segmentIndex = 0;
925 double segmentStartX =
x[0];
926 double segmentStartY =
y[0];
927 for (
int i = 0; i < numberNodes - 1; i++ )
930 distanceToSegment[i] = 0;
932 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
935 totalLineLength += segmentLengths[i];
936 if ( extremeAngleNodes.contains( i ) )
939 straightSegmentLengths << currentStraightSegmentLength;
941 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
943 currentStraightSegmentLength = 0;
944 segmentStartX =
x[i];
945 segmentStartY =
y[i];
947 currentStraightSegmentLength += segmentLengths[i];
949 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
950 straightSegmentLengths << currentStraightSegmentLength;
952 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
955 if ( totalLineLength < labelWidth )
963 double lineStepDistance = ( totalLineLength - labelWidth );
964 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
966 double distanceToEndOfSegment = 0.0;
967 int lastNodeInSegment = 0;
969 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
971 currentStraightSegmentLength = straightSegmentLengths.at( i );
972 double currentSegmentAngle = straightSegmentAngles.at( i );
973 lastNodeInSegment = extremeAngleNodes.at( i );
974 double distanceToStartOfSegment = distanceToEndOfSegment;
975 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
976 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
978 if ( currentStraightSegmentLength < labelWidth )
982 double currentDistanceAlongLine = distanceToStartOfSegment;
983 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
984 double candidateLength = 0.0;
990 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
991 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
993 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
995 if (
pal->isCanceled() )
1001 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1002 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1004 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1010 cost = candidateLength / labelWidth;
1016 cost = ( 1 - cost ) / 100;
1019 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1020 double labelTextAnchor = 0;
1021 switch ( textPoint )
1024 labelTextAnchor = currentDistanceAlongLine;
1027 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1030 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1039 if ( placementIsFlexible )
1042 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1043 cost += costCenter * 0.0005;
1051 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1052 cost += costLineCenter * 0.0005;
1055 if ( placementIsFlexible )
1057 cost += segmentCost * 0.0005;
1058 cost += segmentAngleCost * 0.0001;
1066 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1070 beta = angle + M_PI_2;
1075 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1085 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1086 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 ) );
1093 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1094 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 ) );
1101 const double candidateCost = cost + 0.002;
1102 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 ) );
1108 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1115 currentDistanceAlongLine += lineStepDistance;
1138 std::vector< double > &
x = line->
x;
1139 std::vector< double > &
y = line->
y;
1141 std::vector< double > segmentLengths(
nbPoints - 1 );
1142 std::vector< double >distanceToSegment(
nbPoints );
1144 double totalLineLength = 0.0;
1145 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1148 distanceToSegment[i] = 0;
1150 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1153 totalLineLength += segmentLengths[i];
1155 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1157 double lineStepDistance = ( totalLineLength - labelWidth );
1158 double currentDistanceAlongLine = 0;
1164 if ( totalLineLength > labelWidth )
1166 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1170 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1171 lineStepDistance = -1;
1172 totalLineLength = labelWidth;
1177 currentDistanceAlongLine = std::numeric_limits< double >::max();
1188 switch ( textPoint )
1191 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1194 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1197 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1203 lineStepDistance = -1;
1207 double candidateLength;
1209 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1213 if (
pal->isCanceled() )
1219 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1220 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1222 if ( currentDistanceAlongLine < 0 )
1230 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1233 cost = candidateLength / labelWidth;
1239 cost = ( 1 - cost ) / 100;
1243 double textAnchorPoint = 0;
1244 switch ( textPoint )
1247 textAnchorPoint = currentDistanceAlongLine;
1250 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1253 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1259 double costCenter = std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength;
1260 cost += costCenter / 1000;
1261 cost += initialCost;
1268 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1272 beta = angle + M_PI_2;
1277 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1287 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1288 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 ) );
1295 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1296 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 ) );
1303 const double candidateCost = cost + 0.002;
1304 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 ) );
1310 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1317 currentDistanceAlongLine += lineStepDistance;
1321 if ( lineStepDistance < 0 )
1331 Q_ASSERT( metrics );
1333 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1334 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1336 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1340 labeledLineSegmentIsRightToLeft = !uprightOnly ? placement->labeledLineSegmentIsRightToLeft : placement->flippedCharacterPlacementToGetUprightLabels;
1342 if ( placement->graphemePlacement.empty() )
1345 auto it = placement->graphemePlacement.constBegin();
1346 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 );
1347 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1348 firstPosition->setPartId( it->graphemeIndex );
1351 while ( it != placement->graphemePlacement.constEnd() )
1353 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 );
1354 position->setPartId( it->graphemeIndex );
1357 previousPosition->
setNextPart( std::move( position ) );
1358 previousPosition = nextPosition;
1362 return firstPosition;
1374 const int characterCount = li->
count();
1375 if ( characterCount == 0 )
1381 double totalCharacterWidth = 0;
1382 for (
int i = 0; i < characterCount; ++i )
1385 std::unique_ptr< PointSet > expanded;
1386 double shapeLength = mapShape->
length();
1389 allowOverrun =
false;
1407 if ( totalCharacterWidth > shapeLength )
1409 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1420 if ( allowOverrun && overrun > 0 )
1423 expanded = mapShape->
clone();
1425 mapShape = expanded.get();
1426 shapeLength += 2 * overrun;
1434 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1435 bool positiveShapeHasNegativeDistance =
false;
1436 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1437 bool negativeShapeHasNegativeDistance =
false;
1438 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1442 mapShapeOffsetPositive = mapShape->
clone();
1444 mapShapeOffsetNegative = mapShape->
clone();
1447 if ( mapShapeOffsetPositive )
1448 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1449 positiveShapeHasNegativeDistance = offsetDistance < 0;
1450 if ( mapShapeOffsetNegative )
1451 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1452 negativeShapeHasNegativeDistance = offsetDistance > 0;
1469 if ( mapShapeOffsetPositive )
1470 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1471 positiveShapeHasNegativeDistance = offsetDistance > 0;
1472 if ( mapShapeOffsetNegative )
1473 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1474 negativeShapeHasNegativeDistance = offsetDistance < 0;
1480 std::vector< std::unique_ptr< LabelPosition >> positions;
1481 std::unique_ptr< LabelPosition > backupPlacement;
1484 PointSet *currentMapShape =
nullptr;
1487 currentMapShape = mapShapeOffsetPositive.get();
1491 currentMapShape = mapShape;
1495 currentMapShape = mapShapeOffsetNegative.get();
1497 if ( !currentMapShape )
1501 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1505 double lineAnchorPoint = 0;
1506 if ( originalPoint && offset !=
NoOffset )
1511 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1517 lineAnchorPoint = totalDistance - lineAnchorPoint;
1520 if (
pal->isCanceled() )
1524 double delta = std::max( li->
characterHeight( 0 ) / 6, totalDistance / candidateTargetCount );
1527 double distanceAlongLineToStartCandidate = 0;
1528 bool singleCandidateOnly =
false;
1535 switch ( textPoint )
1538 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1541 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1544 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1550 singleCandidateOnly =
true;
1554 bool hasTestedFirstPlacement =
false;
1555 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1557 if ( singleCandidateOnly && hasTestedFirstPlacement )
1560 if (
pal->isCanceled() )
1563 hasTestedFirstPlacement =
true;
1565 bool labeledLineSegmentIsRightToLeft =
false;
1567 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly,
1570 if ( !labelPosition )
1576 bool isBackupPlacementOnly =
false;
1579 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1580 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1582 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1588 isBackupPlacementOnly =
true;
1595 isBackupPlacementOnly =
true;
1601 backupPlacement.reset();
1604 const double angleDiff = labelPosition->angleDifferential();
1605 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1610 double cost = angleDiffAvg / 100;
1611 if ( cost < 0.0001 )
1615 double labelTextAnchor = 0;
1616 switch ( textPoint )
1619 labelTextAnchor = distanceAlongLineToStartCandidate;
1622 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1625 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1631 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1632 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1634 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1646 labelPosition->setCost( cost );
1648 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1653 while ( within && currentPos )
1656 currentPos = currentPos->
nextPart();
1666 if ( isBackupPlacementOnly )
1667 backupPlacement = std::move( p );
1669 positions.emplace_back( std::move( p ) );
1674 for ( std::unique_ptr< LabelPosition > &pos : positions )
1676 lPos.emplace_back( std::move( pos ) );
1679 if ( backupPlacement )
1680 lPos.emplace_back( std::move( backupPlacement ) );
1682 return positions.size();
1706 const double totalArea =
area();
1708 mapShape->
parent =
nullptr;
1710 if (
pal->isCanceled() )
1713 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1715 QgsDebugMsg( QStringLiteral(
"PAL split polygons resulted in:" ) );
1716 for (
PointSet *ps : shapes_final )
1722 std::size_t nbp = 0;
1724 if ( !shapes_final.isEmpty() )
1732 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1734 std::vector< OrientedConvexHullBoundingBox > boxes;
1735 boxes.reserve( shapes_final.size() );
1738 while ( !shapes_final.isEmpty() )
1740 PointSet *shape = shapes_final.takeFirst();
1744 boxes.emplace_back( box );
1750 if (
pal->isCanceled() )
1754 double densityY = densityX;
1761 std::size_t numberCandidatesGenerated = 0;
1776 double dx = densityX;
1777 double dy = densityY;
1778 if ( numTry == 0 && maxPolygonCandidates > 0 )
1781 const double boxArea = box.width * box.length;
1782 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1783 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1787 if (
pal->isCanceled() )
1788 return numberCandidatesGenerated;
1807 bool enoughPlace =
false;
1811 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1812 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1818 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1820 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1824 enoughPlace =
false;
1840 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1842 if ( box.alpha <= M_PI_4 )
1848 alpha = box.alpha - M_PI_2;
1851 else if ( box.length > box.width )
1853 alpha = box.alpha - M_PI_2;
1860 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1866 dlx = std::cos( beta ) * diago;
1867 dly = std::sin( beta ) * diago;
1869 double px0 = box.width / 2.0;
1870 double py0 = box.length / 2.0;
1872 px0 -= std::ceil( px0 / dx ) * dx;
1873 py0 -= std::ceil( py0 / dy ) * dy;
1875 for ( px = px0; px <= box.width; px += dx )
1877 if (
pal->isCanceled() )
1880 for ( py = py0; py <= box.length; py += dy )
1883 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1884 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1894 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1895 numberCandidatesGenerated++;
1906 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1908 lPos.emplace_back( std::move( potentialCandidate ) );
1909 numberCandidatesGenerated++;
1916 nbp = numberCandidatesGenerated;
1917 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1928 while ( numTry < maxTry );
1930 nbp = numberCandidatesGenerated;
1944 std::size_t candidatesCreated = 0;
2004 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
2006 return candidatesCreated;
2010 return candidatesCreated;
2014 const double ringLength = ring->
length();
2015 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2017 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2020 const double delta = ringLength / targetPolygonCandidates;
2023 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2024 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2025 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2028 const double labelAngle = 0;
2030 std::size_t i = lPos.size();
2038 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2040 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
2041 if ( candidate->intersects( preparedBuffer.get() ) )
2059 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
2060 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2061 candidate->setCost( centroidCost );
2063 lPos.emplace_back( std::move( candidate ) );
2064 candidatesCreated++;
2069 double startSegmentX,
double startSegmentY,
double,
double,
2070 double endSegmentX,
double endSegmentY,
double,
double )
2073 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2078 if ( angle >= 0 && angle <= 5 )
2083 else if ( angle <= 85 )
2087 else if ( angle <= 90 )
2093 else if ( angle <= 95 )
2098 else if ( angle <= 175 )
2102 else if ( angle <= 180 )
2108 else if ( angle <= 185 )
2113 else if ( angle <= 265 )
2117 else if ( angle <= 270 )
2122 else if ( angle <= 275 )
2127 else if ( angle <= 355 )
2137 return !
pal->isCanceled();
2140 return candidatesCreated;
2145 std::vector< std::unique_ptr< LabelPosition > > lPos;
2165 case GEOS_LINESTRING:
2188 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2189 std::fabs(
ymax -
ymin ) < labelHeight ) )
2196 std::size_t created = 0;
2254 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2256 double sizeCost = 0;
2257 if ( geomType == GEOS_LINESTRING )
2259 const double l =
length();
2262 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2263 if ( l >= bbox_length / 4 )
2266 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2268 else if ( geomType == GEOS_POLYGON )
2270 const double a =
area();
2273 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2274 if ( a >= bbox_area / 16 )
2277 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2283 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2285 pos->setCost( pos->cost() + sizeCost / 100 );
2296 const double x1first =
x.front();
2297 const double x1last =
x.back();
2298 const double x2first = p2->
x.front();
2299 const double x2last = p2->
x.back();
2300 const double y1first =
y.front();
2301 const double y1last =
y.back();
2302 const double y2first = p2->
y.front();
2303 const double y2last = p2->
y.back();
2311 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2317 const double p2otherX = p2startTouches ? x2last : x2first;
2318 const double p2otherY = p2startTouches ? y2last : y2first;
2322 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2323 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2328 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2330 catch ( GEOSException &e )
2332 qWarning(
"GEOS exception: %s", e.what() );
2342 if ( !other->
mGeos )
2348 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2349 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2350 GEOSGeometry *geoms[2] = { g1, g2 };
2351 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2354 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2362 mGeos = gTmp.release();
2371 catch ( GEOSException &e )
2373 qWarning(
"GEOS exception: %s", e.what() );
2394 bool result =
false;
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
@ MiddleLeft
Label on left of point.
@ TopRight
Label on top-right of point.
@ MiddleRight
Label on right of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopMiddle
Label directly above point.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ TopLeft
Label on top-left of point.
@ BottomMiddle
Label directly below point.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
@ 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...
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.
QgsLabeling::PolygonPlacementFlags polygonPlacementFlags() const
Returns the polygon placement flags, which dictate how polygon labels can be placed.
QgsPointXY positionOffset() const
Applies only to "offset from point" placement strategy.
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
pal::Layer * layer() const
Gets PAL layer of the label feature. Should be only used internally in PAL.
QgsLabeling::LinePlacementFlags arrangementFlags() const
Returns the feature's arrangement flags.
bool alwaysShow() const
Whether label should be always shown (sets very high label priority)
double lineAnchorPercent() const
Returns the percent along the line at which labels should be placed, for line labels only.
const 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::LabelOffsetType offsetType() const
Returns the offset type, which determines how offsets and distance to label behaves.
QgsPointXY fixedPosition() const
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true)
@ Strict
Line anchor is a strict placement, and other placements are not permitted.
@ HintOnly
Line anchor is a hint for preferred placement only, but other placements close to the hint are permit...
AnchorTextPoint
Anchor point of label text.
@ EndOfText
Anchor using end of text.
@ StartOfText
Anchor using start of text.
@ CenterOfText
Anchor using center of text.
@ FollowPlacement
Automatically set the anchor point based on the lineAnchorPercent() value. Values <25% will use the s...
Contains constants and enums relating to labeling.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ OnLine
Labels can be placed directly over a line feature.
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ AllowPlacementInsideOfPolygon
Labels can be placed inside a polygon feature.
@ AllowPlacementOutsideOfPolygon
Labels can be placed outside of a polygon feature.
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 ...
static CurvePlacementProperties * generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const double *x, const double *y, int numPoints, const std::vector< double > &pathDistances, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, bool uprightOnly=true)
Calculates curved text placement properties.
Main class to handle feature.
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::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::unique_ptr< LabelPosition > curvedPlacementAtOffset(PointSet *mapShape, const std::vector< double > &pathDistances, QgsTextRendererUtils::LabelLineDirection direction, double distance, bool &labeledLineSegmentIsRightToLeft, bool applyAngleConstraints, bool uprightOnly)
Returns the label position for a curved label at a specific offset along a path.
std::vector< std::unique_ptr< LabelPosition > > createCandidates(Pal *pal)
Generates a list of candidate positions for labels for this feature.
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
Layer * layer()
Returns the layer that feature belongs to.
PathOffset
Path offset variances used in curved placement.
int totalRepeats() const
Returns the total number of repeating labels associated with this label.
std::size_t createCandidatesAlongLineNearMidpoint(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, double initialCost=0.0, Pal *pal=nullptr)
Generate candidates for line feature, by trying to place candidates as close as possible to the line'...
void addSizePenalty(std::vector< std::unique_ptr< LabelPosition > > &lPos, double bbx[4], double bby[4]) const
Increases the cost of the label candidates for this feature, based on the size of the feature.
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom
double calculatePriority() const
Calculates the priority for the feature.
std::size_t createCandidatesAtOrderedPositionsOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generates candidates following a prioritized list of predefined positions around a point.
std::size_t createCandidateCenteredOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate one candidate centered over the specified point.
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
Represents the minimum area, oriented bounding box surrounding a convex hull.