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 )
1501 switch (
mLF->curvedLabelMode() )
1517 const int characterCount = li->
count();
1520 double totalCharacterWidth = 0;
1522 for (
int i = 0; i < characterCount; ++i )
1525 if ( stretchWordSpacingToFit && li->
grapheme( i ) ==
' ' )
1530 if ( spaceCount == 0 )
1533 stretchWordSpacingToFit =
false;
1537 const bool usingStretchToFitMode = stretchCharacterSpacingToFit || stretchWordSpacingToFit;
1542 std::unique_ptr< PointSet > expanded;
1543 double shapeLength = mapShape->
length();
1548 allowOverrun =
false;
1551 if ( !usingStretchToFitMode )
1557 switch (
mLF->lineAnchorType() )
1560 overrun = std::min(
mLF->overrunDistance(), totalCharacterWidth * 0.95 );
1565 overrun = std::max(
mLF->overrunDistance(), totalCharacterWidth * 1.05 );
1569 if ( totalCharacterWidth > shapeLength )
1571 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1582 if ( allowOverrun && overrun > 0 )
1585 expanded = mapShape->
clone();
1586 expanded->extendLineByDistance( overrun, overrun,
mLF->overrunSmoothDistance() );
1587 mapShape = expanded.get();
1588 shapeLength += 2 * overrun;
1597 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1598 bool positiveShapeHasNegativeDistance =
false;
1599 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1600 bool negativeShapeHasNegativeDistance =
false;
1601 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1605 mapShapeOffsetPositive = mapShape->
clone();
1607 mapShapeOffsetNegative = mapShape->
clone();
1610 if ( mapShapeOffsetPositive )
1611 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1612 positiveShapeHasNegativeDistance = offsetDistance < 0;
1613 if ( mapShapeOffsetNegative )
1614 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1615 negativeShapeHasNegativeDistance = offsetDistance > 0;
1632 if ( mapShapeOffsetPositive )
1633 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1634 positiveShapeHasNegativeDistance = offsetDistance > 0;
1635 if ( mapShapeOffsetNegative )
1636 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1637 negativeShapeHasNegativeDistance = offsetDistance < 0;
1643 std::vector< std::unique_ptr< LabelPosition >> positions;
1644 std::unique_ptr< LabelPosition > backupPlacement;
1647 PointSet *currentMapShape =
nullptr;
1650 currentMapShape = mapShapeOffsetPositive.get();
1654 currentMapShape = mapShape;
1658 currentMapShape = mapShapeOffsetNegative.get();
1660 if ( !currentMapShape )
1664 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1668 double lineAnchorPoint = 0;
1669 if ( !usingStretchToFitMode )
1671 if ( originalPoint && offset !=
NoOffset )
1676 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1680 lineAnchorPoint = totalDistance *
mLF->lineAnchorPercent();
1682 lineAnchorPoint = totalDistance - lineAnchorPoint;
1686 if (
pal->isCanceled() )
1690 double delta = std::max( li->
characterHeight( 0 ) / 6, totalDistance / candidateTargetCount );
1693 double distanceAlongLineToStartCandidate = 0;
1694 bool singleCandidateOnly =
false;
1695 double additionalCharacterSpacing = 0.0;
1696 double additionalWordSpacing = 0.0;
1697 if ( usingStretchToFitMode )
1700 double extraSpace = totalDistance - totalCharacterWidth;
1705 if ( extraSpace > 0 )
1706 extraSpace *= 0.995;
1708 extraSpace *= 1.005;
1710 if ( stretchWordSpacingToFit )
1712 if ( spaceCount > 0 )
1713 additionalWordSpacing = extraSpace / spaceCount;
1719 if ( characterCount > 1 )
1720 additionalCharacterSpacing = extraSpace / ( characterCount - 1 );
1724 distanceAlongLineToStartCandidate = 0;
1725 delta = totalDistance + 1.0;
1726 singleCandidateOnly =
true;
1730 switch (
mLF->lineAnchorType() )
1736 switch ( textPoint )
1739 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1742 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1745 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1751 singleCandidateOnly =
true;
1756 bool hasTestedFirstPlacement =
false;
1757 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1759 if ( singleCandidateOnly && hasTestedFirstPlacement )
1762 if (
pal->isCanceled() )
1765 hasTestedFirstPlacement =
true;
1767 bool labeledLineSegmentIsRightToLeft =
false;
1773 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly, curvedTextFlags, additionalCharacterSpacing, additionalWordSpacing );
1774 if ( !labelPosition )
1780 bool isBackupPlacementOnly =
false;
1783 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1784 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1786 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1792 isBackupPlacementOnly =
true;
1799 isBackupPlacementOnly =
true;
1805 backupPlacement.reset();
1808 const double angleDiff = labelPosition->angleDifferential();
1809 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1813 const bool anchorIsFlexiblePlacement = !singleCandidateOnly &&
mLF->lineAnchorPercent() > 0.1 &&
mLF->lineAnchorPercent() < 0.9;
1814 double cost = angleDiffAvg / 100;
1815 if ( cost < 0.0001 )
1819 if ( !usingStretchToFitMode )
1822 double labelTextAnchor = 0;
1823 switch ( textPoint )
1826 labelTextAnchor = distanceAlongLineToStartCandidate;
1829 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1832 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1838 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1839 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1842 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1854 labelPosition->setCost( cost );
1856 auto p = std::make_unique< LabelPosition >( *labelPosition );
1857 if ( p &&
mLF->permissibleZonePrepared() )
1861 while ( within && currentPos )
1864 currentPos = currentPos->
nextPart();
1874 if ( isBackupPlacementOnly )
1875 backupPlacement = std::move( p );
1877 positions.emplace_back( std::move( p ) );
1882 for ( std::unique_ptr< LabelPosition > &pos : positions )
1884 lPos.emplace_back( std::move( pos ) );
1887 if ( backupPlacement )
1888 lPos.emplace_back( std::move( backupPlacement ) );
1890 return positions.size();
1897 const int characterCount = metrics->
count();
1899 if ( characterCount == 0 || vertexCount == 0 )
1902 const double distLabel =
mLF->distLabel();
1904 std::unique_ptr< LabelPosition > firstPosition;
1907 int vertexIndex = 0;
1908 int characterIndex = -1;
1909 for ( ; vertexIndex < vertexCount; ++vertexIndex )
1911 if (
pal->isCanceled() )
1914 bool isWhiteSpace =
true;
1915 while ( isWhiteSpace )
1918 if ( characterIndex >= characterCount )
1921 isWhiteSpace = metrics->
grapheme( characterIndex ).trimmed().isEmpty() || metrics->
grapheme( characterIndex ) ==
'\t';
1924 if ( characterIndex >= characterCount )
1927 double x = mapShape->
x[vertexIndex];
1928 double y = mapShape->
y[vertexIndex];
1933 if ( vertexIndex < vertexCount - 1 )
1935 angle = std::atan2( mapShape->
y[vertexIndex + 1] -
y, mapShape->
x[vertexIndex + 1] -
x );
1937 else if ( vertexIndex > 0 )
1939 angle = std::atan2(
y - mapShape->
y[vertexIndex - 1],
x - mapShape->
x[vertexIndex - 1] );
1943 x -= std::sin( angle ) * distLabel;
1944 y += std::cos( angle ) * distLabel;
1950 currentPosition->setPartId( characterIndex );
1952 if ( !firstPosition )
1954 firstPosition = std::move( currentPosition );
1955 previousPosition = firstPosition.get();
1960 previousPosition->
setNextPart( std::move( currentPosition ) );
1961 previousPosition = rawCurrent;
1965 if ( !firstPosition )
1968 if (
mLF->permissibleZonePrepared() )
1972 while ( within && currentPos )
1975 currentPos = currentPos->
nextPart();
1983 lPos.emplace_back( std::move( firstPosition ) );
2004 const std::size_t maxPolygonCandidates =
mLF->layer()->maximumPolygonLabelCandidates();
2005 const std::size_t targetPolygonCandidates = maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates,
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() *
area() ) ) )
2008 const double totalArea =
area();
2010 mapShape->
parent =
nullptr;
2012 if (
pal->isCanceled() )
2015 QVector<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
2018 for (
PointSet *ps : shapes_final )
2024 std::size_t nbp = 0;
2026 if ( !shapes_final.isEmpty() )
2034 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
2036 std::vector< OrientedConvexHullBoundingBox > boxes;
2037 boxes.reserve( shapes_final.size() );
2040 while ( !shapes_final.isEmpty() )
2042 PointSet *shape = shapes_final.takeFirst();
2046 boxes.emplace_back( box );
2052 if (
pal->isCanceled() )
2055 double densityX = 1.0 / std::sqrt(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() );
2056 double densityY = densityX;
2061 int maxTry =
mLF->permissibleZonePrepared() ? 7 : 10;
2063 std::size_t numberCandidatesGenerated = 0;
2078 double dx = densityX;
2079 double dy = densityY;
2080 if ( numTry == 0 && maxPolygonCandidates > 0 )
2083 const double boxArea = box.width * box.length;
2084 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
2085 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
2089 if (
pal->isCanceled() )
2090 return numberCandidatesGenerated;
2101 if (
mLF->permissibleZone().boundingBox().width() < labelWidth ||
2102 mLF->permissibleZone().boundingBox().height() < labelHeight )
2109 bool enoughPlace =
false;
2113 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
2114 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
2120 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
2122 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
2126 enoughPlace =
false;
2142 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
2144 if ( box.alpha <= M_PI_4 )
2150 alpha = box.alpha - M_PI_2;
2153 else if ( box.length > box.width )
2155 alpha = box.alpha - M_PI_2;
2162 beta = std::atan2( labelHeight, labelWidth ) + alpha;
2168 dlx = std::cos( beta ) * diago;
2169 dly = std::sin( beta ) * diago;
2171 double px0 = box.width / 2.0;
2172 double py0 = box.length / 2.0;
2174 px0 -= std::ceil( px0 / dx ) * dx;
2175 py0 -= std::ceil( py0 / dy ) * dy;
2177 for ( px = px0; px <= box.width; px += dx )
2179 if (
pal->isCanceled() )
2182 for ( py = py0; py <= box.length; py += dy )
2185 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
2186 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
2191 if (
mLF->permissibleZonePrepared() )
2197 numberCandidatesGenerated++;
2210 lPos.emplace_back( std::move( potentialCandidate ) );
2211 numberCandidatesGenerated++;
2218 nbp = numberCandidatesGenerated;
2219 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
2230 while ( numTry < maxTry );
2232 nbp = numberCandidatesGenerated;
2245 const std::size_t maxPolygonCandidates =
mLF->layer()->maximumPolygonLabelCandidates();
2246 std::size_t candidatesCreated = 0;
2308 return candidatesCreated;
2312 return candidatesCreated;
2316 const double ringLength = ring->
length();
2317 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2318 const std::size_t candidatesForArea =
static_cast< std::size_t
>( std::ceil(
mLF->layer()->mPal->maximumPolygonCandidatesPerMapUnitSquared() * circleArea ) );
2319 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2322 const double delta = ringLength / targetPolygonCandidates;
2325 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2326 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2327 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2330 const double labelAngle = 0;
2332 std::size_t i = lPos.size();
2340 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2343 if ( candidate->intersects( preparedBuffer.get() ) )
2361 const double centroidDistance = candidate->getDistanceToPoint( cx, cy,
false );
2362 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2363 candidate->setCost( centroidCost );
2365 lPos.emplace_back( std::move( candidate ) );
2366 candidatesCreated++;
2371 double startSegmentX,
double startSegmentY,
double,
double,
2372 double endSegmentX,
double endSegmentY,
double,
double )
2375 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2380 if ( angle >= 0 && angle <= 5 )
2385 else if ( angle <= 85 )
2389 else if ( angle <= 90 )
2395 else if ( angle <= 95 )
2400 else if ( angle <= 175 )
2404 else if ( angle <= 180 )
2410 else if ( angle <= 185 )
2415 else if ( angle <= 265 )
2419 else if ( angle <= 270 )
2424 else if ( angle <= 275 )
2429 else if ( angle <= 355 )
2439 return !
pal->isCanceled();
2442 return candidatesCreated;
2447 std::vector< std::unique_ptr< LabelPosition > > lPos;
2448 double angleInRadians =
mLF->hasFixedAngle() ?
mLF->fixedAngle() : 0.0;
2450 if (
mLF->hasFixedPosition() )
2467 case GEOS_LINESTRING:
2470 else if (
mLF->layer()->isCurved() )
2490 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2491 std::fabs(
ymax -
ymin ) < labelHeight ) )
2498 std::size_t created = 0;
2501 switch (
mLF->layer()->arrangement() )
2556 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2558 double sizeCost = 0;
2559 if ( geomType == GEOS_LINESTRING )
2561 const double l =
length();
2564 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2565 if ( l >= bbox_length / 4 )
2568 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2570 else if ( geomType == GEOS_POLYGON )
2572 const double a =
area();
2575 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2576 if ( a >= bbox_area / 16 )
2579 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2585 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2587 pos->setCost( pos->cost() + sizeCost / 100 );
2598 const double x1first =
x.front();
2599 const double x1last =
x.back();
2600 const double x2first = p2->
x.front();
2601 const double x2last = p2->
x.back();
2602 const double y1first =
y.front();
2603 const double y1last =
y.back();
2604 const double y2first = p2->
y.front();
2605 const double y2last = p2->
y.back();
2613 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2619 const double p2otherX = p2startTouches ? x2last : x2first;
2620 const double p2otherY = p2startTouches ? y2last : y2first;
2626#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=12 )
2627 return ( GEOSPreparedIntersectsXY_r( geosctxt,
preparedGeom(), p2otherX, p2otherY ) != 1 );
2629 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2630 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2632 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2635 catch ( QgsGeosException &e )
2637 qWarning(
"GEOS exception: %s", e.what() );
2647 if ( !other->
mGeos )
2656 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2659 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2667 mGeos = gTmp.release();
2676 catch ( QgsGeosException &e )
2678 qWarning(
"GEOS exception: %s", e.what() );
2686 if (
mLF->alwaysShow() )
2694 return mLF->priority() >= 0 ?
mLF->priority() :
mLF->layer()->priority();
2699 bool result =
false;
2701 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())
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