51using namespace Qt::StringLiterals;
65 for (
int i = 0; i <
mHoles.count(); i++ )
67 mHoles.at( i )->holeOf =
this;
75 , mTotalRepeats( other.mTotalRepeats )
76 , mCachedMaxLineCandidates( other.mCachedMaxLineCandidates )
77 , mCachedMaxPolygonCandidates( other.mCachedMaxPolygonCandidates )
81 mHoles << new FeaturePart( *hole );
82 mHoles.last()->holeOf = this;
96 const GEOSCoordSequence *coordSeq =
nullptr;
99 type = GEOSGeomTypeId_r( geosctxt, geom );
101 if (
type == GEOS_POLYGON )
103 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
105 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
107 for (
int i = 0; i < numHoles; ++i )
109 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
121 geom = GEOSGetExteriorRing_r( geosctxt, geom );
130 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
131 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
134 xmin =
ymin = std::numeric_limits<double>::max();
135 xmax =
ymax = std::numeric_limits<double>::lowest();
142#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
143 GEOSCoordSeq_copyToArrays_r( geosctxt, coordSeq,
x.data(),
y.data(),
nullptr,
nullptr );
144 auto xminmax = std::minmax_element(
x.begin(),
x.end() );
145 xmin = *xminmax.first;
146 xmax = *xminmax.second;
147 auto yminmax = std::minmax_element(
y.begin(),
y.end() );
148 ymin = *yminmax.first;
149 ymax = *yminmax.second;
151 for (
int i = 0; i <
nbPoints; ++i )
153 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
176 return mLF->subPartId();
181 return mLF->layer()->maximumPointLabelCandidates();
186 if ( mCachedMaxLineCandidates > 0 )
187 return mCachedMaxLineCandidates;
189 const double l =
length();
192 const std::size_t candidatesForLineLength =
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * l ) );
193 const std::size_t maxForLayer =
mLF->layer()->maximumLineLabelCandidates();
194 if ( maxForLayer == 0 )
195 mCachedMaxLineCandidates = candidatesForLineLength;
197 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
201 mCachedMaxLineCandidates = 1;
203 return mCachedMaxLineCandidates;
208 if ( mCachedMaxPolygonCandidates > 0 )
209 return mCachedMaxPolygonCandidates;
211 const double a =
area();
214 const std::size_t candidatesForArea =
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() * a ) );
215 const std::size_t maxForLayer =
mLF->layer()->maximumPolygonLabelCandidates();
216 if ( maxForLayer == 0 )
217 mCachedMaxPolygonCandidates = candidatesForArea;
219 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
223 mCachedMaxPolygonCandidates = 1;
225 return mCachedMaxPolygonCandidates;
240 int connectedFeatureId =
mLF->layer()->connectedFeatureId(
mLF->id() );
241 return connectedFeatureId >= 0 && connectedFeatureId ==
mLF->layer()->connectedFeatureId( part->
featureId() );
247 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
249 if ( quadOffsetX < 0 )
251 if ( quadOffsetY < 0 )
255 else if ( quadOffsetY > 0 )
264 else if ( quadOffsetX > 0 )
266 if ( quadOffsetY < 0 )
270 else if ( quadOffsetY > 0 )
281 if ( quadOffsetY < 0 )
285 else if ( quadOffsetY > 0 )
298 return mTotalRepeats;
312 double cost = 0.00005;
313 int id = lPos.size();
315 double xdiff = -labelW / 2.0;
316 double ydiff = -labelH / 2.0;
320 double lx =
x + xdiff;
321 double ly =
y + ydiff;
323 if (
mLF->permissibleZonePrepared() )
341 double cost = 0.0001;
342 int id = lPos.size();
344 double xdiff = -labelW / 2.0;
345 double ydiff = -labelH / 2.0;
351 xdiff += labelW / 2.0 *
mLF->quadOffset().x();
355 ydiff += labelH / 2.0 *
mLF->quadOffset().y();
358 if ( !
mLF->hasFixedPosition() )
362 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
363 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
375 ydiff +=
mLF->quadOffset().y() *
mLF->distLabel();
379 xdiff +=
mLF->quadOffset().x() *
mLF->distLabel();
383 xdiff +=
mLF->quadOffset().x() * M_SQRT1_2 *
mLF->distLabel();
384 ydiff +=
mLF->quadOffset().y() * M_SQRT1_2 *
mLF->distLabel();
391 xdiff +=
mLF->positionOffset().x();
395 ydiff +=
mLF->positionOffset().y();
399 double lx =
x + xdiff;
400 double ly =
y + ydiff;
402 if (
mLF->permissibleZonePrepared() )
423 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
424 unsigned int nPoints = 0;
425 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
428 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
435 catch ( QgsGeosException &e )
437 qWarning(
"GEOS exception: %s", e.what() );
445void 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 )
456 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
457 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
463 deltaX = -labelWidth / 4.0 - visualMargin.
left();
464 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
470 deltaX = -labelWidth / 2.0;
471 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
477 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
478 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
484 deltaX = - visualMargin.
left() + symbolWidthOffset;
485 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
491 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
492 deltaY = -labelHeight / 2.0;
498 deltaX = -visualMargin.
left() + symbolWidthOffset;
499 deltaY = -labelHeight / 2.0;
505 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
506 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
512 deltaX = -labelWidth / 4.0 - visualMargin.
left();
513 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
519 deltaX = -labelWidth / 2.0;
520 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
526 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
527 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
533 deltaX = -visualMargin.
left() + symbolWidthOffset;
534 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
541 deltaX = -labelWidth / 2.0;
542 deltaY = -labelHeight / 2.0;
548 QTransform transformRotation;
549 transformRotation.rotate( angle * 180 / M_PI );
550 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
553 double referenceX = std::cos( alpha ) * distanceToLabel + x;
554 double referenceY = std::sin( alpha ) * distanceToLabel + y;
556 labelX = referenceX + deltaX;
557 labelY = referenceY + deltaY;
562 const QVector< Qgis::LabelPredefinedPointPosition > positions =
mLF->predefinedPositionOrder();
566 const double maximumDistanceToLabel =
mLF->maximumDistance();
570 double symbolWidthOffset{ 0 };
571 double symbolHeightOffset{ 0 };
576 if (
mLF->feature().geometry().constParts().hasNext() )
584 symbolWidthOffset =
mLF->symbolSize().width() / 2.0;
585 symbolHeightOffset =
mLF->symbolSize().height() / 2.0;
589 int candidatesPerPosition = 1;
590 double distanceStep = 0;
591 if ( maximumDistanceToLabel > distanceToLabel && !
qgsDoubleNear( maximumDistanceToLabel, 0 ) )
595 const double rayLength = maximumDistanceToLabel - distanceToLabel;
598 candidatesPerPosition = std::max( 2,
static_cast< int >( std::ceil(
mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * 1.5 * rayLength ) ) );
599 distanceStep = rayLength / ( candidatesPerPosition - 1 );
602 double cost = 0.0001;
603 std::size_t i = lPos.size();
606 const std::size_t maxNumberCandidates =
mLF->layer()->maximumPointLabelCandidates() * candidatesPerPosition;
607 std::size_t created = 0;
609 auto addCandidate = [
this,
x,
y, labelWidth, labelHeight, angle, visualMargin, symbolWidthOffset, symbolHeightOffset, &created, &cost, &lPos, &i, maxNumberCandidates](
Qgis::LabelPredefinedPointPosition position,
double distance ) ->
bool
615 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distance, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
624 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
630 switch ( prioritization )
638 double currentDistance = distanceToLabel;
639 for (
int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
641 if ( !addCandidate( position, currentDistance ) )
650 double currentDistance = distanceToLabel;
651 for (
int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
655 if ( !addCandidate( position, currentDistance ) )
670 const double maximumDistanceToLabel =
mLF->maximumDistance();
674 QTransform transformRotation;
675 transformRotation.rotate( angle * 180 / M_PI );
677 int rayCount =
static_cast< int >(
mLF->layer()->maximumPointLabelCandidates() );
681 int candidatesPerRay = 0;
682 double rayStepDelta = 0;
683 if ( maximumDistanceToLabel > distanceToLabel && !
qgsDoubleNear( maximumDistanceToLabel, 0 ) )
687 const double rayLength = maximumDistanceToLabel - distanceToLabel;
690 candidatesPerRay = std::max( 2,
static_cast< int >( std::ceil(
mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * 1.5 * rayLength ) ) );
691 rayStepDelta = rayLength / ( candidatesPerRay - 1 );
695 candidatesPerRay = 1;
698 int id =
static_cast< int >( lPos.size() );
700 const double candidateAngleIncrement = 2 * M_PI /
static_cast< double >( rayCount );
703 constexpr double a90 = M_PI_2;
704 constexpr double a180 = M_PI;
705 constexpr double a270 = a180 + a90;
706 constexpr double a360 = 2 * M_PI;
708 double gamma1, gamma2;
710 if ( distanceToLabel > 0 )
712 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
713 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
717 gamma1 = gamma2 = a90 / 3.0;
720 if ( gamma1 > a90 / 3.0 )
723 if ( gamma2 > a90 / 3.0 )
726 std::size_t numberCandidatesGenerated = 0;
728 double angleToCandidate = M_PI_4;
730 int integerRayCost = 0;
731 int integerRayCostIncrement = 2;
733 for (
int rayIndex = 0; rayIndex < rayCount; ++rayIndex, angleToCandidate += candidateAngleIncrement )
738 if ( angleToCandidate > a360 )
739 angleToCandidate -= a360;
741 double rayDistance = distanceToLabel;
743 constexpr double RAY_ANGLE_COST_FACTOR = 0.0020;
747 const double scaledRayAngleCost = RAY_ANGLE_COST_FACTOR *
static_cast< double >( integerRayCost )
748 /
static_cast< double >( rayCount - 1 );
750 for (
int j = 0; j < candidatesPerRay; ++j, rayDistance += rayStepDelta )
754 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
756 deltaX = rayDistance;
757 double iota = ( angleToCandidate + gamma1 );
758 if ( iota > a360 - gamma1 )
761 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
765 else if ( angleToCandidate < a90 - gamma2 )
767 deltaX = rayDistance * std::cos( angleToCandidate );
768 deltaY = rayDistance * std::sin( angleToCandidate );
771 else if ( angleToCandidate < a90 + gamma2 )
773 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
774 deltaY = rayDistance;
777 else if ( angleToCandidate < a180 - gamma1 )
779 deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
780 deltaY = rayDistance * std::sin( angleToCandidate );
783 else if ( angleToCandidate < a180 + gamma1 )
785 deltaX = -rayDistance - labelWidth;
786 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
789 else if ( angleToCandidate < a270 - gamma2 )
791 deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
792 deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
795 else if ( angleToCandidate < a270 + gamma2 )
797 deltaY = -rayDistance - labelHeight;
798 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
801 else if ( angleToCandidate < a360 )
803 deltaX = rayDistance * std::cos( angleToCandidate );
804 deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
808 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
810 double labelX =
x + deltaX;
811 double labelY =
y + deltaY;
818 cost = 0.0001 + scaledRayAngleCost;
825 cost += j * RAY_ANGLE_COST_FACTOR + RAY_ANGLE_COST_FACTOR / rayCount;
828 if (
mLF->permissibleZonePrepared() )
838 numberCandidatesGenerated++;
841 integerRayCost += integerRayCostIncrement;
843 if ( integerRayCost ==
static_cast< int >( rayCount ) )
845 integerRayCost =
static_cast< int >( rayCount ) - 1;
846 integerRayCostIncrement = -2;
848 else if ( integerRayCost >
static_cast< int >( rayCount ) )
850 integerRayCost =
static_cast< int >( rayCount ) - 2;
851 integerRayCostIncrement = -2;
855 return numberCandidatesGenerated;
862 double shapeLength = mapShape->
length();
873 std::size_t candidates = 0;
879 if ( candidates < candidateTargetCount )
894 std::vector< double > &
x = line->
x;
895 std::vector< double > &
y = line->
y;
897 std::vector< double > segmentLengths(
nbPoints - 1 );
898 std::vector< double >distanceToSegment(
nbPoints );
900 double totalLineLength = 0.0;
901 for (
int i = 0; i < line->
nbPoints - 1; i++ )
904 distanceToSegment[i] = 0;
906 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
909 totalLineLength += segmentLengths[i];
911 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
914 double lineStepDistance = 0;
916 const double lineAnchorPoint = totalLineLength *
mLF->lineAnchorPercent();
917 double currentDistanceAlongLine = lineStepDistance;
918 switch (
mLF->lineAnchorType() )
921 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
925 currentDistanceAlongLine = lineAnchorPoint;
926 lineStepDistance = -1;
932 double candidateCenterX, candidateCenterY;
934 while ( currentDistanceAlongLine <= totalLineLength )
936 if (
pal->isCanceled() )
941 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
944 double cost = totalLineLength > 0 ? std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength : 0;
951 labelX = candidateCenterX;
954 labelX = candidateCenterX - labelWidth / 2;
957 labelX = candidateCenterX - labelWidth;
965 currentDistanceAlongLine += lineStepDistance;
969 if ( lineStepDistance < 0 )
986 QVector< int > extremeAngleNodes;
989 std::vector< double > &
x = line->
x;
990 std::vector< double > &
y = line->
y;
994 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
996 double x1 =
x[i - 1];
998 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
999 double y1 =
y[i - 1];
1001 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
1006 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
1010 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
1011 extremeAngleNodes << i;
1013 extremeAngleNodes << numberNodes - 1;
1015 if ( extremeAngleNodes.isEmpty() )
1022 std::vector< double > segmentLengths( numberNodes - 1 );
1023 std::vector< double > distanceToSegment( numberNodes );
1024 double totalLineLength = 0.0;
1025 QVector< double > straightSegmentLengths;
1026 QVector< double > straightSegmentAngles;
1027 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
1028 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
1029 double currentStraightSegmentLength = 0;
1030 double longestSegmentLength = 0;
1031 double segmentStartX =
x[0];
1032 double segmentStartY =
y[0];
1033 for (
int i = 0; i < numberNodes - 1; i++ )
1036 distanceToSegment[i] = 0;
1038 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1041 totalLineLength += segmentLengths[i];
1042 if ( extremeAngleNodes.contains( i ) )
1045 straightSegmentLengths << currentStraightSegmentLength;
1047 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
1048 currentStraightSegmentLength = 0;
1049 segmentStartX =
x[i];
1050 segmentStartY =
y[i];
1052 currentStraightSegmentLength += segmentLengths[i];
1054 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1055 straightSegmentLengths << currentStraightSegmentLength;
1057 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
1058 const double lineAnchorPoint = totalLineLength *
mLF->lineAnchorPercent();
1060 if ( totalLineLength < labelWidth )
1068 double lineStepDistance = ( totalLineLength - labelWidth );
1069 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1071 double distanceToEndOfSegment = 0.0;
1072 int lastNodeInSegment = 0;
1074 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
1076 currentStraightSegmentLength = straightSegmentLengths.at( i );
1077 double currentSegmentAngle = straightSegmentAngles.at( i );
1078 lastNodeInSegment = extremeAngleNodes.at( i );
1079 double distanceToStartOfSegment = distanceToEndOfSegment;
1080 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
1081 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
1083 if ( currentStraightSegmentLength < labelWidth )
1087 double currentDistanceAlongLine = distanceToStartOfSegment;
1088 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1089 double candidateLength = 0.0;
1095 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
1096 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
1098 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
1100 if (
pal->isCanceled() )
1106 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1107 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1115 cost = candidateLength / labelWidth;
1121 cost = ( 1 - cost ) / 100;
1124 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1125 double labelTextAnchor = 0;
1126 switch ( textPoint )
1129 labelTextAnchor = currentDistanceAlongLine;
1132 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1135 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1142 const bool placementIsFlexible =
mLF->lineAnchorPercent() > 0.1 &&
mLF->lineAnchorPercent() < 0.9;
1144 if ( placementIsFlexible )
1147 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1148 cost += costCenter * 0.0005;
1156 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1157 cost += costLineCenter * 0.0005;
1160 if ( placementIsFlexible )
1162 cost += segmentCost * 0.0005;
1163 cost += segmentAngleCost * 0.0001;
1171 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1175 beta = angle + M_PI_2;
1180 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1188 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) )
1190 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1196 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) )
1198 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1204 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle ) )
1206 const double candidateCost = cost + 0.002;
1220 currentDistanceAlongLine += lineStepDistance;
1243 std::vector< double > &
x = line->
x;
1244 std::vector< double > &
y = line->
y;
1246 std::vector< double > segmentLengths(
nbPoints - 1 );
1247 std::vector< double >distanceToSegment(
nbPoints );
1249 double totalLineLength = 0.0;
1250 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1253 distanceToSegment[i] = 0;
1255 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1258 totalLineLength += segmentLengths[i];
1260 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1262 double lineStepDistance = ( totalLineLength - labelWidth );
1263 double currentDistanceAlongLine = 0;
1269 if ( totalLineLength > labelWidth )
1271 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1275 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1276 lineStepDistance = -1;
1277 totalLineLength = labelWidth;
1282 currentDistanceAlongLine = std::numeric_limits< double >::max();
1285 const double lineAnchorPoint = totalLineLength * std::min( 0.99,
mLF->lineAnchorPercent() );
1287 switch (
mLF->lineAnchorType() )
1293 switch ( textPoint )
1296 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1299 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1302 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1308 lineStepDistance = -1;
1312 double candidateLength;
1314 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1318 if (
pal->isCanceled() )
1324 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1325 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1327 if ( currentDistanceAlongLine < 0 )
1337 cost = candidateLength / labelWidth;
1343 cost = ( 1 - cost ) / 100;
1347 double textAnchorPoint = 0;
1348 switch ( textPoint )
1351 textAnchorPoint = currentDistanceAlongLine;
1354 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1357 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1363 double costCenter = totalLineLength > 0 ? std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength : 0;
1364 cost += costCenter / 1000;
1365 cost += initialCost;
1372 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1376 beta = angle + M_PI_2;
1381 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
1389 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle ) )
1391 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1397 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle ) )
1399 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1405 if ( !
mLF->permissibleZonePrepared() ||
GeomFunction::containsCandidate(
mLF->permissibleZonePrepared(), candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle ) )
1407 const double candidateCost = cost + 0.002;
1421 currentDistanceAlongLine += lineStepDistance;
1425 if ( lineStepDistance < 0 )
1435 Q_ASSERT( metrics );
1437 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1438 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1440 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1441 QgsTextRendererUtils::generateCurvedTextPlacement( *metrics, mapShape->
x.data(), mapShape->
y.data(), mapShape->
nbPoints, pathDistances, offsetAlongLine, direction, maximumCharacterAngleInside, maximumCharacterAngleOutside, flags, additionalCharacterSpacing, additionalWordSpacing )
1446 if ( placement->graphemePlacement.empty() )
1449 auto it = placement->graphemePlacement.constBegin();
1451 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1452 firstPosition->setPartId( it->graphemeIndex );
1456 bool skipWhitespace =
false;
1457 switch (
mLF->whitespaceCollisionHandling() )
1463 skipWhitespace =
true;
1467 while ( it != placement->graphemePlacement.constEnd() )
1469 if ( skipWhitespace && it->isWhitespace )
1475 position->setPartId( it->graphemeIndex );
1478 previousPosition->
setNextPart( std::move( position ) );
1479 previousPosition = nextPosition;
1483 return firstPosition;
1495 const int characterCount = li->
count();
1496 if ( characterCount == 0 )
1499 switch (
mLF->curvedLabelMode() )
1514 const int characterCount = li->
count();
1517 double totalCharacterWidth = 0;
1519 for (
int i = 0; i < characterCount; ++i )
1522 if ( stretchWordSpacingToFit && li->
grapheme( i ) ==
' ' )
1527 if ( spaceCount == 0 )
1530 stretchWordSpacingToFit =
false;
1534 const bool usingStretchToFitMode = stretchCharacterSpacingToFit || stretchWordSpacingToFit;
1539 std::unique_ptr< PointSet > expanded;
1540 double shapeLength = mapShape->
length();
1545 allowOverrun =
false;
1548 if ( !usingStretchToFitMode )
1554 switch (
mLF->lineAnchorType() )
1557 overrun = std::min(
mLF->overrunDistance(), totalCharacterWidth * 0.95 );
1562 overrun = std::max(
mLF->overrunDistance(), totalCharacterWidth * 1.05 );
1566 if ( totalCharacterWidth > shapeLength )
1568 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1579 if ( allowOverrun && overrun > 0 )
1582 expanded = mapShape->
clone();
1583 expanded->extendLineByDistance( overrun, overrun,
mLF->overrunSmoothDistance() );
1584 mapShape = expanded.get();
1585 shapeLength += 2 * overrun;
1594 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1595 bool positiveShapeHasNegativeDistance =
false;
1596 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1597 bool negativeShapeHasNegativeDistance =
false;
1598 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1602 mapShapeOffsetPositive = mapShape->
clone();
1604 mapShapeOffsetNegative = mapShape->
clone();
1607 if ( mapShapeOffsetPositive )
1608 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1609 positiveShapeHasNegativeDistance = offsetDistance < 0;
1610 if ( mapShapeOffsetNegative )
1611 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1612 negativeShapeHasNegativeDistance = offsetDistance > 0;
1629 if ( mapShapeOffsetPositive )
1630 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1631 positiveShapeHasNegativeDistance = offsetDistance > 0;
1632 if ( mapShapeOffsetNegative )
1633 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1634 negativeShapeHasNegativeDistance = offsetDistance < 0;
1640 std::vector< std::unique_ptr< LabelPosition >> positions;
1641 std::unique_ptr< LabelPosition > backupPlacement;
1644 PointSet *currentMapShape =
nullptr;
1647 currentMapShape = mapShapeOffsetPositive.get();
1651 currentMapShape = mapShape;
1655 currentMapShape = mapShapeOffsetNegative.get();
1657 if ( !currentMapShape )
1661 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1665 double lineAnchorPoint = 0;
1666 if ( !usingStretchToFitMode )
1668 if ( originalPoint )
1673 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1677 lineAnchorPoint = totalDistance *
mLF->lineAnchorPercent();
1679 lineAnchorPoint = totalDistance - lineAnchorPoint;
1683 if (
pal->isCanceled() )
1687 double delta = std::max( li->
characterHeight( 0 ) / 6, totalDistance / candidateTargetCount );
1690 double distanceAlongLineToStartCandidate = 0;
1691 bool singleCandidateOnly =
false;
1692 double additionalCharacterSpacing = 0.0;
1693 double additionalWordSpacing = 0.0;
1694 if ( usingStretchToFitMode )
1697 double extraSpace = totalDistance - totalCharacterWidth;
1702 if ( extraSpace > 0 )
1703 extraSpace *= 0.995;
1705 extraSpace *= 1.005;
1707 if ( stretchWordSpacingToFit )
1709 if ( spaceCount > 0 )
1710 additionalWordSpacing = extraSpace / spaceCount;
1716 if ( characterCount > 1 )
1717 additionalCharacterSpacing = extraSpace / ( characterCount - 1 );
1721 distanceAlongLineToStartCandidate = 0;
1722 delta = totalDistance + 1.0;
1723 singleCandidateOnly =
true;
1727 switch (
mLF->lineAnchorType() )
1733 switch ( textPoint )
1736 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1739 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1742 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1748 singleCandidateOnly =
true;
1753 bool hasTestedFirstPlacement =
false;
1754 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1756 if ( singleCandidateOnly && hasTestedFirstPlacement )
1759 if (
pal->isCanceled() )
1762 hasTestedFirstPlacement =
true;
1764 bool labeledLineSegmentIsRightToLeft =
false;
1770 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly, curvedTextFlags, additionalCharacterSpacing, additionalWordSpacing );
1771 if ( !labelPosition )
1777 bool isBackupPlacementOnly =
false;
1780 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1781 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1783 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1789 isBackupPlacementOnly =
true;
1796 isBackupPlacementOnly =
true;
1802 backupPlacement.reset();
1805 const double angleDiff = labelPosition->angleDifferential();
1806 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1810 const bool anchorIsFlexiblePlacement = !singleCandidateOnly &&
mLF->lineAnchorPercent() > 0.1 &&
mLF->lineAnchorPercent() < 0.9;
1811 double cost = angleDiffAvg / 100;
1812 if ( cost < 0.0001 )
1816 if ( !usingStretchToFitMode )
1819 double labelTextAnchor = 0;
1820 switch ( textPoint )
1823 labelTextAnchor = distanceAlongLineToStartCandidate;
1826 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1829 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1835 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1836 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1839 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1851 labelPosition->setCost( cost );
1853 auto p = std::make_unique< LabelPosition >( *labelPosition );
1854 if ( p &&
mLF->permissibleZonePrepared() )
1858 while ( within && currentPos )
1861 currentPos = currentPos->
nextPart();
1871 if ( isBackupPlacementOnly )
1872 backupPlacement = std::move( p );
1874 positions.emplace_back( std::move( p ) );
1879 for ( std::unique_ptr< LabelPosition > &pos : positions )
1881 lPos.emplace_back( std::move( pos ) );
1884 if ( backupPlacement )
1885 lPos.emplace_back( std::move( backupPlacement ) );
1887 return positions.size();
1894 const int characterCount = metrics->
count();
1896 if ( characterCount == 0 || vertexCount == 0 )
1899 const double distLabel =
mLF->distLabel();
1901 std::unique_ptr< LabelPosition > firstPosition;
1904 int vertexIndex = 0;
1905 int characterIndex = -1;
1906 for ( ; vertexIndex < vertexCount; ++vertexIndex )
1908 if (
pal->isCanceled() )
1911 bool isWhiteSpace =
true;
1912 while ( isWhiteSpace )
1915 if ( characterIndex >= characterCount )
1918 isWhiteSpace = metrics->
grapheme( characterIndex ).trimmed().isEmpty() || metrics->
grapheme( characterIndex ) ==
'\t';
1921 if ( characterIndex >= characterCount )
1924 double x = mapShape->
x[vertexIndex];
1925 double y = mapShape->
y[vertexIndex];
1930 if ( vertexIndex < vertexCount - 1 )
1932 angle = std::atan2( mapShape->
y[vertexIndex + 1] -
y, mapShape->
x[vertexIndex + 1] -
x );
1934 else if ( vertexIndex > 0 )
1936 angle = std::atan2(
y - mapShape->
y[vertexIndex - 1],
x - mapShape->
x[vertexIndex - 1] );
1940 x -= std::sin( angle ) * distLabel;
1941 y += std::cos( angle ) * distLabel;
1947 currentPosition->setPartId( characterIndex );
1949 if ( !firstPosition )
1951 firstPosition = std::move( currentPosition );
1952 previousPosition = firstPosition.get();
1957 previousPosition->
setNextPart( std::move( currentPosition ) );
1958 previousPosition = rawCurrent;
1962 if ( !firstPosition )
1965 if (
mLF->permissibleZonePrepared() )
1969 while ( within && currentPos )
1972 currentPos = currentPos->
nextPart();
1980 lPos.emplace_back( std::move( firstPosition ) );
2001 const std::size_t maxPolygonCandidates =
mLF->layer()->maximumPolygonLabelCandidates();
2002 const std::size_t targetPolygonCandidates = maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates,
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() *
area() ) ) )
2005 const double totalArea =
area();
2007 mapShape->
parent =
nullptr;
2009 if (
pal->isCanceled() )
2012 QVector<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
2015 for (
PointSet *ps : shapes_final )
2021 std::size_t nbp = 0;
2023 if ( !shapes_final.isEmpty() )
2031 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
2033 std::vector< OrientedConvexHullBoundingBox > boxes;
2034 boxes.reserve( shapes_final.size() );
2037 while ( !shapes_final.isEmpty() )
2039 PointSet *shape = shapes_final.takeFirst();
2043 boxes.emplace_back( box );
2049 if (
pal->isCanceled() )
2052 double densityX = 1.0 / std::sqrt(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() );
2053 double densityY = densityX;
2058 int maxTry =
mLF->permissibleZonePrepared() ? 7 : 10;
2060 std::size_t numberCandidatesGenerated = 0;
2075 double dx = densityX;
2076 double dy = densityY;
2077 if ( numTry == 0 && maxPolygonCandidates > 0 )
2080 const double boxArea = box.width * box.length;
2081 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
2082 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
2086 if (
pal->isCanceled() )
2087 return numberCandidatesGenerated;
2098 if (
mLF->permissibleZone().boundingBox().width() < labelWidth ||
2099 mLF->permissibleZone().boundingBox().height() < labelHeight )
2106 bool enoughPlace =
false;
2110 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
2111 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
2117 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
2119 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
2123 enoughPlace =
false;
2139 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
2141 if ( box.alpha <= M_PI_4 )
2147 alpha = box.alpha - M_PI_2;
2150 else if ( box.length > box.width )
2152 alpha = box.alpha - M_PI_2;
2159 beta = std::atan2( labelHeight, labelWidth ) + alpha;
2165 dlx = std::cos( beta ) * diago;
2166 dly = std::sin( beta ) * diago;
2168 double px0 = box.width / 2.0;
2169 double py0 = box.length / 2.0;
2171 px0 -= std::ceil( px0 / dx ) * dx;
2172 py0 -= std::ceil( py0 / dy ) * dy;
2174 for ( px = px0; px <= box.width; px += dx )
2176 if (
pal->isCanceled() )
2179 for ( py = py0; py <= box.length; py += dy )
2182 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
2183 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
2188 if (
mLF->permissibleZonePrepared() )
2194 numberCandidatesGenerated++;
2207 lPos.emplace_back( std::move( potentialCandidate ) );
2208 numberCandidatesGenerated++;
2215 nbp = numberCandidatesGenerated;
2216 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
2227 while ( numTry < maxTry );
2229 nbp = numberCandidatesGenerated;
2242 const std::size_t maxPolygonCandidates =
mLF->layer()->maximumPolygonLabelCandidates();
2243 std::size_t candidatesCreated = 0;
2305 return candidatesCreated;
2309 return candidatesCreated;
2313 const double ringLength = ring->
length();
2314 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2315 const std::size_t candidatesForArea =
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() * circleArea ) );
2316 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2319 const double delta = ringLength / targetPolygonCandidates;
2322 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2323 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2324 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2327 const double labelAngle = 0;
2329 std::size_t i = lPos.size();
2337 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2340 if ( candidate->intersects( preparedBuffer.get() ) )
2358 const double centroidDistance = candidate->getDistanceToPoint( cx, cy,
false );
2359 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2360 candidate->setCost( centroidCost );
2362 lPos.emplace_back( std::move( candidate ) );
2363 candidatesCreated++;
2368 double startSegmentX,
double startSegmentY,
double,
double,
2369 double endSegmentX,
double endSegmentY,
double,
double )
2372 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2377 if ( angle >= 0 && angle <= 5 )
2382 else if ( angle <= 85 )
2386 else if ( angle <= 90 )
2392 else if ( angle <= 95 )
2397 else if ( angle <= 175 )
2401 else if ( angle <= 180 )
2407 else if ( angle <= 185 )
2412 else if ( angle <= 265 )
2416 else if ( angle <= 270 )
2421 else if ( angle <= 275 )
2426 else if ( angle <= 355 )
2436 return !
pal->isCanceled();
2439 return candidatesCreated;
2444 std::vector< std::unique_ptr< LabelPosition > > lPos;
2445 double angleInRadians =
mLF->hasFixedAngle() ?
mLF->fixedAngle() : 0.0;
2447 if (
mLF->hasFixedPosition() )
2464 case GEOS_LINESTRING:
2467 else if (
mLF->layer()->isCurved() )
2487 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2488 std::fabs(
ymax -
ymin ) < labelHeight ) )
2495 std::size_t created = 0;
2498 switch (
mLF->layer()->arrangement() )
2553 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2555 double sizeCost = 0;
2556 if ( geomType == GEOS_LINESTRING )
2558 const double l =
length();
2561 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2562 if ( l >= bbox_length / 4 )
2565 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2567 else if ( geomType == GEOS_POLYGON )
2569 const double a =
area();
2572 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2573 if ( a >= bbox_area / 16 )
2576 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2582 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2584 pos->setCost( pos->cost() + sizeCost / 100 );
2595 const double x1first =
x.front();
2596 const double x1last =
x.back();
2597 const double x2first = p2->
x.front();
2598 const double x2last = p2->
x.back();
2599 const double y1first =
y.front();
2600 const double y1last =
y.back();
2601 const double y2first = p2->
y.front();
2602 const double y2last = p2->
y.back();
2610 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2616 const double p2otherX = p2startTouches ? x2last : x2first;
2617 const double p2otherY = p2startTouches ? y2last : y2first;
2623#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
2624 return ( GEOSPreparedIntersectsXY_r( geosctxt,
preparedGeom(), p2otherX, p2otherY ) != 1 );
2626 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2627 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2629 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2632 catch ( QgsGeosException &e )
2634 qWarning(
"GEOS exception: %s", e.what() );
2644 if ( !other->
mGeos )
2653 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2656 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2664 mGeos = gTmp.release();
2673 catch ( QgsGeosException &e )
2675 qWarning(
"GEOS exception: %s", e.what() );
2683 if (
mLF->alwaysShow() )
2691 return mLF->priority() >= 0 ?
mLF->priority() :
mLF->layer()->priority();
2696 bool result =
false;
2698 switch (
mLF->layer()->upsidedownLabels() )
@ StretchCharacterSpacingToFitLine
Increases (or decreases) the character spacing used for each label in order to fit the entire text ov...
@ Default
Default curved placement, characters are placed in an optimal position along the line....
@ StretchWordSpacingToFitLine
Increases (or decreases) the word spacing used for each label in order to fit the entire text over th...
@ PlaceCharactersAtVertices
Each individual character from the label text is placed such that their left-baseline position is loc...
@ 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.
@ TreatWhitespaceAsCollision
Treat overlapping whitespace text in labels and whitespace overlapping obstacles as collisions.
@ IgnoreWhitespaceCollisions
Ignore overlapping whitespace text in labels and whitespace overlapping obstacles.
@ 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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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.
QString grapheme(int index) const
Returns the grapheme at the specified index.
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.
std::unique_ptr< LabelPosition > curvedPlacementAtOffset(PointSet *mapShape, const std::vector< double > &pathDistances, QgsTextRendererUtils::LabelLineDirection direction, double distance, bool &labeledLineSegmentIsRightToLeft, bool applyAngleConstraints, Qgis::CurvedTextFlags flags, double additionalCharacterSpacing, double additionalWordSpacing)
Returns the label position for a curved label at a specific offset along a path.
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 createCurvedCandidateWithCharactersAtVertices(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generates a curved candidates for line features, placing individual characters on the line vertices.
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.
std::size_t createDefaultCurvedCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate curved candidates for line features, using default placement.
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.
int subPartId() const
Returns the unique sub part ID for the feature, for features which register multiple labels.
std::size_t createCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate candidates for line feature.
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged).
std::size_t createCurvedCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate curved candidates for line features.
bool onlyShowUprightLabels() const
Returns true if feature's label must be displayed upright.
std::size_t createCandidatesOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate one candidate over or offset the specified point.
std::unique_ptr< LabelPosition > createCandidatePointOnSurface(PointSet *mapShape)
Creates a single candidate using the "point on sruface" algorithm.
double getLabelWidth(double angle=0.0) const
Returns the width of the label, optionally taking an angle (in radians) into account.
QgsLabelFeature * feature()
Returns the parent feature.
std::vector< std::unique_ptr< LabelPosition > > createCandidates(Pal *pal)
Generates a list of candidate positions for labels for this feature.
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
Layer * layer()
Returns the layer that feature belongs to.
PathOffset
Path offset variances used in curved placement.
int totalRepeats() const
Returns the total number of repeating labels associated with this label.
std::size_t createCandidatesAlongLineNearMidpoint(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, double initialCost=0.0, Pal *pal=nullptr)
Generate candidates for line feature, by trying to place candidates as close as possible to the line'...
void addSizePenalty(std::vector< std::unique_ptr< LabelPosition > > &lPos, double bbx[4], double bby[4]) const
Increases the cost of the label candidates for this feature, based on the size of the feature.
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom
double calculatePriority() const
Calculates the priority for the feature.
std::size_t createCandidatesAtOrderedPositionsOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generates candidates following a prioritized list of predefined positions around a point.
std::size_t createCandidateCenteredOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate one candidate centered over the specified point.
std::size_t maximumPointCandidates() const
Returns the maximum number of point candidates to generate for this feature.
static bool reorderPolygon(std::vector< double > &x, std::vector< double > &y)
Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside.
static bool containsCandidate(const GEOSPreparedGeometry *geom, double x, double y, double width, double height, double alpha)
Returns true if a GEOS prepared geometry totally contains a label candidate.
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.
static QVector< PointSet * > splitPolygons(PointSet *inputShape, double labelWidth, double labelHeight)
Split a polygon using some random logic into some other polygons.
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.
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.
#define BUILTIN_UNREACHABLE
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