47 #include <QLinkedList>
57 mGeos =
const_cast<GEOSGeometry *
>( geom );
63 for (
int i = 0; i <
mHoles.count(); i++ )
65 mHoles.at( i )->holeOf =
this;
77 mHoles.last()->holeOf =
this;
91 const GEOSCoordSequence *coordSeq =
nullptr;
94 type = GEOSGeomTypeId_r( geosctxt, geom );
96 if (
type == GEOS_POLYGON )
98 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
100 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
102 for (
int i = 0; i < numHoles; ++i )
104 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
116 geom = GEOSGetExteriorRing_r( geosctxt, geom );
125 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
126 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
129 xmin =
ymin = std::numeric_limits<double>::max();
130 xmax =
ymax = std::numeric_limits<double>::lowest();
137 for (
int i = 0; i <
nbPoints; ++i )
139 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
140 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
142 GEOSCoordSeq_getX_r( geosctxt, coordSeq, i, &
x[i] );
143 GEOSCoordSeq_getY_r( geosctxt, coordSeq, i, &
y[i] );
171 if ( mCachedMaxLineCandidates > 0 )
172 return mCachedMaxLineCandidates;
174 const double l =
length();
179 if ( maxForLayer == 0 )
180 mCachedMaxLineCandidates = candidatesForLineLength;
182 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
186 mCachedMaxLineCandidates = 1;
188 return mCachedMaxLineCandidates;
193 if ( mCachedMaxPolygonCandidates > 0 )
194 return mCachedMaxPolygonCandidates;
196 const double a =
area();
201 if ( maxForLayer == 0 )
202 mCachedMaxPolygonCandidates = candidatesForArea;
204 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
208 mCachedMaxPolygonCandidates = 1;
210 return mCachedMaxPolygonCandidates;
232 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
234 if ( quadOffsetX < 0 )
236 if ( quadOffsetY < 0 )
240 else if ( quadOffsetY > 0 )
249 else if ( quadOffsetX > 0 )
251 if ( quadOffsetY < 0 )
255 else if ( quadOffsetY > 0 )
266 if ( quadOffsetY < 0 )
270 else if ( quadOffsetY > 0 )
283 return mTotalRepeats;
297 double cost = 0.00005;
298 int id = lPos.size();
300 double xdiff = -labelW / 2.0;
301 double ydiff = -labelH / 2.0;
305 double lx =
x + xdiff;
306 double ly =
y + ydiff;
326 double cost = 0.0001;
327 int id = lPos.size();
329 double xdiff = -labelW / 2.0;
330 double ydiff = -labelH / 2.0;
347 double xd = xdiff * std::cos(
angle ) - ydiff * std::sin(
angle );
348 double yd = xdiff * std::sin(
angle ) + ydiff * std::cos(
angle );
384 double lx =
x + xdiff;
385 double ly =
y + ydiff;
395 lPos.emplace_back( std::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH,
angle, cost,
this,
false, quadrantFromOffset() ) );
408 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
409 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
410 unsigned int nPoints = 0;
411 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
414 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
416 GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
417 GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
421 catch ( GEOSException &e )
423 qWarning(
"GEOS exception: %s", e.what() );
431 void createCandidateAtOrderedPositionOverPoint(
double &labelX,
double &labelY,
LabelPosition::Quadrant &quadrant,
double x,
double y,
double labelWidth,
double labelHeight,
Qgis::LabelPredefinedPointPosition position,
double distanceToLabel,
const QgsMargins &visualMargin,
double symbolWidthOffset,
double symbolHeightOffset,
double angle )
442 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
443 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
449 deltaX = -labelWidth / 4.0 - visualMargin.
left();
450 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
456 deltaX = -labelWidth / 2.0;
457 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
463 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
464 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
470 deltaX = - visualMargin.
left() + symbolWidthOffset;
471 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
477 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
478 deltaY = -labelHeight / 2.0;
484 deltaX = -visualMargin.
left() + symbolWidthOffset;
485 deltaY = -labelHeight / 2.0;
491 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
492 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
498 deltaX = -labelWidth / 4.0 - visualMargin.
left();
499 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
505 deltaX = -labelWidth / 2.0;
506 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
512 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
513 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
519 deltaX = -visualMargin.
left() + symbolWidthOffset;
520 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
526 QTransform transformRotation;
527 transformRotation.rotate(
angle * 180 / M_PI );
528 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
531 double referenceX = std::cos( alpha ) * distanceToLabel + x;
532 double referenceY = std::sin( alpha ) * distanceToLabel + y;
534 labelX = referenceX + deltaX;
535 labelY = referenceY + deltaY;
549 double cost = 0.0001;
550 std::size_t i = lPos.size();
553 std::size_t created = 0;
560 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset,
angle );
564 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
568 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
584 if ( maxNumberCandidates == 0 )
585 maxNumberCandidates = 16;
589 int id = lPos.size();
591 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
596 double a270 = a180 + a90;
597 double a360 = 2 * M_PI;
599 double gamma1, gamma2;
601 if ( distanceToLabel > 0 )
603 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
604 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
608 gamma1 = gamma2 = a90 / 3.0;
611 if ( gamma1 > a90 / 3.0 )
614 if ( gamma2 > a90 / 3.0 )
617 std::size_t numberCandidatesGenerated = 0;
620 double angleToCandidate;
621 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
626 if ( angleToCandidate > a360 )
627 angleToCandidate -= a360;
631 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
633 deltaX = distanceToLabel;
634 double iota = ( angleToCandidate + gamma1 );
635 if ( iota > a360 - gamma1 )
639 deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
643 else if ( angleToCandidate < a90 - gamma2 )
645 deltaX = distanceToLabel * std::cos( angleToCandidate );
646 deltaY = distanceToLabel * std::sin( angleToCandidate );
649 else if ( angleToCandidate < a90 + gamma2 )
652 deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
653 deltaY = distanceToLabel;
656 else if ( angleToCandidate < a180 - gamma1 )
658 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
659 deltaY = distanceToLabel * std::sin( angleToCandidate );
662 else if ( angleToCandidate < a180 + gamma1 )
664 deltaX = -distanceToLabel - labelWidth;
666 deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
669 else if ( angleToCandidate < a270 - gamma2 )
671 deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
672 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
675 else if ( angleToCandidate < a270 + gamma2 )
677 deltaY = -distanceToLabel - labelHeight;
679 deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
682 else if ( angleToCandidate < a360 )
684 deltaX = distanceToLabel * std::cos( angleToCandidate );
685 deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
691 QTransform transformRotation;
692 transformRotation.rotate(
angle * 180 / M_PI );
693 transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
695 double labelX =
x + deltaX;
696 double labelY =
y + deltaY;
700 if ( maxNumberCandidates == 1 )
703 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
714 lPos.emplace_back( std::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
715 numberCandidatesGenerated++;
719 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
721 icost =
static_cast< int >( maxNumberCandidates ) - 1;
724 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
726 icost =
static_cast< int >( maxNumberCandidates ) - 2;
732 return numberCandidatesGenerated;
739 double shapeLength = mapShape->
length();
750 std::size_t candidates = 0;
756 if ( candidates < candidateTargetCount )
771 std::vector< double > &
x = line->
x;
772 std::vector< double > &
y = line->
y;
774 std::vector< double > segmentLengths(
nbPoints - 1 );
775 std::vector< double >distanceToSegment(
nbPoints );
777 double totalLineLength = 0.0;
778 for (
int i = 0; i < line->
nbPoints - 1; i++ )
781 distanceToSegment[i] = 0;
783 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
786 totalLineLength += segmentLengths[i];
788 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
791 double lineStepDistance = 0;
794 double currentDistanceAlongLine = lineStepDistance;
798 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
802 currentDistanceAlongLine = lineAnchorPoint;
803 lineStepDistance = -1;
809 double candidateCenterX, candidateCenterY;
811 while ( currentDistanceAlongLine <= totalLineLength )
813 if (
pal->isCanceled() )
818 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
821 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
828 labelX = candidateCenterX;
831 labelX = candidateCenterX - labelWidth / 2;
834 labelX = candidateCenterX - labelWidth;
840 lPos.emplace_back( std::make_unique< LabelPosition >( i, labelX, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
842 currentDistanceAlongLine += lineStepDistance;
846 if ( lineStepDistance < 0 )
860 flags = QgsLabeling::LinePlacementFlag::OnLine;
863 QVector< int > extremeAngleNodes;
866 std::vector< double > &
x = line->
x;
867 std::vector< double > &
y = line->
y;
871 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
873 double x1 =
x[i - 1];
875 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
876 double y1 =
y[i - 1];
878 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
883 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
887 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
888 extremeAngleNodes << i;
890 extremeAngleNodes << numberNodes - 1;
892 if ( extremeAngleNodes.isEmpty() )
899 std::vector< double > segmentLengths( numberNodes - 1 );
900 std::vector< double > distanceToSegment( numberNodes );
901 double totalLineLength = 0.0;
902 QVector< double > straightSegmentLengths;
903 QVector< double > straightSegmentAngles;
904 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
905 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
906 double currentStraightSegmentLength = 0;
907 double longestSegmentLength = 0;
908 int segmentIndex = 0;
909 double segmentStartX =
x[0];
910 double segmentStartY =
y[0];
911 for (
int i = 0; i < numberNodes - 1; i++ )
914 distanceToSegment[i] = 0;
916 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
919 totalLineLength += segmentLengths[i];
920 if ( extremeAngleNodes.contains( i ) )
923 straightSegmentLengths << currentStraightSegmentLength;
925 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
927 currentStraightSegmentLength = 0;
928 segmentStartX =
x[i];
929 segmentStartY =
y[i];
931 currentStraightSegmentLength += segmentLengths[i];
933 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
934 straightSegmentLengths << currentStraightSegmentLength;
936 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
939 if ( totalLineLength < labelWidth )
947 double lineStepDistance = ( totalLineLength - labelWidth );
948 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
950 double distanceToEndOfSegment = 0.0;
951 int lastNodeInSegment = 0;
953 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
955 currentStraightSegmentLength = straightSegmentLengths.at( i );
956 double currentSegmentAngle = straightSegmentAngles.at( i );
957 lastNodeInSegment = extremeAngleNodes.at( i );
958 double distanceToStartOfSegment = distanceToEndOfSegment;
959 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
960 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
962 if ( currentStraightSegmentLength < labelWidth )
966 double currentDistanceAlongLine = distanceToStartOfSegment;
967 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
968 double candidateLength = 0.0;
974 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
975 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
977 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
979 if (
pal->isCanceled() )
985 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
986 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
988 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
994 cost = candidateLength / labelWidth;
1000 cost = ( 1 - cost ) / 100;
1003 const double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
1004 double labelTextAnchor = 0;
1005 switch ( textPoint )
1008 labelTextAnchor = currentDistanceAlongLine;
1011 labelTextAnchor = currentDistanceAlongLine + labelWidth / 2.0;
1014 labelTextAnchor = currentDistanceAlongLine + labelWidth;
1023 if ( placementIsFlexible )
1026 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
1027 cost += costCenter * 0.0005;
1035 double costLineCenter = 2 * std::fabs( labelTextAnchor - lineAnchorPoint ) / totalLineLength;
1036 cost += costLineCenter * 0.0005;
1039 if ( placementIsFlexible )
1041 cost += segmentCost * 0.0005;
1042 cost += segmentAngleCost * 0.0001;
1050 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1054 beta =
angle + M_PI_2;
1059 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1061 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1062 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1063 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1069 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1070 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1077 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1078 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1081 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1085 const double candidateCost = cost + 0.002;
1086 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1092 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1099 currentDistanceAlongLine += lineStepDistance;
1118 flags = QgsLabeling::LinePlacementFlag::OnLine;
1122 std::vector< double > &
x = line->
x;
1123 std::vector< double > &
y = line->
y;
1125 std::vector< double > segmentLengths(
nbPoints - 1 );
1126 std::vector< double >distanceToSegment(
nbPoints );
1128 double totalLineLength = 0.0;
1129 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1132 distanceToSegment[i] = 0;
1134 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1137 totalLineLength += segmentLengths[i];
1139 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1141 double lineStepDistance = ( totalLineLength - labelWidth );
1142 double currentDistanceAlongLine = 0;
1148 if ( totalLineLength > labelWidth )
1150 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1154 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1155 lineStepDistance = -1;
1156 totalLineLength = labelWidth;
1161 currentDistanceAlongLine = std::numeric_limits< double >::max();
1172 switch ( textPoint )
1175 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1178 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth / 2, totalLineLength * 0.99 - labelWidth );
1181 currentDistanceAlongLine = std::min( lineAnchorPoint - labelWidth, totalLineLength * 0.99 - labelWidth );
1187 lineStepDistance = -1;
1191 double candidateLength;
1193 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1197 if (
pal->isCanceled() )
1203 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1204 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1206 if ( currentDistanceAlongLine < 0 )
1214 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1217 cost = candidateLength / labelWidth;
1223 cost = ( 1 - cost ) / 100;
1227 double textAnchorPoint = 0;
1228 switch ( textPoint )
1231 textAnchorPoint = currentDistanceAlongLine;
1234 textAnchorPoint = currentDistanceAlongLine + labelWidth / 2;
1237 textAnchorPoint = currentDistanceAlongLine + labelWidth;
1243 double costCenter = std::fabs( lineAnchorPoint - textAnchorPoint ) / totalLineLength;
1244 cost += costCenter / 1000;
1245 cost += initialCost;
1252 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1256 beta =
angle + M_PI_2;
1261 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1263 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1264 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1265 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1271 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1272 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1279 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1280 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1283 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1287 const double candidateCost = cost + 0.002;
1288 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1294 lPos.emplace_back( std::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1301 currentDistanceAlongLine += lineStepDistance;
1305 if ( lineStepDistance < 0 )
1315 Q_ASSERT( metrics );
1317 const double maximumCharacterAngleInside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleInside() ) : -1;
1318 const double maximumCharacterAngleOutside = applyAngleConstraints ? std::fabs( qgis::down_cast< QgsTextLabelFeature *>(
mLF )->maximumCharacterAngleOutside() ) : -1;
1320 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement(
1324 labeledLineSegmentIsRightToLeft = !uprightOnly ? placement->labeledLineSegmentIsRightToLeft : placement->flippedCharacterPlacementToGetUprightLabels;
1326 if ( placement->graphemePlacement.empty() )
1329 auto it = placement->graphemePlacement.constBegin();
1330 std::unique_ptr< LabelPosition > firstPosition = std::make_unique< LabelPosition >( 0, it->x, it->y, it->width, it->height, it->angle, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1331 firstPosition->setUpsideDownCharCount( placement->upsideDownCharCount );
1332 firstPosition->setPartId( it->graphemeIndex );
1335 while ( it != placement->graphemePlacement.constEnd() )
1337 std::unique_ptr< LabelPosition > position = std::make_unique< LabelPosition >( 0, it->x, it->y, it->width, it->height, it->angle, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1338 position->setPartId( it->graphemeIndex );
1341 previousPosition->
setNextPart( std::move( position ) );
1342 previousPosition = nextPosition;
1346 return firstPosition;
1358 const int characterCount = li->
count();
1359 if ( characterCount == 0 )
1365 double totalCharacterWidth = 0;
1366 for (
int i = 0; i < characterCount; ++i )
1369 std::unique_ptr< PointSet > expanded;
1370 double shapeLength = mapShape->
length();
1373 allowOverrun =
false;
1391 if ( totalCharacterWidth > shapeLength )
1393 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1404 if ( allowOverrun && overrun > 0 )
1407 expanded = mapShape->
clone();
1409 mapShape = expanded.get();
1410 shapeLength += 2 * overrun;
1415 flags = QgsLabeling::LinePlacementFlag::OnLine;
1416 const bool hasAboveBelowLinePlacement = flags & QgsLabeling::LinePlacementFlag::AboveLine || flags & QgsLabeling::LinePlacementFlag::BelowLine;
1418 std::unique_ptr< PointSet > mapShapeOffsetPositive;
1419 bool positiveShapeHasNegativeDistance =
false;
1420 std::unique_ptr< PointSet > mapShapeOffsetNegative;
1421 bool negativeShapeHasNegativeDistance =
false;
1422 if ( hasAboveBelowLinePlacement && !
qgsDoubleNear( offsetDistance, 0 ) )
1425 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1426 mapShapeOffsetPositive = mapShape->
clone();
1427 if ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) || ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1428 mapShapeOffsetNegative = mapShape->
clone();
1429 if ( offsetDistance >= 0.0 || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
1431 if ( mapShapeOffsetPositive )
1432 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance );
1433 positiveShapeHasNegativeDistance = offsetDistance < 0;
1434 if ( mapShapeOffsetNegative )
1435 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance * -1 );
1436 negativeShapeHasNegativeDistance = offsetDistance > 0;
1441 if ( flags & QgsLabeling::LinePlacementFlag::AboveLine
1442 && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1444 flags &= ~
QgsLabeling::LinePlacementFlag::AboveLine;
1445 flags |= QgsLabeling::LinePlacementFlag::BelowLine;
1447 else if ( flags & QgsLabeling::LinePlacementFlag::BelowLine
1448 && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1450 flags &= ~
QgsLabeling::LinePlacementFlag::BelowLine;
1451 flags |= QgsLabeling::LinePlacementFlag::AboveLine;
1453 if ( mapShapeOffsetPositive )
1454 mapShapeOffsetPositive->offsetCurveByDistance( offsetDistance * -1 );
1455 positiveShapeHasNegativeDistance = offsetDistance > 0;
1456 if ( mapShapeOffsetNegative )
1457 mapShapeOffsetNegative->offsetCurveByDistance( offsetDistance );
1458 negativeShapeHasNegativeDistance = offsetDistance < 0;
1464 std::vector< std::unique_ptr< LabelPosition >> positions;
1465 std::unique_ptr< LabelPosition > backupPlacement;
1468 PointSet *currentMapShape =
nullptr;
1471 currentMapShape = mapShapeOffsetPositive.get();
1473 if ( offset ==
NoOffset && flags & QgsLabeling::LinePlacementFlag::OnLine )
1475 currentMapShape = mapShape;
1479 currentMapShape = mapShapeOffsetNegative.get();
1481 if ( !currentMapShape )
1485 const auto [ pathDistances, totalDistance ] = currentMapShape->
edgeDistances();
1489 double lineAnchorPoint = 0;
1490 if ( originalPoint && offset !=
NoOffset )
1495 lineAnchorPoint = currentMapShape->
lineLocatePoint( originalPoint.get() );
1501 lineAnchorPoint = totalDistance - lineAnchorPoint;
1504 if (
pal->isCanceled() )
1508 double delta = std::max( li->
characterHeight() / 6, totalDistance / candidateTargetCount );
1511 double distanceAlongLineToStartCandidate = 0;
1512 bool singleCandidateOnly =
false;
1519 switch ( textPoint )
1522 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint, 0.0, totalDistance * 0.999 );
1525 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth() / 2, 0.0, totalDistance * 0.999 -
getLabelWidth() / 2 );
1528 distanceAlongLineToStartCandidate = std::clamp( lineAnchorPoint -
getLabelWidth(), 0.0, totalDistance * 0.999 -
getLabelWidth() ) ;
1534 singleCandidateOnly =
true;
1538 bool hasTestedFirstPlacement =
false;
1539 for ( ; distanceAlongLineToStartCandidate <= totalDistance; distanceAlongLineToStartCandidate += delta )
1541 if ( singleCandidateOnly && hasTestedFirstPlacement )
1544 if (
pal->isCanceled() )
1547 hasTestedFirstPlacement =
true;
1549 bool labeledLineSegmentIsRightToLeft =
false;
1551 std::unique_ptr< LabelPosition > labelPosition =
curvedPlacementAtOffset( currentMapShape, pathDistances, direction, distanceAlongLineToStartCandidate, labeledLineSegmentIsRightToLeft, !singleCandidateOnly,
1552 onlyShowUprightLabels() && ( !singleCandidateOnly || !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ) );
1554 if ( !labelPosition )
1560 bool isBackupPlacementOnly =
false;
1561 if ( flags & QgsLabeling::LinePlacementFlag::MapOrientation )
1563 if ( ( currentMapShape == mapShapeOffsetPositive.get() && positiveShapeHasNegativeDistance )
1564 || ( currentMapShape == mapShapeOffsetNegative.get() && negativeShapeHasNegativeDistance ) )
1566 labeledLineSegmentIsRightToLeft = !labeledLineSegmentIsRightToLeft;
1569 if ( ( offset !=
NoOffset ) && !labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::AboveLine ) )
1572 isBackupPlacementOnly =
true;
1576 if ( ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft && !( flags & QgsLabeling::LinePlacementFlag::BelowLine ) )
1579 isBackupPlacementOnly =
true;
1585 backupPlacement.reset();
1588 const double angleDiff = labelPosition->angleDifferential();
1589 const double angleDiffAvg = characterCount > 1 ? ( angleDiff / ( characterCount - 1 ) ) : 0;
1594 double cost = angleDiffAvg / 100;
1595 if ( cost < 0.0001 )
1599 double labelTextAnchor = 0;
1600 switch ( textPoint )
1603 labelTextAnchor = distanceAlongLineToStartCandidate;
1606 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1609 labelTextAnchor = distanceAlongLineToStartCandidate +
getLabelWidth();
1615 double costCenter = std::fabs( lineAnchorPoint - labelTextAnchor ) / totalDistance;
1616 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1618 const bool isBelow = ( offset !=
NoOffset ) && labeledLineSegmentIsRightToLeft;
1630 labelPosition->setCost( cost );
1632 std::unique_ptr< LabelPosition > p = std::make_unique< LabelPosition >( *labelPosition );
1637 while ( within && currentPos )
1640 currentPos = currentPos->
nextPart();
1650 if ( isBackupPlacementOnly )
1651 backupPlacement = std::move( p );
1653 positions.emplace_back( std::move( p ) );
1658 for ( std::unique_ptr< LabelPosition > &pos : positions )
1660 lPos.emplace_back( std::move( pos ) );
1663 if ( backupPlacement )
1664 lPos.emplace_back( std::move( backupPlacement ) );
1666 return positions.size();
1690 const double totalArea =
area();
1692 mapShape->
parent =
nullptr;
1694 if (
pal->isCanceled() )
1697 QLinkedList<PointSet *> shapes_final =
splitPolygons( mapShape, labelWidth, labelHeight );
1699 QgsDebugMsg( QStringLiteral(
"PAL split polygons resulted in:" ) );
1700 for (
PointSet *ps : shapes_final )
1706 std::size_t nbp = 0;
1708 if ( !shapes_final.isEmpty() )
1716 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1718 std::vector< OrientedConvexHullBoundingBox > boxes;
1719 boxes.reserve( shapes_final.size() );
1722 while ( !shapes_final.isEmpty() )
1724 PointSet *shape = shapes_final.takeFirst();
1728 boxes.emplace_back( box );
1734 if (
pal->isCanceled() )
1738 double densityY = densityX;
1745 std::size_t numberCandidatesGenerated = 0;
1760 double dx = densityX;
1761 double dy = densityY;
1762 if ( numTry == 0 && maxPolygonCandidates > 0 )
1765 const double boxArea = box.width * box.length;
1766 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1767 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1771 if (
pal->isCanceled() )
1772 return numberCandidatesGenerated;
1791 bool enoughPlace =
false;
1795 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1796 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1802 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1804 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1808 enoughPlace =
false;
1824 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1826 if ( box.alpha <= M_PI_4 )
1832 alpha = box.alpha - M_PI_2;
1835 else if ( box.length > box.width )
1837 alpha = box.alpha - M_PI_2;
1844 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1850 dlx = std::cos( beta ) * diago;
1851 dly = std::sin( beta ) * diago;
1853 double px0 = box.width / 2.0;
1854 double py0 = box.length / 2.0;
1856 px0 -= std::ceil( px0 / dx ) * dx;
1857 py0 -= std::ceil( py0 / dy ) * dy;
1859 for ( px = px0; px <= box.width; px += dx )
1861 if (
pal->isCanceled() )
1864 for ( py = py0; py <= box.length; py += dy )
1867 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1868 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1878 lPos.emplace_back( std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1879 numberCandidatesGenerated++;
1890 std::unique_ptr< LabelPosition > potentialCandidate = std::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1892 lPos.emplace_back( std::move( potentialCandidate ) );
1893 numberCandidatesGenerated++;
1900 nbp = numberCandidatesGenerated;
1901 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1912 while ( numTry < maxTry );
1914 nbp = numberCandidatesGenerated;
1928 std::size_t candidatesCreated = 0;
1988 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1990 return candidatesCreated;
1994 return candidatesCreated;
1998 const double ringLength = ring->
length();
1999 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
2001 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
2004 const double delta = ringLength / targetPolygonCandidates;
2007 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
2008 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
2009 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
2012 const double labelAngle = 0;
2014 std::size_t i = lPos.size();
2022 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0, labelAngle );
2024 std::unique_ptr< LabelPosition > candidate = std::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
2025 if ( candidate->intersects( preparedBuffer.get() ) )
2043 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
2044 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
2045 candidate->setCost( centroidCost );
2047 lPos.emplace_back( std::move( candidate ) );
2048 candidatesCreated++;
2053 double startSegmentX,
double startSegmentY,
double,
double,
2054 double endSegmentX,
double endSegmentY,
double,
double )
2057 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2067 else if (
angle <= 85 )
2071 else if (
angle <= 90 )
2077 else if (
angle <= 95 )
2082 else if (
angle <= 175 )
2086 else if (
angle <= 180 )
2092 else if (
angle <= 185 )
2097 else if (
angle <= 265 )
2101 else if (
angle <= 270 )
2106 else if (
angle <= 275 )
2111 else if (
angle <= 355 )
2121 return !
pal->isCanceled();
2124 return candidatesCreated;
2129 std::vector< std::unique_ptr< LabelPosition > > lPos;
2149 case GEOS_LINESTRING:
2163 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2164 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2172 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2173 std::fabs(
ymax -
ymin ) < labelHeight ) )
2180 std::size_t created = 0;
2238 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2240 double sizeCost = 0;
2241 if ( geomType == GEOS_LINESTRING )
2243 const double l =
length();
2246 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2247 if ( l >= bbox_length / 4 )
2250 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2252 else if ( geomType == GEOS_POLYGON )
2254 const double a =
area();
2257 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2258 if ( a >= bbox_area / 16 )
2261 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2267 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2269 pos->setCost( pos->cost() + sizeCost / 100 );
2280 const double x1first =
x.front();
2281 const double x1last =
x.back();
2282 const double x2first = p2->
x.front();
2283 const double x2last = p2->
x.back();
2284 const double y1first =
y.front();
2285 const double y1last =
y.back();
2286 const double y2first = p2->
y.front();
2287 const double y2last = p2->
y.back();
2295 if ( ( !p2startTouches && !p2endTouches ) || ( p2startTouches && p2endTouches ) )
2301 const double p2otherX = p2startTouches ? x2last : x2first;
2302 const double p2otherY = p2startTouches ? y2last : y2first;
2306 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
2307 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
2308 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, p2otherX, p2otherY );
2310 GEOSCoordSeq_setX_r( geosctxt, coord, 0, p2otherX );
2311 GEOSCoordSeq_setY_r( geosctxt, coord, 0, p2otherY );
2317 return ( GEOSPreparedIntersects_r( geosctxt,
preparedGeom(), p2OtherEnd.get() ) != 1 );
2319 catch ( GEOSException &e )
2321 qWarning(
"GEOS exception: %s", e.what() );
2331 if ( !other->
mGeos )
2337 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2338 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2339 GEOSGeometry *geoms[2] = { g1, g2 };
2340 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2343 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2351 mGeos = gTmp.release();
2360 catch ( GEOSException &e )
2362 qWarning(
"GEOS exception: %s", e.what() );
2383 bool result =
false;
2387 case Qgis::UpsideDownLabelHandling::FlipUpsideDownLabels:
2390 case Qgis::UpsideDownLabelHandling::AllowUpsideDownWhenRotationIsDefined:
2397 case Qgis::UpsideDownLabelHandling::AlwaysAllowUpsideDown: