57 mGeos =
const_cast<GEOSGeometry *
>( geom );
63 for (
int i = 0; i <
mHoles.count(); i++ )
65 mHoles.at( i )->holeOf =
this;
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;
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;
549 double cost = 0.0001;
550 std::size_t i = lPos.size();
553 std::size_t created = 0;
560 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset,
angle );
564 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
568 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
584 if ( maxNumberCandidates == 0 )
585 maxNumberCandidates = 16;
589 int id = lPos.size();
591 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
596 double a270 = a180 + a90;
597 double a360 = 2 * M_PI;
599 double gamma1, gamma2;
601 if ( distanceToLabel > 0 )
603 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
604 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
608 gamma1 = gamma2 = a90 / 3.0;
611 if ( gamma1 > a90 / 3.0 )
614 if ( gamma2 > a90 / 3.0 )
617 std::size_t numberCandidatesGenerated = 0;
620 double angleToCandidate;
621 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
626 if ( angleToCandidate > a360 )
627 angleToCandidate -= a360;
631 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
633 deltaX = distanceToLabel;
634 double iota = ( angleToCandidate + gamma1 );
635 if ( iota > a360 - gamma1 )
639 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
643 else if ( angleToCandidate < a90 - gamma2 )
645 deltaX = distanceToLabel * std::cos( angleToCandidate );
646 deltaY = distanceToLabel * std::sin( angleToCandidate );
649 else if ( angleToCandidate < a90 + gamma2 )
652 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
653 deltaY = distanceToLabel;
656 else if ( angleToCandidate < a180 - gamma1 )
658 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
659 deltaY = distanceToLabel * std::sin( angleToCandidate );
662 else if ( angleToCandidate < a180 + gamma1 )
664 deltaX = -distanceToLabel - labelWidth;
666 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
669 else if ( angleToCandidate < a270 - gamma2 )
671 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
672 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
675 else if ( angleToCandidate < a270 + gamma2 )
677 deltaY = -distanceToLabel - labelHeight;
679 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
682 else if ( angleToCandidate < a360 )
684 deltaX = distanceToLabel * std::cos( angleToCandidate );
685 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
691 QTransform transformRotation;
692 transformRotation.rotate(
angle * 180 / M_PI );
693 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
695 double labelX =
x + deltaX;
696 double labelY =
y + deltaY;
700 if ( maxNumberCandidates == 1 )
703 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
714 lPos.emplace_back( std::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
715 numberCandidatesGenerated++;
719 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
721 icost =
static_cast< int >( maxNumberCandidates ) - 1;
724 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
726 icost =
static_cast< int >( maxNumberCandidates ) - 2;
732 return numberCandidatesGenerated;
739 double shapeLength = mapShape->
length();
750 std::size_t candidates = 0;
756 if ( candidates < candidateTargetCount )
771 std::vector< double > &
x = line->
x;
772 std::vector< double > &
y = line->
y;
774 std::vector< double > segmentLengths(
nbPoints - 1 );
775 std::vector< double >distanceToSegment(
nbPoints );
777 double totalLineLength = 0.0;
778 for (
int i = 0; i < line->
nbPoints - 1; i++ )
781 distanceToSegment[i] = 0;
783 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
786 totalLineLength += segmentLengths[i];
788 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
791 double lineStepDistance = 0;
794 double currentDistanceAlongLine = lineStepDistance;
798 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
802 currentDistanceAlongLine = lineAnchorPoint;
803 lineStepDistance = -1;
809 double candidateCenterX, candidateCenterY;
811 while ( currentDistanceAlongLine <= totalLineLength )
813 if (
pal->isCanceled() )
818 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
821 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
828 labelX = candidateCenterX;
831 labelX = candidateCenterX - labelWidth / 2;
834 labelX = candidateCenterX - labelWidth;
840 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
842 currentDistanceAlongLine += lineStepDistance;
846 if ( lineStepDistance < 0 )
860 flags = QgsLabeling::LinePlacementFlag::OnLine;
863 QVector< int > extremeAngleNodes;
866 std::vector< double > &
x = line->
x;
867 std::vector< double > &
y = line->
y;
871 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
873 double x1 =
x[i - 1];
875 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
876 double y1 =
y[i - 1];
878 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
883 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
887 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
888 extremeAngleNodes << i;
890 extremeAngleNodes << numberNodes - 1;
892 if ( extremeAngleNodes.isEmpty() )
899 std::vector< double > segmentLengths( numberNodes - 1 );
900 std::vector< double > distanceToSegment( numberNodes );
901 double totalLineLength = 0.0;
902 QVector< double > straightSegmentLengths;
903 QVector< double > straightSegmentAngles;
904 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
905 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
906 double currentStraightSegmentLength = 0;
907 double longestSegmentLength = 0;
908 int segmentIndex = 0;
909 double segmentStartX =
x[0];
910 double segmentStartY =
y[0];
911 for (
int i = 0; i < numberNodes - 1; i++ )
914 distanceToSegment[i] = 0;
916 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
919 totalLineLength += segmentLengths[i];
920 if ( extremeAngleNodes.contains( i ) )
923 straightSegmentLengths << currentStraightSegmentLength;
925 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
927 currentStraightSegmentLength = 0;
928 segmentStartX =
x[i];
929 segmentStartY =
y[i];
931 currentStraightSegmentLength += segmentLengths[i];
933 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
934 straightSegmentLengths << currentStraightSegmentLength;
936 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
939 if ( totalLineLength < labelWidth )
947 double lineStepDistance = ( totalLineLength - labelWidth );
948 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
950 double distanceToEndOfSegment = 0.0;
951 int lastNodeInSegment = 0;
953 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
955 currentStraightSegmentLength = straightSegmentLengths.at( i );
956 double currentSegmentAngle = straightSegmentAngles.at( i );
957 lastNodeInSegment = extremeAngleNodes.at( i );
958 double distanceToStartOfSegment = distanceToEndOfSegment;
959 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
960 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
962 if ( currentStraightSegmentLength < labelWidth )
966 double currentDistanceAlongLine = distanceToStartOfSegment;
967 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
968 double candidateLength = 0.0;
974 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
975 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
977 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
979 if (
pal->isCanceled() )
985 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
986 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
988 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
994 cost = candidateLength / labelWidth;
1000 cost = ( 1 - cost ) / 100;
1003 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1004 double labelTextAnchor = 0;
1005 switch ( textPoint )
1008 labelTextAnchor = currentDistanceAlongLine;
1011 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1014 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1023 if ( placementIsFlexible )
1026 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1027 cost += costCenter * 0.0005;
1035 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1036 cost += costLineCenter * 0.0005;
1039 if ( placementIsFlexible )
1041 cost += segmentCost * 0.0005;
1042 cost += segmentAngleCost * 0.0001;
1050 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1054 beta =
angle + M_PI_2;
1059 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1061 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1062 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1063 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1069 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1070 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 ) );
1077 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1078 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 ) );
1081 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1085 const double candidateCost = cost + 0.002;
1086 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 ) );
1092 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1099 currentDistanceAlongLine += lineStepDistance;
1118 flags = QgsLabeling::LinePlacementFlag::OnLine;
1122 std::vector< double > &
x = line->
x;
1123 std::vector< double > &
y = line->
y;
1125 std::vector< double > segmentLengths(
nbPoints - 1 );
1126 std::vector< double >distanceToSegment(
nbPoints );
1128 double totalLineLength = 0.0;
1129 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1132 distanceToSegment[i] = 0;
1134 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1137 totalLineLength += segmentLengths[i];
1139 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1141 double lineStepDistance = ( totalLineLength - labelWidth );
1142 double currentDistanceAlongLine = 0;
1148 if ( totalLineLength > labelWidth )
1150 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1154 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1155 lineStepDistance = -1;
1156 totalLineLength = labelWidth;
1161 currentDistanceAlongLine = std::numeric_limits< double >::max();
1172 switch ( textPoint )
1175 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1178 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1181 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1187 lineStepDistance = -1;
1191 double candidateLength;
1193 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1197 if (
pal->isCanceled() )
1203 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1204 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1206 if ( currentDistanceAlongLine < 0 )
1214 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1217 cost = candidateLength / labelWidth;
1223 cost = ( 1 - cost ) / 100;
1227 double textAnchorPoint = 0;
1228 switch ( textPoint )
1231 textAnchorPoint = currentDistanceAlongLine;
1234 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1237 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1243 double costCenter = std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength;
1244 cost += costCenter / 1000;
1245 cost += initialCost;
1252 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1256 beta =
angle + M_PI_2;
1261 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1263 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1264 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1265 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1271 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1272 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 ) );
1279 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1280 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 ) );
1283 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1287 const double candidateCost = cost + 0.002;
1288 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 ) );
1294 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1301 currentDistanceAlongLine += lineStepDistance;
1305 if ( lineStepDistance < 0 )
1315 Q_ASSERT( metrics );
1317 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1318 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1320 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1324 labeledLineSegmentIsRightToLeft = !uprightOnly ? placement->labeledLineSegmentIsRightToLeft : placement->flippedCharacterPlacementToGetUprightLabels;
1326 if ( placement->graphemePlacement.empty() )
1329 auto it = placement->graphemePlacement.constBegin();
1330 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 );
1331 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1332 firstPosition->setPartId( it->graphemeIndex );
1335 while ( it != placement->graphemePlacement.constEnd() )
1337 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 );
1338 position->setPartId( it->graphemeIndex );
1341 previousPosition->
setNextPart( std::move( position ) );
1342 previousPosition = nextPosition;
1346 return firstPosition;
1358 const int characterCount = li->
count();
1359 if ( characterCount == 0 )
1365 double totalCharacterWidth = 0;
1366 for (
int i = 0; i < characterCount; ++i )
1369 std::unique_ptr< PointSet > expanded;
1370 double shapeLength = mapShape->
length();
1373 allowOverrun =
false;
1391 if ( totalCharacterWidth > shapeLength )
1393 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1404 if ( allowOverrun && overrun > 0 )
1407 expanded = mapShape->
clone();
1409 mapShape = expanded.get();
1410 shapeLength += 2 * overrun;
1415 flags = QgsLabeling::LinePlacementFlag::OnLine;
1416 const bool hasAboveBelowLinePlacement = flags & QgsLabeling::LinePlacementFlag::AboveLine || flags & QgsLabeling::LinePlacementFlag::BelowLine;
1418 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1419 bool positiveShapeHasNegativeDistance =
false;
1420 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1421 bool negativeShapeHasNegativeDistance =
false;
1422 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1425 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1426 mapShapeOffsetPositive = mapShape->
clone();
1427 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1428 mapShapeOffsetNegative = mapShape->
clone();
1429 if ( offsetDistance >= 0.0 || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
1431 if ( mapShapeOffsetPositive )
1432 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1433 positiveShapeHasNegativeDistance = offsetDistance < 0;
1434 if ( mapShapeOffsetNegative )
1435 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1436 negativeShapeHasNegativeDistance = offsetDistance > 0;
1441 if ( flags & QgsLabeling::LinePlacementFlag::AboveLine
1442 && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1445 flags |= QgsLabeling::LinePlacementFlag::BelowLine;
1447 else if ( flags & QgsLabeling::LinePlacementFlag::BelowLine
1448 && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1451 flags |= QgsLabeling::LinePlacementFlag::AboveLine;
1453 if ( mapShapeOffsetPositive )
1454 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1455 positiveShapeHasNegativeDistance = offsetDistance > 0;
1456 if ( mapShapeOffsetNegative )
1457 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1458 negativeShapeHasNegativeDistance = offsetDistance < 0;
1464 std::vector< std::unique_ptr< LabelPosition >> positions;
1465 std::unique_ptr< LabelPosition > backupPlacement;
1468 PointSet *currentMapShape =
nullptr;
1471 currentMapShape = mapShapeOffsetPositive.get();
1473 if ( offset ==
NoOffset && flags & QgsLabeling::LinePlacementFlag::OnLine )
1475 currentMapShape = mapShape;
1479 currentMapShape = mapShapeOffsetNegative.get();
1481 if ( !currentMapShape )
1485 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1489 double lineAnchorPoint = 0;
1490 if ( originalPoint && offset !=
NoOffset )
1495 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1501 lineAnchorPoint = totalDistance - lineAnchorPoint;
1504 if (
pal->isCanceled() )
1508 double delta = std::max( li->
characterHeight( 0 ) / 6, totalDistance / candidateTargetCount );
1511 double distanceAlongLineToStartCandidate = 0;
1512 bool singleCandidateOnly =
false;
1519 switch ( textPoint )
1522 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1525 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1528 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1534 singleCandidateOnly =
true;
1538 bool hasTestedFirstPlacement =
false;
1539 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1541 if ( singleCandidateOnly && hasTestedFirstPlacement )
1544 if (
pal->isCanceled() )
1547 hasTestedFirstPlacement =
true;
1549 bool labeledLineSegmentIsRightToLeft =
false;
1551 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly,
1552 onlyShowUprightLabels() && ( !singleCandidateOnly || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ) );
1554 if ( !labelPosition )
1560 bool isBackupPlacementOnly =
false;
1561 if ( flags & QgsLabeling::LinePlacementFlag::MapOrientation )
1563 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1564 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1566 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1569 if ( ( offset !=
NoOffset ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1572 isBackupPlacementOnly =
true;
1576 if ( ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1579 isBackupPlacementOnly =
true;
1585 backupPlacement.reset();
1588 const double angleDiff = labelPosition->angleDifferential();
1589 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1594 double cost = angleDiffAvg / 100;
1595 if ( cost < 0.0001 )
1599 double labelTextAnchor = 0;
1600 switch ( textPoint )
1603 labelTextAnchor = distanceAlongLineToStartCandidate;
1606 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1609 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1615 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1616 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1618 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1630 labelPosition->setCost( cost );
1632 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1637 while ( within && currentPos )
1640 currentPos = currentPos->
nextPart();
1650 if ( isBackupPlacementOnly )
1651 backupPlacement = std::move( p );
1653 positions.emplace_back( std::move( p ) );
1658 for ( std::unique_ptr< LabelPosition > &pos : positions )
1660 lPos.emplace_back( std::move( pos ) );
1663 if ( backupPlacement )
1664 lPos.emplace_back( std::move( backupPlacement ) );
1666 return positions.size();
1690 const double totalArea =
area();
1692 mapShape->
parent =
nullptr;
1694 if (
pal->isCanceled() )
1697 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1699 QgsDebugMsg( QStringLiteral(
"PAL split polygons resulted in:" ) );
1700 for (
PointSet *ps : shapes_final )
1706 std::size_t nbp = 0;
1708 if ( !shapes_final.isEmpty() )
1716 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1718 std::vector< OrientedConvexHullBoundingBox > boxes;
1719 boxes.reserve( shapes_final.size() );
1722 while ( !shapes_final.isEmpty() )
1724 PointSet *shape = shapes_final.takeFirst();
1728 boxes.emplace_back( box );
1734 if (
pal->isCanceled() )
1738 double densityY = densityX;
1745 std::size_t numberCandidatesGenerated = 0;
1760 double dx = densityX;
1761 double dy = densityY;
1762 if ( numTry == 0 && maxPolygonCandidates > 0 )
1765 const double boxArea = box.width * box.length;
1766 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1767 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1771 if (
pal->isCanceled() )
1772 return numberCandidatesGenerated;
1791 bool enoughPlace =
false;
1795 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1796 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1802 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1804 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1808 enoughPlace =
false;
1824 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1826 if ( box.alpha <= M_PI_4 )
1832 alpha = box.alpha - M_PI_2;
1835 else if ( box.length > box.width )
1837 alpha = box.alpha - M_PI_2;
1844 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1850 dlx = std::cos( beta ) * diago;
1851 dly = std::sin( beta ) * diago;
1853 double px0 = box.width / 2.0;
1854 double py0 = box.length / 2.0;
1856 px0 -= std::ceil( px0 / dx ) * dx;
1857 py0 -= std::ceil( py0 / dy ) * dy;
1859 for ( px = px0; px <= box.width; px += dx )
1861 if (
pal->isCanceled() )
1864 for ( py = py0; py <= box.length; py += dy )
1867 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1868 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1878 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1879 numberCandidatesGenerated++;
1890 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1892 lPos.emplace_back( std::move( potentialCandidate ) );
1893 numberCandidatesGenerated++;
1900 nbp = numberCandidatesGenerated;
1901 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1912 while ( numTry < maxTry );
1914 nbp = numberCandidatesGenerated;
1928 std::size_t candidatesCreated = 0;
1988 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1990 return candidatesCreated;
1994 return candidatesCreated;
1998 const double ringLength = ring->
length();
1999 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2001 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2004 const double delta = ringLength / targetPolygonCandidates;
2007 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2008 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2009 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2012 const double labelAngle = 0;
2014 std::size_t i = lPos.size();
2022 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2024 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
2025 if ( candidate->intersects( preparedBuffer.get() ) )
2043 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
2044 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2045 candidate->setCost( centroidCost );
2047 lPos.emplace_back( std::move( candidate ) );
2048 candidatesCreated++;
2053 double startSegmentX,
double startSegmentY,
double,
double,
2054 double endSegmentX,
double endSegmentY,
double,
double )
2057 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2067 else if (
angle <= 85 )
2071 else if (
angle <= 90 )
2077 else if (
angle <= 95 )
2082 else if (
angle <= 175 )
2086 else if (
angle <= 180 )
2092 else if (
angle <= 185 )
2097 else if (
angle <= 265 )
2101 else if (
angle <= 270 )
2106 else if (
angle <= 275 )
2111 else if (
angle <= 355 )
2121 return !
pal->isCanceled();
2124 return candidatesCreated;
2129 std::vector< std::unique_ptr< LabelPosition > > lPos;
2149 case GEOS_LINESTRING:
2163 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2164 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2172 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2173 std::fabs(
ymax -
ymin ) < labelHeight ) )
2180 std::size_t created = 0;
2238 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2240 double sizeCost = 0;
2241 if ( geomType == GEOS_LINESTRING )
2243 const double l =
length();
2246 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2247 if ( l >= bbox_length / 4 )
2250 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2252 else if ( geomType == GEOS_POLYGON )
2254 const double a =
area();
2257 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2258 if ( a >= bbox_area / 16 )
2261 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2267 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2269 pos->setCost( pos->cost() + sizeCost / 100 );
2280 const double x1first =
x.front();
2281 const double x1last =
x.back();
2282 const double x2first = p2->
x.front();
2283 const double x2last = p2->
x.back();
2284 const double y1first =
y.front();
2285 const double y1last =
y.back();
2286 const double y2first = p2->
y.front();
2287 const double y2last = p2->
y.back();
2295 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2301 const double p2otherX = p2startTouches ? x2last : x2first;
2302 const double p2otherY = p2startTouches ? y2last : y2first;
2306 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2307 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2312 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2314 catch ( GEOSException &e )
2316 qWarning(
"GEOS exception: %s", e.what() );
2326 if ( !other->
mGeos )
2332 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2333 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2334 GEOSGeometry *geoms[2] = { g1, g2 };
2335 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2338 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2346 mGeos = gTmp.release();
2355 catch ( GEOSException &e )
2357 qWarning(
"GEOS exception: %s", e.what() );
2378 bool result =
false;
2382 case Qgis::UpsideDownLabelHandling::FlipUpsideDownLabels:
2385 case Qgis::UpsideDownLabelHandling::AllowUpsideDownWhenRotationIsDefined:
2392 case Qgis::UpsideDownLabelHandling::AlwaysAllowUpsideDown:
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
@ MiddleLeft
Label on left of point.
@ TopRight
Label on top-right of point.
@ MiddleRight
Label on right of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopMiddle
Label directly above point.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ TopLeft
Label on top-left of point.
@ BottomMiddle
Label directly below point.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
static double normalizedAngle(double angle) SIP_HOLDGIL
Ensures that an angle is in the range 0 <= angle < 2 pi.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
static GEOSContextHandle_t getGEOSHandler()
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
double overrunSmoothDistance() const
Returns the distance (in map units) with which the ends of linear features are averaged over when cal...
double fixedAngle() const
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true)
const QSizeF & symbolSize() const
Returns the size of the rendered symbol associated with this feature, if applicable.
QVector< Qgis::LabelPredefinedPointPosition > predefinedPositionOrder() const
Returns the priority ordered list of predefined positions for label candidates.
QgsLabeling::PolygonPlacementFlags polygonPlacementFlags() const
Returns the polygon placement flags, which dictate how polygon labels can be placed.
QgsPointXY positionOffset() const
Applies only to "offset from point" placement strategy.
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
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.
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
void setAnchorPosition(const QgsPointXY &anchorPosition)
In case of quadrand or aligned positioning, this is set to the anchor point.
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)
double overrunDistance() const
Returns the permissible distance (in map units) which labels are allowed to overrun the start or end ...
double priority() const
Returns the feature's labeling priority.
QgsGeometry permissibleZone() const
Returns the label's permissible zone geometry.
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
QgsLabelLineSettings::AnchorTextPoint lineAnchorTextPoint() const
Returns the line anchor text point, which dictates which part of the label text should be placed at t...
const 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.
Line string geometry type, with support for z-dimension and m-values.
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
The QgsMargins class defines the four margins of a rectangle.
double top() const
Returns the top margin.
double right() const
Returns the right margin.
double bottom() const
Returns the bottom margin.
double left() const
Returns the left margin.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A class to represent a 2D point.
Contains precalculated properties regarding text metrics for text to be renderered at a later stage.
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 height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
LabelLineDirection
Controls behavior of curved text with respect to line directions.
@ FollowLineDirection
Curved text placement will respect the line direction and ignore painter orientation.
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
static CurvePlacementProperties * generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const double *x, const double *y, int numPoints, const std::vector< double > &pathDistances, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, bool uprightOnly=true)
Calculates curved text placement properties.
Main class to handle feature.
std::size_t createCandidatesAroundPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate candidates for point feature, located around a specified point.
std::size_t createCandidatesOutsidePolygon(std::vector< std::unique_ptr< LabelPosition > > &lPos, Pal *pal)
Generate candidates outside of polygon features.
bool hasFixedRotation() const
Returns true if the feature's label has a fixed rotation.
double getLabelHeight(double angle=0.0) const
Returns the height of the label, optionally taking an angle into account.
QList< FeaturePart * > mHoles
double getLabelDistance() const
Returns the distance from the anchor point to the label.
~FeaturePart() override
Deletes the feature.
bool hasFixedPosition() const
Returns true if the feature's label has a fixed position.
std::size_t createCandidatesForPolygon(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate candidates for polygon features.
void setTotalRepeats(int repeats)
Returns the total number of repeating labels associated with this label.
std::size_t maximumPolygonCandidates() const
Returns the maximum number of polygon candidates to generate for this feature.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
std::size_t createCandidatesAlongLineNearStraightSegments(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate candidates for line feature, by trying to place candidates towards the middle of the longest...
bool hasSameLabelFeatureAs(FeaturePart *part) const
Tests whether this feature part belongs to the same QgsLabelFeature as another feature part.
double fixedAngle() const
Returns the fixed angle for the feature's label.
std::size_t maximumLineCandidates() const
Returns the maximum number of line candidates to generate for this feature.
std::size_t createHorizontalCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate horizontal candidates for line feature.
std::size_t createCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate candidates for line feature.
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged).
std::size_t createCurvedCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate curved candidates for line features.
bool onlyShowUprightLabels() const
Returns true if feature's label must be displayed upright.
std::size_t createCandidatesOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate one candidate over or offset the specified point.
std::unique_ptr< LabelPosition > createCandidatePointOnSurface(PointSet *mapShape)
Creates a single candidate using the "point on sruface" algorithm.
double getLabelWidth(double angle=0.0) const
Returns the width of the label, optionally taking an angle into account.
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 rad).
Quadrant
Position of label candidate relative to feature.
void setNextPart(std::unique_ptr< LabelPosition > next)
Sets the next part of this label position (i.e.
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)
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Represents the minimum area, oriented bounding box surrounding a convex hull.