63 for (
int i = 0; i <
mHoles.count(); i++ )
65 mHoles.at( i )->holeOf =
this;
73 , mTotalRepeats( other.mTotalRepeats )
74 , mCachedMaxLineCandidates( other.mCachedMaxLineCandidates )
75 , mCachedMaxPolygonCandidates( other.mCachedMaxPolygonCandidates )
79 mHoles << new FeaturePart( *hole );
80 mHoles.last()->holeOf = this;
94 const GEOSCoordSequence *coordSeq =
nullptr;
97 type = GEOSGeomTypeId_r( geosctxt, geom );
99 if (
type == GEOS_POLYGON )
101 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
103 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
105 for (
int i = 0; i < numHoles; ++i )
107 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
119 geom = GEOSGetExteriorRing_r( geosctxt, geom );
128 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
129 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
132 xmin =
ymin = std::numeric_limits<double>::max();
133 xmax =
ymax = std::numeric_limits<double>::lowest();
140#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
141 GEOSCoordSeq_copyToArrays_r( geosctxt, coordSeq,
x.data(),
y.data(),
nullptr,
nullptr );
142 auto xminmax = std::minmax_element(
x.begin(),
x.end() );
143 xmin = *xminmax.first;
144 xmax = *xminmax.second;
145 auto yminmax = std::minmax_element(
y.begin(),
y.end() );
146 ymin = *yminmax.first;
147 ymax = *yminmax.second;
149 for (
int i = 0; i <
nbPoints; ++i )
151 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
174 return mLF->layer()->maximumPointLabelCandidates();
179 if ( mCachedMaxLineCandidates > 0 )
180 return mCachedMaxLineCandidates;
182 const double l =
length();
185 const std::size_t candidatesForLineLength =
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * l ) );
186 const std::size_t maxForLayer =
mLF->layer()->maximumLineLabelCandidates();
187 if ( maxForLayer == 0 )
188 mCachedMaxLineCandidates = candidatesForLineLength;
190 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
194 mCachedMaxLineCandidates = 1;
196 return mCachedMaxLineCandidates;
201 if ( mCachedMaxPolygonCandidates > 0 )
202 return mCachedMaxPolygonCandidates;
204 const double a =
area();
207 const std::size_t candidatesForArea =
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() * a ) );
208 const std::size_t maxForLayer =
mLF->layer()->maximumPolygonLabelCandidates();
209 if ( maxForLayer == 0 )
210 mCachedMaxPolygonCandidates = candidatesForArea;
212 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
216 mCachedMaxPolygonCandidates = 1;
218 return mCachedMaxPolygonCandidates;
233 int connectedFeatureId =
mLF->layer()->connectedFeatureId(
mLF->id() );
234 return connectedFeatureId >= 0 && connectedFeatureId ==
mLF->layer()->connectedFeatureId( part->
featureId() );
240 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
242 if ( quadOffsetX < 0 )
244 if ( quadOffsetY < 0 )
248 else if ( quadOffsetY > 0 )
257 else if ( quadOffsetX > 0 )
259 if ( quadOffsetY < 0 )
263 else if ( quadOffsetY > 0 )
274 if ( quadOffsetY < 0 )
278 else if ( quadOffsetY > 0 )
291 return mTotalRepeats;
305 double cost = 0.00005;
306 int id = lPos.size();
308 double xdiff = -labelW / 2.0;
309 double ydiff = -labelH / 2.0;
313 double lx =
x + xdiff;
314 double ly =
y + ydiff;
316 if (
mLF->permissibleZonePrepared() )
334 double cost = 0.0001;
335 int id = lPos.size();
337 double xdiff = -labelW / 2.0;
338 double ydiff = -labelH / 2.0;
344 xdiff += labelW / 2.0 *
mLF->quadOffset().x();
348 ydiff += labelH / 2.0 *
mLF->quadOffset().y();
351 if ( !
mLF->hasFixedPosition() )
355 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
356 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
368 ydiff +=
mLF->quadOffset().y() *
mLF->distLabel();
372 xdiff +=
mLF->quadOffset().x() *
mLF->distLabel();
376 xdiff +=
mLF->quadOffset().x() * M_SQRT1_2 *
mLF->distLabel();
377 ydiff +=
mLF->quadOffset().y() * M_SQRT1_2 *
mLF->distLabel();
384 xdiff +=
mLF->positionOffset().x();
388 ydiff +=
mLF->positionOffset().y();
392 double lx =
x + xdiff;
393 double ly =
y + ydiff;
395 if (
mLF->permissibleZonePrepared() )
416 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
417 unsigned int nPoints = 0;
418 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
421 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
428 catch ( QgsGeosException &e )
430 qWarning(
"GEOS exception: %s", e.what() );
438void createCandidateAtOrderedPositionOverPoint(
double &labelX,
double &labelY,
Qgis::LabelQuadrantPosition &quadrant,
double x,
double y,
double labelWidth,
double labelHeight,
Qgis::LabelPredefinedPointPosition position,
double distanceToLabel,
const QgsMargins &visualMargin,
double symbolWidthOffset,
double symbolHeightOffset,
double angle )
449 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
450 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
456 deltaX = -labelWidth / 4.0 - visualMargin.
left();
457 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
463 deltaX = -labelWidth / 2.0;
464 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
470 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
471 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
477 deltaX = - visualMargin.
left() + symbolWidthOffset;
478 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
484 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
485 deltaY = -labelHeight / 2.0;
491 deltaX = -visualMargin.
left() + symbolWidthOffset;
492 deltaY = -labelHeight / 2.0;
498 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
499 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
505 deltaX = -labelWidth / 4.0 - visualMargin.
left();
506 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
512 deltaX = -labelWidth / 2.0;
513 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
519 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
520 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
526 deltaX = -visualMargin.
left() + symbolWidthOffset;
527 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
534 deltaX = -labelWidth / 2.0;
535 deltaY = -labelHeight / 2.0;
541 QTransform transformRotation;
542 transformRotation.rotate( angle * 180 / M_PI );
543 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
546 double referenceX = std::cos( alpha ) * distanceToLabel + x;
547 double referenceY = std::sin( alpha ) * distanceToLabel + y;
549 labelX = referenceX + deltaX;
550 labelY = referenceY + deltaY;
555 const QVector< Qgis::LabelPredefinedPointPosition > positions =
mLF->predefinedPositionOrder();
559 const double maximumDistanceToLabel =
mLF->maximumDistance();
563 double symbolWidthOffset{ 0 };
564 double symbolHeightOffset{ 0 };
569 if (
mLF->feature().geometry().constParts().hasNext() )
577 symbolWidthOffset =
mLF->symbolSize().width() / 2.0;
578 symbolHeightOffset =
mLF->symbolSize().height() / 2.0;
582 int candidatesPerPosition = 1;
583 double distanceStep = 0;
584 if ( maximumDistanceToLabel > distanceToLabel && !
qgsDoubleNear( maximumDistanceToLabel, 0 ) )
588 const double rayLength = maximumDistanceToLabel - distanceToLabel;
591 candidatesPerPosition = std::max( 2,
static_cast< int >( std::ceil(
mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * 1.5 * rayLength ) ) );
592 distanceStep = rayLength / ( candidatesPerPosition - 1 );
595 double cost = 0.0001;
596 std::size_t i = lPos.size();
599 const std::size_t maxNumberCandidates =
mLF->layer()->maximumPointLabelCandidates() * candidatesPerPosition;
600 std::size_t created = 0;
602 auto addCandidate = [
this,
x,
y, labelWidth, labelHeight, angle, visualMargin, symbolWidthOffset, symbolHeightOffset, &created, &cost, &lPos, &i, maxNumberCandidates](
Qgis::LabelPredefinedPointPosition position,
double distance ) ->
bool
608 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distance, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
617 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
623 switch ( prioritization )
631 double currentDistance = distanceToLabel;
632 for (
int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
634 if ( !addCandidate( position, currentDistance ) )
643 double currentDistance = distanceToLabel;
644 for (
int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
648 if ( !addCandidate( position, currentDistance ) )
663 const double maximumDistanceToLabel =
mLF->maximumDistance();
667 QTransform transformRotation;
668 transformRotation.rotate( angle * 180 / M_PI );
670 int rayCount =
static_cast< int >(
mLF->layer()->maximumPointLabelCandidates() );
674 int candidatesPerRay = 0;
675 double rayStepDelta = 0;
676 if ( maximumDistanceToLabel > distanceToLabel && !
qgsDoubleNear( maximumDistanceToLabel, 0 ) )
680 const double rayLength = maximumDistanceToLabel - distanceToLabel;
683 candidatesPerRay = std::max( 2,
static_cast< int >( std::ceil(
mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * 1.5 * rayLength ) ) );
684 rayStepDelta = rayLength / ( candidatesPerRay - 1 );
688 candidatesPerRay = 1;
691 int id =
static_cast< int >( lPos.size() );
693 const double candidateAngleIncrement = 2 * M_PI /
static_cast< double >( rayCount );
696 constexpr double a90 = M_PI_2;
697 constexpr double a180 = M_PI;
698 constexpr double a270 = a180 + a90;
699 constexpr double a360 = 2 * M_PI;
701 double gamma1, gamma2;
703 if ( distanceToLabel > 0 )
705 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
706 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
710 gamma1 = gamma2 = a90 / 3.0;
713 if ( gamma1 > a90 / 3.0 )
716 if ( gamma2 > a90 / 3.0 )
719 std::size_t numberCandidatesGenerated = 0;
721 double angleToCandidate = M_PI_4;
723 int integerRayCost = 0;
724 int integerRayCostIncrement = 2;
726 for (
int rayIndex = 0; rayIndex < rayCount; ++rayIndex, angleToCandidate += candidateAngleIncrement )
731 if ( angleToCandidate > a360 )
732 angleToCandidate -= a360;
734 double rayDistance = distanceToLabel;
736 constexpr double RAY_ANGLE_COST_FACTOR = 0.0020;
740 const double scaledRayAngleCost = RAY_ANGLE_COST_FACTOR *
static_cast< double >( integerRayCost )
741 /
static_cast< double >( rayCount - 1 );
743 for (
int j = 0; j < candidatesPerRay; ++j, rayDistance += rayStepDelta )
747 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
749 deltaX = rayDistance;
750 double iota = ( angleToCandidate + gamma1 );
751 if ( iota > a360 - gamma1 )
754 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
758 else if ( angleToCandidate < a90 - gamma2 )
760 deltaX = rayDistance * std::cos( angleToCandidate );
761 deltaY = rayDistance * std::sin( angleToCandidate );
764 else if ( angleToCandidate < a90 + gamma2 )
766 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
767 deltaY = rayDistance;
770 else if ( angleToCandidate < a180 - gamma1 )
772 deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
773 deltaY = rayDistance * std::sin( angleToCandidate );
776 else if ( angleToCandidate < a180 + gamma1 )
778 deltaX = -rayDistance - labelWidth;
779 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
782 else if ( angleToCandidate < a270 - gamma2 )
784 deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
785 deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
788 else if ( angleToCandidate < a270 + gamma2 )
790 deltaY = -rayDistance - labelHeight;
791 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
794 else if ( angleToCandidate < a360 )
796 deltaX = rayDistance * std::cos( angleToCandidate );
797 deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
801 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
803 double labelX =
x + deltaX;
804 double labelY =
y + deltaY;
811 cost = 0.0001 + scaledRayAngleCost;
818 cost += j * RAY_ANGLE_COST_FACTOR + RAY_ANGLE_COST_FACTOR / rayCount;
821 if (
mLF->permissibleZonePrepared() )
831 numberCandidatesGenerated++;
834 integerRayCost += integerRayCostIncrement;
836 if ( integerRayCost ==
static_cast< int >( rayCount ) )
838 integerRayCost =
static_cast< int >( rayCount ) - 1;
839 integerRayCostIncrement = -2;
841 else if ( integerRayCost >
static_cast< int >( rayCount ) )
843 integerRayCost =
static_cast< int >( rayCount ) - 2;
844 integerRayCostIncrement = -2;
848 return numberCandidatesGenerated;
855 double shapeLength = mapShape->
length();
866 std::size_t candidates = 0;
872 if ( candidates < candidateTargetCount )
887 std::vector< double > &
x = line->
x;
888 std::vector< double > &
y = line->
y;
890 std::vector< double > segmentLengths(
nbPoints - 1 );
891 std::vector< double >distanceToSegment(
nbPoints );
893 double totalLineLength = 0.0;
894 for (
int i = 0; i < line->
nbPoints - 1; i++ )
897 distanceToSegment[i] = 0;
899 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
902 totalLineLength += segmentLengths[i];
904 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
907 double lineStepDistance = 0;
909 const double lineAnchorPoint = totalLineLength *
mLF->lineAnchorPercent();
910 double currentDistanceAlongLine = lineStepDistance;
911 switch (
mLF->lineAnchorType() )
914 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
918 currentDistanceAlongLine = lineAnchorPoint;
919 lineStepDistance = -1;
925 double candidateCenterX, candidateCenterY;
927 while ( currentDistanceAlongLine <= totalLineLength )
929 if (
pal->isCanceled() )
934 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
937 double cost = totalLineLength > 0 ? std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength : 0;
944 labelX = candidateCenterX;
947 labelX = candidateCenterX - labelWidth / 2;
950 labelX = candidateCenterX - labelWidth;
958 currentDistanceAlongLine += lineStepDistance;
962 if ( lineStepDistance < 0 )
979 QVector< int > extremeAngleNodes;
982 std::vector< double > &
x = line->
x;
983 std::vector< double > &
y = line->
y;
987 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
989 double x1 =
x[i - 1];
991 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
992 double y1 =
y[i - 1];
994 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
999 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
1003 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
1004 extremeAngleNodes << i;
1006 extremeAngleNodes << numberNodes - 1;
1008 if ( extremeAngleNodes.isEmpty() )
1015 std::vector< double > segmentLengths( numberNodes - 1 );
1016 std::vector< double > distanceToSegment( numberNodes );
1017 double totalLineLength = 0.0;
1018 QVector< double > straightSegmentLengths;
1019 QVector< double > straightSegmentAngles;
1020 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
1021 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
1022 double currentStraightSegmentLength = 0;
1023 double longestSegmentLength = 0;
1024 double segmentStartX =
x[0];
1025 double segmentStartY =
y[0];
1026 for (
int i = 0; i < numberNodes - 1; i++ )
1029 distanceToSegment[i] = 0;
1031 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1034 totalLineLength += segmentLengths[i];
1035 if ( extremeAngleNodes.contains( i ) )
1038 straightSegmentLengths << currentStraightSegmentLength;
1040 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
1041 currentStraightSegmentLength = 0;
1042 segmentStartX =
x[i];
1043 segmentStartY =
y[i];
1045 currentStraightSegmentLength += segmentLengths[i];
1047 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1048 straightSegmentLengths << currentStraightSegmentLength;
1050 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
1051 const double lineAnchorPoint = totalLineLength *
mLF->lineAnchorPercent();
1053 if ( totalLineLength < labelWidth )
1061 double lineStepDistance = ( totalLineLength - labelWidth );
1062 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1064 double distanceToEndOfSegment = 0.0;
1065 int lastNodeInSegment = 0;
1067 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
1069 currentStraightSegmentLength = straightSegmentLengths.at( i );
1070 double currentSegmentAngle = straightSegmentAngles.at( i );
1071 lastNodeInSegment = extremeAngleNodes.at( i );
1072 double distanceToStartOfSegment = distanceToEndOfSegment;
1073 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
1074 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
1076 if ( currentStraightSegmentLength < labelWidth )
1080 double currentDistanceAlongLine = distanceToStartOfSegment;
1081 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1082 double candidateLength = 0.0;
1088 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
1089 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
1091 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
1093 if (
pal->isCanceled() )
1099 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1100 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1108 cost = candidateLength / labelWidth;
1114 cost = ( 1 - cost ) / 100;
1117 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1118 double labelTextAnchor = 0;
1119 switch ( textPoint )
1122 labelTextAnchor = currentDistanceAlongLine;
1125 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1128 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1135 const bool placementIsFlexible =
mLF->lineAnchorPercent() > 0.1 &&
mLF->lineAnchorPercent() < 0.9;
1137 if ( placementIsFlexible )
1140 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1141 cost += costCenter * 0.0005;
1149 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1150 cost += costLineCenter * 0.0005;
1153 if ( placementIsFlexible )
1155 cost += segmentCost * 0.0005;
1156 cost += segmentAngleCost * 0.0001;
1164 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1168 beta = angle + M_PI_2;
1173 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1181 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) )
1183 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1189 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) )
1191 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1197 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle ) )
1199 const double candidateCost = cost + 0.002;
1213 currentDistanceAlongLine += lineStepDistance;
1236 std::vector< double > &
x = line->
x;
1237 std::vector< double > &
y = line->
y;
1239 std::vector< double > segmentLengths(
nbPoints - 1 );
1240 std::vector< double >distanceToSegment(
nbPoints );
1242 double totalLineLength = 0.0;
1243 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1246 distanceToSegment[i] = 0;
1248 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1251 totalLineLength += segmentLengths[i];
1253 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1255 double lineStepDistance = ( totalLineLength - labelWidth );
1256 double currentDistanceAlongLine = 0;
1262 if ( totalLineLength > labelWidth )
1264 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1268 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1269 lineStepDistance = -1;
1270 totalLineLength = labelWidth;
1275 currentDistanceAlongLine = std::numeric_limits< double >::max();
1278 const double lineAnchorPoint = totalLineLength * std::min( 0.99,
mLF->lineAnchorPercent() );
1280 switch (
mLF->lineAnchorType() )
1286 switch ( textPoint )
1289 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1292 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1295 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1301 lineStepDistance = -1;
1305 double candidateLength;
1307 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1311 if (
pal->isCanceled() )
1317 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1318 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1320 if ( currentDistanceAlongLine < 0 )
1330 cost = candidateLength / labelWidth;
1336 cost = ( 1 - cost ) / 100;
1340 double textAnchorPoint = 0;
1341 switch ( textPoint )
1344 textAnchorPoint = currentDistanceAlongLine;
1347 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1350 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1356 double costCenter = totalLineLength > 0 ? std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength : 0;
1357 cost += costCenter / 1000;
1358 cost += initialCost;
1365 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1369 beta = angle + M_PI_2;
1374 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1382 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) )
1384 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1390 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) )
1392 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1398 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle ) )
1400 const double candidateCost = cost + 0.002;
1414 currentDistanceAlongLine += lineStepDistance;
1418 if ( lineStepDistance < 0 )
1428 Q_ASSERT( metrics );
1430 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1431 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1433 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1439 if ( placement->graphemePlacement.empty() )
1442 auto it = placement->graphemePlacement.constBegin();
1444 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1445 firstPosition->setPartId( it->graphemeIndex );
1448 while ( it != placement->graphemePlacement.constEnd() )
1451 position->setPartId( it->graphemeIndex );
1454 previousPosition->
setNextPart( std::move( position ) );
1455 previousPosition = nextPosition;
1459 return firstPosition;
1471 const int characterCount = li->
count();
1472 if ( characterCount == 0 )
1478 double totalCharacterWidth = 0;
1479 for (
int i = 0; i < characterCount; ++i )
1482 std::unique_ptr< PointSet > expanded;
1483 double shapeLength = mapShape->
length();
1486 allowOverrun =
false;
1492 switch (
mLF->lineAnchorType() )
1495 overrun = std::min(
mLF->overrunDistance(), totalCharacterWidth * 0.95 );
1500 overrun = std::max(
mLF->overrunDistance(), totalCharacterWidth * 1.05 );
1504 if ( totalCharacterWidth > shapeLength )
1506 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1517 if ( allowOverrun && overrun > 0 )
1520 expanded = mapShape->
clone();
1521 expanded->extendLineByDistance( overrun, overrun,
mLF->overrunSmoothDistance() );
1522 mapShape = expanded.get();
1523 shapeLength += 2 * overrun;
1531 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1532 bool positiveShapeHasNegativeDistance =
false;
1533 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1534 bool negativeShapeHasNegativeDistance =
false;
1535 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1539 mapShapeOffsetPositive = mapShape->
clone();
1541 mapShapeOffsetNegative = mapShape->
clone();
1544 if ( mapShapeOffsetPositive )
1545 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1546 positiveShapeHasNegativeDistance = offsetDistance < 0;
1547 if ( mapShapeOffsetNegative )
1548 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1549 negativeShapeHasNegativeDistance = offsetDistance > 0;
1566 if ( mapShapeOffsetPositive )
1567 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1568 positiveShapeHasNegativeDistance = offsetDistance > 0;
1569 if ( mapShapeOffsetNegative )
1570 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1571 negativeShapeHasNegativeDistance = offsetDistance < 0;
1577 std::vector< std::unique_ptr< LabelPosition >> positions;
1578 std::unique_ptr< LabelPosition > backupPlacement;
1581 PointSet *currentMapShape =
nullptr;
1584 currentMapShape = mapShapeOffsetPositive.get();
1588 currentMapShape = mapShape;
1592 currentMapShape = mapShapeOffsetNegative.get();
1594 if ( !currentMapShape )
1598 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1602 double lineAnchorPoint = 0;
1603 if ( originalPoint && offset !=
NoOffset )
1608 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1612 lineAnchorPoint = totalDistance *
mLF->lineAnchorPercent();
1614 lineAnchorPoint = totalDistance - lineAnchorPoint;
1617 if (
pal->isCanceled() )
1621 double delta = std::max( li->
characterHeight( 0 ) / 6, totalDistance / candidateTargetCount );
1624 double distanceAlongLineToStartCandidate = 0;
1625 bool singleCandidateOnly =
false;
1626 switch (
mLF->lineAnchorType() )
1632 switch ( textPoint )
1635 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1638 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1641 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1647 singleCandidateOnly =
true;
1651 bool hasTestedFirstPlacement =
false;
1652 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1654 if ( singleCandidateOnly && hasTestedFirstPlacement )
1657 if (
pal->isCanceled() )
1660 hasTestedFirstPlacement =
true;
1662 bool labeledLineSegmentIsRightToLeft =
false;
1668 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly, curvedTextFlags );
1669 if ( !labelPosition )
1675 bool isBackupPlacementOnly =
false;
1678 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1679 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1681 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1687 isBackupPlacementOnly =
true;
1694 isBackupPlacementOnly =
true;
1700 backupPlacement.reset();
1703 const double angleDiff = labelPosition->angleDifferential();
1704 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1708 const bool anchorIsFlexiblePlacement = !singleCandidateOnly &&
mLF->lineAnchorPercent() > 0.1 &&
mLF->lineAnchorPercent() < 0.9;
1709 double cost = angleDiffAvg / 100;
1710 if ( cost < 0.0001 )
1714 double labelTextAnchor = 0;
1715 switch ( textPoint )
1718 labelTextAnchor = distanceAlongLineToStartCandidate;
1721 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1724 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1730 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1731 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1733 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1745 labelPosition->setCost( cost );
1747 auto p = std::make_unique< LabelPosition >( *labelPosition );
1748 if ( p &&
mLF->permissibleZonePrepared() )
1752 while ( within && currentPos )
1755 currentPos = currentPos->
nextPart();
1765 if ( isBackupPlacementOnly )
1766 backupPlacement = std::move( p );
1768 positions.emplace_back( std::move( p ) );
1773 for ( std::unique_ptr< LabelPosition > &pos : positions )
1775 lPos.emplace_back( std::move( pos ) );
1778 if ( backupPlacement )
1779 lPos.emplace_back( std::move( backupPlacement ) );
1781 return positions.size();
1801 const std::size_t maxPolygonCandidates =
mLF->layer()->maximumPolygonLabelCandidates();
1802 const std::size_t targetPolygonCandidates = maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates,
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() *
area() ) ) )
1805 const double totalArea =
area();
1807 mapShape->
parent =
nullptr;
1809 if (
pal->isCanceled() )
1812 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1814 QgsDebugMsgLevel( QStringLiteral(
"PAL split polygons resulted in:" ), 2 );
1815 for (
PointSet *ps : shapes_final )
1821 std::size_t nbp = 0;
1823 if ( !shapes_final.isEmpty() )
1831 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1833 std::vector< OrientedConvexHullBoundingBox > boxes;
1834 boxes.reserve( shapes_final.size() );
1837 while ( !shapes_final.isEmpty() )
1839 PointSet *shape = shapes_final.takeFirst();
1843 boxes.emplace_back( box );
1849 if (
pal->isCanceled() )
1852 double densityX = 1.0 / std::sqrt(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() );
1853 double densityY = densityX;
1858 int maxTry =
mLF->permissibleZonePrepared() ? 7 : 10;
1860 std::size_t numberCandidatesGenerated = 0;
1875 double dx = densityX;
1876 double dy = densityY;
1877 if ( numTry == 0 && maxPolygonCandidates > 0 )
1880 const double boxArea = box.width * box.length;
1881 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1882 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1886 if (
pal->isCanceled() )
1887 return numberCandidatesGenerated;
1898 if (
mLF->permissibleZone().boundingBox().width() < labelWidth ||
1899 mLF->permissibleZone().boundingBox().height() < labelHeight )
1906 bool enoughPlace =
false;
1910 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1911 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1917 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1919 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1923 enoughPlace =
false;
1939 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1941 if ( box.alpha <= M_PI_4 )
1947 alpha = box.alpha - M_PI_2;
1950 else if ( box.length > box.width )
1952 alpha = box.alpha - M_PI_2;
1959 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1965 dlx = std::cos( beta ) * diago;
1966 dly = std::sin( beta ) * diago;
1968 double px0 = box.width / 2.0;
1969 double py0 = box.length / 2.0;
1971 px0 -= std::ceil( px0 / dx ) * dx;
1972 py0 -= std::ceil( py0 / dy ) * dy;
1974 for ( px = px0; px <= box.width; px += dx )
1976 if (
pal->isCanceled() )
1979 for ( py = py0; py <= box.length; py += dy )
1982 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1983 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1988 if (
mLF->permissibleZonePrepared() )
1994 numberCandidatesGenerated++;
2007 lPos.emplace_back( std::move( potentialCandidate ) );
2008 numberCandidatesGenerated++;
2015 nbp = numberCandidatesGenerated;
2016 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
2027 while ( numTry < maxTry );
2029 nbp = numberCandidatesGenerated;
2042 const std::size_t maxPolygonCandidates =
mLF->layer()->maximumPolygonLabelCandidates();
2043 std::size_t candidatesCreated = 0;
2105 return candidatesCreated;
2109 return candidatesCreated;
2113 const double ringLength = ring->
length();
2114 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2115 const std::size_t candidatesForArea =
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() * circleArea ) );
2116 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2119 const double delta = ringLength / targetPolygonCandidates;
2122 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2123 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2124 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2127 const double labelAngle = 0;
2129 std::size_t i = lPos.size();
2137 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2140 if ( candidate->intersects( preparedBuffer.get() ) )
2158 const double centroidDistance = candidate->getDistanceToPoint( cx, cy,
false );
2159 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2160 candidate->setCost( centroidCost );
2162 lPos.emplace_back( std::move( candidate ) );
2163 candidatesCreated++;
2168 double startSegmentX,
double startSegmentY,
double,
double,
2169 double endSegmentX,
double endSegmentY,
double,
double )
2172 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2177 if ( angle >= 0 && angle <= 5 )
2182 else if ( angle <= 85 )
2186 else if ( angle <= 90 )
2192 else if ( angle <= 95 )
2197 else if ( angle <= 175 )
2201 else if ( angle <= 180 )
2207 else if ( angle <= 185 )
2212 else if ( angle <= 265 )
2216 else if ( angle <= 270 )
2221 else if ( angle <= 275 )
2226 else if ( angle <= 355 )
2236 return !
pal->isCanceled();
2239 return candidatesCreated;
2244 std::vector< std::unique_ptr< LabelPosition > > lPos;
2245 double angleInRadians =
mLF->hasFixedAngle() ?
mLF->fixedAngle() : 0.0;
2247 if (
mLF->hasFixedPosition() )
2264 case GEOS_LINESTRING:
2267 else if (
mLF->layer()->isCurved() )
2287 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2288 std::fabs(
ymax -
ymin ) < labelHeight ) )
2295 std::size_t created = 0;
2298 switch (
mLF->layer()->arrangement() )
2353 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2355 double sizeCost = 0;
2356 if ( geomType == GEOS_LINESTRING )
2358 const double l =
length();
2361 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2362 if ( l >= bbox_length / 4 )
2365 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2367 else if ( geomType == GEOS_POLYGON )
2369 const double a =
area();
2372 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2373 if ( a >= bbox_area / 16 )
2376 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2382 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2384 pos->setCost( pos->cost() + sizeCost / 100 );
2395 const double x1first =
x.front();
2396 const double x1last =
x.back();
2397 const double x2first = p2->
x.front();
2398 const double x2last = p2->
x.back();
2399 const double y1first =
y.front();
2400 const double y1last =
y.back();
2401 const double y2first = p2->
y.front();
2402 const double y2last = p2->
y.back();
2410 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2416 const double p2otherX = p2startTouches ? x2last : x2first;
2417 const double p2otherY = p2startTouches ? y2last : y2first;
2423#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
2424 return ( GEOSPreparedIntersectsXY_r( geosctxt,
preparedGeom(), p2otherX, p2otherY ) != 1 );
2426 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2427 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2429 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2432 catch ( QgsGeosException &e )
2434 qWarning(
"GEOS exception: %s", e.what() );
2444 if ( !other->
mGeos )
2453 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2456 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2464 mGeos = gTmp.release();
2473 catch ( QgsGeosException &e )
2475 qWarning(
"GEOS exception: %s", e.what() );
2483 if (
mLF->alwaysShow() )
2491 return mLF->priority() >= 0 ?
mLF->priority() :
mLF->layer()->priority();
2496 bool result =
false;
2498 switch (
mLF->layer()->upsidedownLabels() )
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
LabelPrioritization
Label prioritization.
@ PreferCloser
Prefer closer labels, falling back to alternate positions before larger distances.
@ PreferPositionOrdering
Prefer labels follow position ordering, falling back to more distance labels before alternate positio...
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only.
@ AllowPlacementInsideOfPolygon
Labels can be placed inside a polygon feature.
@ AllowPlacementOutsideOfPolygon
Labels can be placed outside of a polygon feature.
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
LabelQuadrantPosition
Label quadrant positions.
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
@ OverPoint
Label directly centered over point.
@ MiddleLeft
Label on left of point.
@ TopRight
Label on top-right of point.
@ MiddleRight
Label on right of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopMiddle
Label directly above point.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ TopLeft
Label on top-left of point.
@ BottomMiddle
Label directly below point.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
@ FlipUpsideDownLabels
Upside-down labels (90 <= angle < 270) are shown upright.
@ AlwaysAllowUpsideDown
Show upside down for all labels, including dynamic ones.
@ AllowUpsideDownWhenRotationIsDefined
Show upside down when rotation is layer- or data-defined.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
A geometry is the spatial representation of a feature.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
Describes a feature that should be used within the labeling engine.
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.
@ Strict
Line anchor is a strict placement, and other placements are not permitted.
@ HintOnly
Line anchor is a hint for preferred placement only, but other placements close to the hint are permit...
AnchorTextPoint
Anchor point of label text.
@ EndOfText
Anchor using end of text.
@ StartOfText
Anchor using start of text.
@ CenterOfText
Anchor using center of text.
@ FollowPlacement
Automatically set the anchor point based on the lineAnchorPercent() value. Values <25% will use the s...
Line string geometry type, with support for z-dimension and m-values.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Contains precalculated properties regarding text metrics for text to be rendered 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...
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, Qgis::CurvedTextFlags flags=Qgis::CurvedTextFlags())
Calculates curved text placement properties.
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 ...
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::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::unique_ptr< LabelPosition > curvedPlacementAtOffset(PointSet *mapShape, const std::vector< double > &pathDistances, QgsTextRendererUtils::LabelLineDirection direction, double distance, bool &labeledLineSegmentIsRightToLeft, bool applyAngleConstraints, Qgis::CurvedTextFlags flags)
Returns the label position for a curved label at a specific offset along a path.
std::size_t maximumPointCandidates() const
Returns the maximum number of point candidates to generate for this feature.
static bool reorderPolygon(std::vector< double > &x, std::vector< double > &y)
Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside.
static bool containsCandidate(const GEOSPreparedGeometry *geom, double x, double y, double width, double height, double alpha)
Returns true if a GEOS prepared geometry totally contains a label candidate.
double getAlpha() const
Returns the angle to rotate text (in radians).
void setNextPart(std::unique_ptr< LabelPosition > next)
Sets the next part of this label position (i.e.
double getX(int i=0) const
Returns the down-left x coordinate.
double getY(int i=0) const
Returns the down-left y coordinate.
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
QString name() const
Returns the layer's name.
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.
friend class LabelPosition
double lineLocatePoint(const GEOSGeometry *point) const
Returns the distance along the geometry closest to the specified GEOS point.
double length() const
Returns length of line geometry.
double area() const
Returns area of polygon geometry.
bool isClosed() const
Returns true if pointset is closed.
void createGeosGeom() const
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py) const
Gets a point a set distance along a line geometry.
void getCentroid(double &px, double &py, bool forceInside=false) const
OrientedConvexHullBoundingBox computeConvexHullOrientedBoundingBox(bool &ok) const
Computes an oriented bounding box for the shape's convex hull.
const GEOSPreparedGeometry * preparedGeom() const
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
void invalidateGeos() const
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
std::tuple< std::vector< double >, double > edgeDistances() const
Returns a vector of edge distances as well as its total length.
static QLinkedList< PointSet * > splitPolygons(PointSet *inputShape, double labelWidth, double labelHeight)
Split a polygon using some random logic into some other polygons.
void createCandidateAtOrderedPositionOverPoint(double &labelX, double &labelY, Qgis::LabelQuadrantPosition &quadrant, double x, double y, double labelWidth, double labelHeight, Qgis::LabelPredefinedPointPosition position, double distanceToLabel, const QgsMargins &visualMargin, double symbolWidthOffset, double symbolHeightOffset, double angle)
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
T qgsgeometry_cast(QgsAbstractGeometry *geom)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)
Represents the minimum area, oriented bounding box surrounding a convex hull.
struct GEOSGeom_t GEOSGeometry