45 #include <QLinkedList>
55 mGeos =
const_cast<GEOSGeometry *
>( geom );
61 for (
int i = 0; i <
mHoles.count(); i++ )
63 mHoles.at( i )->holeOf =
this;
75 mHoles.last()->holeOf =
this;
89 const GEOSCoordSequence *coordSeq =
nullptr;
92 type = GEOSGeomTypeId_r( geosctxt, geom );
94 if (
type == GEOS_POLYGON )
96 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
98 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
100 for (
int i = 0; i < numHoles; ++i )
102 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
114 geom = GEOSGetExteriorRing_r( geosctxt, geom );
123 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
124 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
127 xmin =
ymin = std::numeric_limits<double>::max();
128 xmax =
ymax = std::numeric_limits<double>::lowest();
135 for (
int i = 0; i <
nbPoints; ++i )
137 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
138 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, i, &
x[i], &
y[i] );
140 GEOSCoordSeq_getX_r( geosctxt, coordSeq, i, &
x[i] );
141 GEOSCoordSeq_getY_r( geosctxt, coordSeq, i, &
y[i] );
169 if ( mCachedMaxLineCandidates > 0 )
170 return mCachedMaxLineCandidates;
172 const double l =
length();
177 if ( maxForLayer == 0 )
178 mCachedMaxLineCandidates = candidatesForLineLength;
180 mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
184 mCachedMaxLineCandidates = 1;
186 return mCachedMaxLineCandidates;
191 if ( mCachedMaxPolygonCandidates > 0 )
192 return mCachedMaxPolygonCandidates;
194 const double a =
area();
199 if ( maxForLayer == 0 )
200 mCachedMaxPolygonCandidates = candidatesForArea;
202 mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
206 mCachedMaxPolygonCandidates = 1;
208 return mCachedMaxPolygonCandidates;
230 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
232 if ( quadOffsetX < 0 )
234 if ( quadOffsetY < 0 )
238 else if ( quadOffsetY > 0 )
247 else if ( quadOffsetX > 0 )
249 if ( quadOffsetY < 0 )
253 else if ( quadOffsetY > 0 )
264 if ( quadOffsetY < 0 )
268 else if ( quadOffsetY > 0 )
281 return mTotalRepeats;
295 double cost = 0.00005;
296 int id = lPos.size();
298 double xdiff = -labelW / 2.0;
299 double ydiff = -labelH / 2.0;
303 double lx =
x + xdiff;
304 double ly =
y + ydiff;
324 double cost = 0.0001;
325 int id = lPos.size();
327 double xdiff = -labelW / 2.0;
328 double ydiff = -labelH / 2.0;
345 double xd = xdiff * std::cos(
angle ) - ydiff * std::sin(
angle );
346 double yd = xdiff * std::sin(
angle ) + ydiff * std::cos(
angle );
382 double lx =
x + xdiff;
383 double ly =
y + ydiff;
393 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id, lx, ly, labelW, labelH,
angle, cost,
this,
false, quadrantFromOffset() ) );
406 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
407 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
408 unsigned int nPoints = 0;
409 GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
412 GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
414 GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
415 GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
419 catch ( GEOSException &e )
421 qWarning(
"GEOS exception: %s", e.what() );
429 void createCandidateAtOrderedPositionOverPoint(
double &labelX,
double &labelY,
LabelPosition::Quadrant &quadrant,
double x,
double y,
double labelWidth,
double labelHeight,
QgsPalLayerSettings::PredefinedPointPosition position,
double distanceToLabel,
const QgsMargins &visualMargin,
double symbolWidthOffset,
double symbolHeightOffset )
439 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
440 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
446 deltaX = -labelWidth / 4.0 - visualMargin.
left();
447 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
453 deltaX = -labelWidth / 2.0;
454 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
460 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
461 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
467 deltaX = - visualMargin.
left() + symbolWidthOffset;
468 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
474 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
475 deltaY = -labelHeight / 2.0;
481 deltaX = -visualMargin.
left() + symbolWidthOffset;
482 deltaY = -labelHeight / 2.0;
488 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
489 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
495 deltaX = -labelWidth / 4.0 - visualMargin.
left();
496 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
502 deltaX = -labelWidth / 2.0;
503 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
509 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
510 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
516 deltaX = -visualMargin.
left() + symbolWidthOffset;
517 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
522 double referenceX = std::cos( alpha ) * distanceToLabel + x;
523 double referenceY = std::sin( alpha ) * distanceToLabel + y;
525 labelX = referenceX + deltaX;
526 labelY = referenceY + deltaY;
540 double cost = 0.0001;
541 std::size_t i = lPos.size();
544 std::size_t created = 0;
551 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset );
555 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
559 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
575 if ( maxNumberCandidates == 0 )
576 maxNumberCandidates = 16;
580 int id = lPos.size();
582 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
587 double a270 = a180 + a90;
588 double a360 = 2 * M_PI;
590 double gamma1, gamma2;
592 if ( distanceToLabel > 0 )
594 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
595 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
599 gamma1 = gamma2 = a90 / 3.0;
602 if ( gamma1 > a90 / 3.0 )
605 if ( gamma2 > a90 / 3.0 )
608 std::size_t numberCandidatesGenerated = 0;
611 double angleToCandidate;
612 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
617 if ( angleToCandidate > a360 )
618 angleToCandidate -= a360;
622 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
624 labelX += distanceToLabel;
625 double iota = ( angleToCandidate + gamma1 );
626 if ( iota > a360 - gamma1 )
630 labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
634 else if ( angleToCandidate < a90 - gamma2 )
636 labelX += distanceToLabel * std::cos( angleToCandidate );
637 labelY += distanceToLabel * std::sin( angleToCandidate );
640 else if ( angleToCandidate < a90 + gamma2 )
643 labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
644 labelY += distanceToLabel;
647 else if ( angleToCandidate < a180 - gamma1 )
649 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
650 labelY += distanceToLabel * std::sin( angleToCandidate );
653 else if ( angleToCandidate < a180 + gamma1 )
655 labelX += -distanceToLabel - labelWidth;
657 labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
660 else if ( angleToCandidate < a270 - gamma2 )
662 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
663 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
666 else if ( angleToCandidate < a270 + gamma2 )
668 labelY += -distanceToLabel - labelHeight;
670 labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
673 else if ( angleToCandidate < a360 )
675 labelX += distanceToLabel * std::cos( angleToCandidate );
676 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
682 if ( maxNumberCandidates == 1 )
685 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
696 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
697 numberCandidatesGenerated++;
701 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
703 icost =
static_cast< int >( maxNumberCandidates ) - 1;
706 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
708 icost =
static_cast< int >( maxNumberCandidates ) - 2;
714 return numberCandidatesGenerated;
721 double shapeLength = mapShape->
length();
732 std::size_t candidates = 0;
738 if ( candidates < candidateTargetCount )
753 std::vector< double > &
x = line->
x;
754 std::vector< double > &
y = line->
y;
756 std::vector< double > segmentLengths(
nbPoints - 1 );
757 std::vector< double >distanceToSegment(
nbPoints );
759 double totalLineLength = 0.0;
760 for (
int i = 0; i < line->
nbPoints - 1; i++ )
763 distanceToSegment[i] = 0;
765 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
768 totalLineLength += segmentLengths[i];
770 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
773 double lineStepDistance = 0;
776 double currentDistanceAlongLine = lineStepDistance;
780 lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
784 currentDistanceAlongLine = lineAnchorPoint;
785 lineStepDistance = -1;
789 double candidateCenterX, candidateCenterY;
791 while ( currentDistanceAlongLine <= totalLineLength )
793 if (
pal->isCanceled() )
798 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
801 double cost = std::fabs( lineAnchorPoint - currentDistanceAlongLine ) / totalLineLength;
804 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateCenterX - labelWidth / 2, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
806 currentDistanceAlongLine += lineStepDistance;
810 if ( lineStepDistance < 0 )
824 flags = QgsLabeling::LinePlacementFlag::OnLine;
827 QVector< int > extremeAngleNodes;
830 std::vector< double > &
x = line->
x;
831 std::vector< double > &
y = line->
y;
835 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
837 double x1 =
x[i - 1];
839 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
840 double y1 =
y[i - 1];
842 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
847 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
851 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
852 extremeAngleNodes << i;
854 extremeAngleNodes << numberNodes - 1;
856 if ( extremeAngleNodes.isEmpty() )
863 std::vector< double > segmentLengths( numberNodes - 1 );
864 std::vector< double > distanceToSegment( numberNodes );
865 double totalLineLength = 0.0;
866 QVector< double > straightSegmentLengths;
867 QVector< double > straightSegmentAngles;
868 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
869 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
870 double currentStraightSegmentLength = 0;
871 double longestSegmentLength = 0;
872 int segmentIndex = 0;
873 double segmentStartX =
x[0];
874 double segmentStartY =
y[0];
875 for (
int i = 0; i < numberNodes - 1; i++ )
878 distanceToSegment[i] = 0;
880 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
883 totalLineLength += segmentLengths[i];
884 if ( extremeAngleNodes.contains( i ) )
887 straightSegmentLengths << currentStraightSegmentLength;
889 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
891 currentStraightSegmentLength = 0;
892 segmentStartX =
x[i];
893 segmentStartY =
y[i];
895 currentStraightSegmentLength += segmentLengths[i];
897 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
898 straightSegmentLengths << currentStraightSegmentLength;
900 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
903 if ( totalLineLength < labelWidth )
909 double lineStepDistance = ( totalLineLength - labelWidth );
910 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
912 double distanceToEndOfSegment = 0.0;
913 int lastNodeInSegment = 0;
915 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
917 currentStraightSegmentLength = straightSegmentLengths.at( i );
918 double currentSegmentAngle = straightSegmentAngles.at( i );
919 lastNodeInSegment = extremeAngleNodes.at( i );
920 double distanceToStartOfSegment = distanceToEndOfSegment;
921 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
922 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
924 if ( currentStraightSegmentLength < labelWidth )
928 double currentDistanceAlongLine = distanceToStartOfSegment;
929 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
930 double candidateLength = 0.0;
936 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
937 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
939 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
941 if (
pal->isCanceled() )
947 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
948 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
950 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
956 cost = candidateLength / labelWidth;
962 cost = ( 1 - cost ) / 100;
966 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
968 if ( placementIsFlexible )
971 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
972 cost += costCenter * 0.0005;
980 double costLineCenter = 2 * std::fabs( labelCenter - lineAnchorPoint ) / totalLineLength;
981 cost += costLineCenter * 0.0005;
984 if ( placementIsFlexible )
986 cost += segmentCost * 0.0005;
987 cost += segmentAngleCost * 0.0001;
995 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
999 beta =
angle + M_PI_2;
1004 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1006 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1007 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1008 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1014 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
1015 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1022 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1023 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1026 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1030 const double candidateCost = cost + 0.002;
1031 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1037 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1044 currentDistanceAlongLine += lineStepDistance;
1063 flags = QgsLabeling::LinePlacementFlag::OnLine;
1067 std::vector< double > &
x = line->
x;
1068 std::vector< double > &
y = line->
y;
1070 std::vector< double > segmentLengths(
nbPoints - 1 );
1071 std::vector< double >distanceToSegment(
nbPoints );
1073 double totalLineLength = 0.0;
1074 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1077 distanceToSegment[i] = 0;
1079 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1082 totalLineLength += segmentLengths[i];
1084 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1086 double lineStepDistance = ( totalLineLength - labelWidth );
1087 double currentDistanceAlongLine = 0;
1091 if ( totalLineLength > labelWidth )
1093 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1097 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1098 lineStepDistance = -1;
1099 totalLineLength = labelWidth;
1104 currentDistanceAlongLine = std::numeric_limits< double >::max();
1115 currentDistanceAlongLine = std::min( lineAnchorPoint, totalLineLength * 0.99 - labelWidth );
1116 lineStepDistance = -1;
1120 double candidateLength;
1122 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1126 if (
pal->isCanceled() )
1132 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1133 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1135 if ( currentDistanceAlongLine < 0 )
1143 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1146 cost = candidateLength / labelWidth;
1152 cost = ( 1 - cost ) / 100;
1156 double costCenter = std::fabs( lineAnchorPoint - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
1157 cost += costCenter / 1000;
1158 cost += initialCost;
1165 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1169 beta =
angle + M_PI_2;
1174 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1176 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1177 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1178 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1184 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1185 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1192 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1193 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1196 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1200 const double candidateCost = cost + 0.002;
1201 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight,
angle, candidateCost,
this, isRightToLeft,
LabelPosition::QuadrantOver ) );
1207 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1214 currentDistanceAlongLine += lineStepDistance;
1218 if ( lineStepDistance < 0 )
1228 double offsetAlongSegment = offsetAlongLine;
1231 while ( index < path_positions->
nbPoints && offsetAlongSegment > path_distances[index] )
1233 offsetAlongSegment -= path_distances[index];
1236 if ( index >= path_positions->
nbPoints )
1245 const double segment_length = path_distances[index];
1252 if ( orientation == 0 )
1256 double _distance = offsetAlongSegment;
1257 int endindex = index;
1259 double startLabelX = 0;
1260 double startLabelY = 0;
1261 double endLabelX = 0;
1262 double endLabelY = 0;
1263 for (
int i = 0; i < li->
char_num; i++ )
1266 double characterStartX, characterStartY;
1267 if ( !
nextCharPosition( ci.
width, path_distances[endindex], path_positions, endindex, _distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
1273 startLabelX = characterStartX;
1274 startLabelY = characterStartY;
1279 double dx = endLabelX - startLabelX;
1280 double dy = endLabelY - startLabelY;
1281 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
1283 bool isRightToLeft = ( lineAngle > 90 || lineAngle < -90 );
1284 reversed = isRightToLeft;
1285 orientation = isRightToLeft ? -1 : 1;
1290 if ( orientation < 0 )
1293 reversed = !reversed;
1298 std::unique_ptr< LabelPosition > slp;
1301 double old_x = path_positions->
x[index - 1];
1302 double old_y = path_positions->
y[index - 1];
1304 double new_x = path_positions->
x[index];
1305 double new_y = path_positions->
y[index];
1307 double dx = new_x - old_x;
1308 double dy = new_y - old_y;
1310 double angle = std::atan2( -dy, dx );
1312 for (
int i = 0; i < li->
char_num; i++ )
1314 double last_character_angle =
angle;
1322 double start_x, start_y, end_x, end_y;
1323 if ( !
nextCharPosition( ci.
width, path_distances[index], path_positions, index, offsetAlongSegment, start_x, start_y, end_x, end_y ) )
1329 angle = std::atan2( start_y - end_y, end_x - start_x );
1334 double angle_delta = last_character_angle -
angle;
1336 while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
1337 while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
1341 && angle_delta < li->max_char_angle_outside * ( M_PI / 180 ) ) ) )
1349 if ( orientation < 0 )
1354 start_x += dist * std::cos(
angle + M_PI_2 );
1355 start_y -= dist * std::sin(
angle + M_PI_2 );
1357 double render_angle =
angle;
1359 double render_x = start_x;
1360 double render_y = start_y;
1366 if ( orientation < 0 )
1369 render_x += ci.
width * std::cos( render_angle );
1370 render_y -= ci.
width * std::sin( render_angle );
1371 render_angle += M_PI;
1374 std::unique_ptr< LabelPosition > tmp = qgis::make_unique< LabelPosition >( 0, render_x , render_y , ci.
width, string_height, -render_angle, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1375 tmp->setPartId( orientation > 0 ? i : li->
char_num - i - 1 );
1378 slp = std::move( tmp );
1384 while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
1385 while ( render_angle < 0 ) render_angle += 2 * M_PI;
1387 if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI )
1394 static std::unique_ptr< LabelPosition > _createCurvedCandidate(
LabelPosition *lp,
double angle,
double dist )
1396 std::unique_ptr< LabelPosition > newLp = qgis::make_unique< LabelPosition >( *lp );
1397 newLp->offsetPosition( dist * std::cos(
angle + M_PI_2 ), dist * std::sin(
angle + M_PI_2 ) );
1412 double totalCharacterWidth = 0;
1413 for (
int i = 0; i < li->
char_num; ++i )
1416 std::unique_ptr< PointSet > expanded;
1417 double shapeLength = mapShape->
length();
1420 allowOverrun =
false;
1426 if ( totalCharacterWidth > shapeLength )
1428 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1435 if ( allowOverrun && overrun > 0 )
1438 expanded = mapShape->
clone();
1440 mapShape = expanded.get();
1441 shapeLength = mapShape->
length();
1445 std::unique_ptr< double [] > path_distances = qgis::make_unique<double[]>( mapShape->
nbPoints );
1446 double total_distance = 0;
1447 double old_x = -1.0, old_y = -1.0;
1448 for (
int i = 0; i < mapShape->
nbPoints; i++ )
1451 path_distances[i] = 0;
1453 path_distances[i] = std::sqrt( std::pow( old_x - mapShape->
x[i], 2 ) + std::pow( old_y - mapShape->
y[i], 2 ) );
1454 old_x = mapShape->
x[i];
1455 old_y = mapShape->
y[i];
1457 total_distance += path_distances[i];
1467 if (
pal->isCanceled() )
1470 std::vector< std::unique_ptr< LabelPosition >> positions;
1472 double delta = std::max( li->
label_height / 6, total_distance / candidateTargetCount );
1476 flags = QgsLabeling::LinePlacementFlag::OnLine;
1479 double distanceAlongLineToStartCandidate = 0;
1480 bool singleCandidateOnly =
false;
1487 distanceAlongLineToStartCandidate = std::min( lineAnchorPoint, total_distance * 0.99 -
getLabelWidth() );
1488 singleCandidateOnly =
true;
1492 for ( ; distanceAlongLineToStartCandidate <= total_distance; distanceAlongLineToStartCandidate += delta )
1496 bool reversed =
false;
1498 if (
pal->isCanceled() )
1502 int orientation = 0;
1503 if ( !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
1510 std::unique_ptr< LabelPosition > slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip, !singleCandidateOnly );
1515 if ( slp->upsideDownCharCount() >= li->
char_num / 2.0 )
1520 orientation = -orientation;
1521 slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip, !singleCandidateOnly );
1528 double angle_diff = 0.0, angle_last = 0.0, diff;
1530 double sin_avg = 0, cos_avg = 0;
1533 if ( tmp != slp.get() )
1535 diff = std::fabs( tmp->
getAlpha() - angle_last );
1536 if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
1537 diff = std::min( diff, 2 * M_PI - diff );
1541 sin_avg += std::sin( tmp->
getAlpha() );
1542 cos_avg += std::cos( tmp->
getAlpha() );
1550 double angle_diff_avg = li->
char_num > 1 ? ( angle_diff / ( li->
char_num - 1 ) ) : 0;
1551 double cost = angle_diff_avg / 100;
1552 if ( cost < 0.0001 )
1556 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1557 double costCenter = std::fabs( lineAnchorPoint - labelCenter ) / total_distance;
1558 cost += costCenter / ( anchorIsFlexiblePlacement ? 100 : 10 );
1559 slp->setCost( cost );
1562 double angle_avg = std::atan2( sin_avg / li->
char_num, cos_avg / li->
char_num );
1563 bool localreversed = flip ? !reversed : reversed;
1565 for (
int i = 0; i <= 2; ++i )
1567 std::unique_ptr< LabelPosition > p;
1568 if ( i == 0 && ( ( !localreversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( localreversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) ) )
1570 if ( i == 1 && flags & QgsLabeling::LinePlacementFlag::OnLine )
1572 p = _createCurvedCandidate( slp.get(), angle_avg, 0 );
1573 p->setCost( p->cost() + 0.002 );
1575 if ( i == 2 && ( ( !localreversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( localreversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) ) )
1578 p->setCost( p->cost() + 0.001 );
1585 while ( within && currentPos )
1588 currentPos = currentPos->
nextPart();
1597 positions.emplace_back( std::move( p ) );
1599 if ( singleCandidateOnly )
1603 for ( std::unique_ptr< LabelPosition > &pos : positions )
1605 lPos.emplace_back( std::move( pos ) );
1608 return positions.size();
1632 QLinkedList<PointSet *> shapes_toProcess;
1633 QLinkedList<PointSet *> shapes_final;
1634 const double totalArea =
area();
1636 mapShape->
parent =
nullptr;
1638 if (
pal->isCanceled() )
1641 shapes_toProcess.append( mapShape );
1643 splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight );
1645 std::size_t nbp = 0;
1647 if ( !shapes_final.isEmpty() )
1655 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1657 std::vector< OrientedConvexHullBoundingBox > boxes;
1658 boxes.reserve( shapes_final.size() );
1661 while ( !shapes_final.isEmpty() )
1663 PointSet *shape = shapes_final.takeFirst();
1667 boxes.emplace_back( box );
1673 if (
pal->isCanceled() )
1677 double densityY = densityX;
1684 std::size_t numberCandidatesGenerated = 0;
1699 double dx = densityX;
1700 double dy = densityY;
1701 if ( numTry == 0 && maxPolygonCandidates > 0 )
1704 const double boxArea = box.width * box.length;
1705 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1706 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1710 if (
pal->isCanceled() )
1711 return numberCandidatesGenerated;
1730 bool enoughPlace =
false;
1734 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1735 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1741 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1743 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1747 enoughPlace =
false;
1763 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1765 if ( box.alpha <= M_PI_4 )
1771 alpha = box.alpha - M_PI_2;
1774 else if ( box.length > box.width )
1776 alpha = box.alpha - M_PI_2;
1783 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1789 dlx = std::cos( beta ) * diago;
1790 dly = std::sin( beta ) * diago;
1792 double px0 = box.width / 2.0;
1793 double py0 = box.length / 2.0;
1795 px0 -= std::ceil( px0 / dx ) * dx;
1796 py0 -= std::ceil( py0 / dy ) * dy;
1798 for ( px = px0; px <= box.width; px += dx )
1800 if (
pal->isCanceled() )
1803 for ( py = py0; py <= box.length; py += dy )
1806 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1807 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1817 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1818 numberCandidatesGenerated++;
1829 std::unique_ptr< LabelPosition > potentialCandidate = qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1831 lPos.emplace_back( std::move( potentialCandidate ) );
1832 numberCandidatesGenerated++;
1839 nbp = numberCandidatesGenerated;
1840 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1851 while ( numTry < maxTry );
1853 nbp = numberCandidatesGenerated;
1867 std::size_t candidatesCreated = 0;
1927 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1929 return candidatesCreated;
1933 return candidatesCreated;
1937 const double ringLength = ring->
length();
1938 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
1940 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
1943 const double delta = ringLength / targetPolygonCandidates;
1946 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
1947 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
1948 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
1951 const double labelAngle = 0;
1953 std::size_t i = lPos.size();
1961 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0 );
1963 std::unique_ptr< LabelPosition > candidate = qgis::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
1964 if ( candidate->intersects( preparedBuffer.get() ) )
1982 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
1983 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
1984 candidate->setCost( centroidCost );
1986 lPos.emplace_back( std::move( candidate ) );
1987 candidatesCreated++;
1992 double startSegmentX,
double startSegmentY,
double,
double,
1993 double endSegmentX,
double endSegmentY,
double,
double )
1996 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2006 else if (
angle <= 85 )
2010 else if (
angle <= 90 )
2016 else if (
angle <= 95 )
2021 else if (
angle <= 175 )
2025 else if (
angle <= 180 )
2031 else if (
angle <= 185 )
2036 else if (
angle <= 265 )
2040 else if (
angle <= 270 )
2045 else if (
angle <= 275 )
2050 else if (
angle <= 355 )
2060 return !
pal->isCanceled();
2063 return candidatesCreated;
2068 std::vector< std::unique_ptr< LabelPosition > > lPos;
2088 case GEOS_LINESTRING:
2102 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2103 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2111 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2112 std::fabs(
ymax -
ymin ) < labelHeight ) )
2119 std::size_t created = 0;
2177 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2179 double sizeCost = 0;
2180 if ( geomType == GEOS_LINESTRING )
2182 const double l =
length();
2185 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2186 if ( l >= bbox_length / 4 )
2189 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2191 else if ( geomType == GEOS_POLYGON )
2193 const double a =
area();
2196 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2197 if ( a >= bbox_area / 16 )
2200 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2206 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2208 pos->setCost( pos->cost() + sizeCost / 100 );
2221 catch ( GEOSException &e )
2223 qWarning(
"GEOS exception: %s", e.what() );
2233 if ( !other->
mGeos )
2239 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2240 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2241 GEOSGeometry *geoms[2] = { g1, g2 };
2242 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2245 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2253 mGeos = gTmp.release();
2262 catch ( GEOSException &e )
2264 qWarning(
"GEOS exception: %s", e.what() );
2285 bool uprightLabel =
false;
2290 uprightLabel =
true;
2296 uprightLabel =
true;
2302 uprightLabel =
true;
2304 return uprightLabel;
2308 double &characterStartX,
double &characterStartY,
double &characterEndX,
double &characterEndY )
const
2317 double segmentStartX = path_positions->
x[index - 1];
2318 double segmentStartY = path_positions->
y[index - 1];
2320 double segmentEndX = path_positions->
x[index];
2321 double segmentEndY = path_positions->
y[index];
2323 double segmentDx = segmentEndX - segmentStartX;
2324 double segmentDy = segmentEndY - segmentStartY;
2326 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2327 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2333 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
2336 currentDistanceAlongSegment += charWidth;
2337 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2338 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2346 segmentStartX = segmentEndX;
2347 segmentStartY = segmentEndY;
2349 if ( index >= path_positions->
nbPoints )
2353 segmentEndX = path_positions->
x[index];
2354 segmentEndY = path_positions->
y[index];
2356 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth );
2362 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
static double normalizedAngle(double angle) SIP_HOLDGIL
Ensures that an angle is in the range 0 <= angle < 2 pi.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
static GEOSContextHandle_t getGEOSHandler()
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
double overrunSmoothDistance() const
Returns the distance (in map units) with which the ends of linear features are averaged over when cal...
QVector< QgsPalLayerSettings::PredefinedPointPosition > predefinedPositionOrder() const
Returns the priority ordered list of predefined positions for label candidates.
pal::Layer * layer() const
Gets PAL layer of the label feature. Should be only used internally in PAL.
double fixedAngle() const
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true)
const QSizeF & symbolSize() const
Returns the size of the rendered symbol associated with this feature, if applicable.
QgsPalLayerSettings::OffsetType offsetType() const
Returns the offset type, which determines how offsets and distance to label behaves.
QgsLabeling::PolygonPlacementFlags polygonPlacementFlags() const
Returns the polygon placement flags, which dictate how polygon labels can be placed.
QgsPointXY positionOffset() const
Applies only to "offset from point" placement strategy.
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
QgsLabeling::LinePlacementFlags arrangementFlags() const
Returns the feature's arrangement flags.
bool alwaysShow() const
Whether label should be always shown (sets very high label priority)
double lineAnchorPercent() const
Returns the percent along the line at which labels should be placed, for line labels only.
const QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
pal::LabelInfo * curvedLabelInfo() const
Gets additional info required for curved label placement. Returns nullptr if not set.
QgsLabelLineSettings::AnchorType lineAnchorType() const
Returns the line anchor type, which dictates how the lineAnchorPercent() setting is handled.
double distLabel() const
Applies to "around point" placement strategy or linestring features.
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
void setAnchorPosition(const QgsPointXY &anchorPosition)
In case of quadrand or aligned positioning, this is set to the anchor point.
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)
double overrunDistance() const
Returns the permissible distance (in map units) which labels are allowed to overrun the start or end ...
double priority() const
Returns the feature's labeling priority.
QgsGeometry permissibleZone() const
Returns the label's permissible zone geometry.
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
const GEOSPreparedGeometry * permissibleZonePrepared() const
Returns a GEOS prepared geometry representing the label's permissibleZone().
QgsPointXY fixedPosition() const
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true)
@ Strict
Line anchor is a strict placement, and other placements are not permitted.
@ HintOnly
Line anchor is a hint for preferred placement only, but other placements close to the hint are permit...
Line string geometry type, with support for z-dimension and m-values.
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
void visitPointsByRegularDistance(double distance, const std::function< bool(double x, double y, double z, double m, double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM, double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM) > &visitPoint) const
Visits regular points along the linestring, spaced by distance.
The QgsMargins class defines the four margins of a rectangle.
double top() const
Returns the top margin.
double right() const
Returns the right margin.
double bottom() const
Returns the bottom margin.
double left() const
Returns the left margin.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ 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'...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
PredefinedPointPosition
Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomMiddle
Label directly below point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ MiddleLeft
Label on left of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
@ MiddleRight
Label on right of point.
@ TopMiddle
Label directly above point.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ TopRight
Label on top-right of point.
@ TopLeft
Label on top-left of point.
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
A class to represent a 2D point.
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Main class to handle feature.
std::size_t createCandidatesAroundPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate candidates for point feature, located around a specified point.
std::size_t createCandidatesOutsidePolygon(std::vector< std::unique_ptr< LabelPosition > > &lPos, Pal *pal)
Generate candidates outside of polygon features.
bool hasFixedRotation() const
Returns true if the feature's label has a fixed rotation.
double getLabelHeight(double angle=0.0) const
Returns the height of the label, optionally taking an angle into account.
QList< FeaturePart * > mHoles
double getLabelDistance() const
Returns the distance from the anchor point to the label.
~FeaturePart() override
Delete the feature.
bool hasFixedPosition() const
Returns true if the feature's label has a fixed position.
std::unique_ptr< LabelPosition > curvedPlacementAtOffset(PointSet *path_positions, double *path_distances, int &orientation, double distance, bool &reversed, bool &flip, bool applyAngleConstraints)
Returns the label position for a curved label at a specific offset along a path.
std::size_t createCandidatesForPolygon(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate candidates for polygon features.
void setTotalRepeats(int repeats)
Returns the total number of repeating labels associated with this label.
std::size_t maximumPolygonCandidates() const
Returns the maximum number of polygon candidates to generate for this feature.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
std::size_t createCandidatesAlongLineNearStraightSegments(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate candidates for line feature, by trying to place candidates towards the middle of the longest...
bool hasSameLabelFeatureAs(FeaturePart *part) const
Tests whether this feature part belongs to the same QgsLabelFeature as another feature part.
void addSizePenalty(std::vector< std::unique_ptr< LabelPosition > > &lPos, double bbx[4], double bby[4])
Increases the cost of the label candidates for this feature, based on the size of the feature.
double fixedAngle() const
Returns the fixed angle for the feature's label.
std::size_t maximumLineCandidates() const
Returns the maximum number of line candidates to generate for this feature.
std::size_t createHorizontalCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, Pal *pal)
Generate horizontal candidates for line feature.
std::size_t createCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate candidates for line feature.
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged).
std::size_t createCurvedCandidatesAlongLine(std::vector< std::unique_ptr< LabelPosition > > &lPos, PointSet *mapShape, bool allowOverrun, Pal *pal)
Generate curved candidates for line features.
std::size_t createCandidatesOverPoint(double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle)
Generate one candidate over or offset the specified point.
std::unique_ptr< LabelPosition > createCandidatePointOnSurface(PointSet *mapShape)
Creates a single candidate using the "point on sruface" algorithm.
double getLabelWidth(double angle=0.0) const
Returns the width of the label, optionally taking an angle into account.
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.
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'...
bool nextCharPosition(double charWidth, double segmentLength, PointSet *path_positions, int &index, double ¤tDistanceAlongSegment, double &characterStartX, double &characterStartY, double &characterEndX, double &characterEndY) const
Returns true if the next char position is found. The referenced parameters are updated.
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.
QgsLabelFeature * feature()
Returns the parent feature.
bool showUprightLabels() const
Returns true if feature's label must be displayed upright.
std::size_t maximumPointCandidates() const
Returns the maximum number of point candidates to generate for this feature.
static double dist_euc2d(double x1, double y1, double x2, double y2)
static int reorderPolygon(int nbPoints, 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.
static void findLineCircleIntersection(double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes)
Optional additional info about label (for curved labels)
double max_char_angle_outside
double max_char_angle_inside
CharacterInfo * char_info
LabelPosition is a candidate feature label position.
double getAlpha() const
Returns the angle to rotate text (in rad).
Quadrant
Position of label candidate relative to feature.
void setNextPart(std::unique_ptr< LabelPosition > next)
Sets the next part of this label position (i.e.
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
int incrementUpsideDownCharCount()
Increases the count of upside down characters for this label position.
double getX(int i=0) const
Returns the down-left x coordinate.
double getY(int i=0) const
Returns the down-left y coordinate.
A set of features which influence the labeling process.
QgsPalLayerSettings::Placement arrangement() const
Returns the layer's arrangement policy.
QString name() const
Returns the layer's name.
std::size_t maximumPolygonLabelCandidates() const
Returns the maximum number of polygon label candidates to generate for features in this layer.
int connectedFeatureId(QgsFeatureId featureId) const
Returns the connected feature ID for a label feature ID, which is unique for all features which have ...
std::size_t maximumPointLabelCandidates() const
Returns the maximum number of point label candidates to generate for features in this layer.
bool centroidInside() const
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
UpsideDownLabels upsidedownLabels() const
Returns how upside down labels are handled within the layer.
bool isCurved() const
Returns true if the layer has curved labels.
double priority() const
Returns the layer's priority, between 0 and 1.
std::size_t maximumLineLabelCandidates() const
Returns the maximum number of line label candidates to generate for features in this layer.
double maximumLineCandidatesPerMapUnit() const
Returns the maximum number of line label candidate positions per map unit.
double maximumPolygonCandidatesPerMapUnitSquared() const
Returns the maximum number of polygon label candidate positions per map unit squared.
The underlying raw pal geometry class.
std::unique_ptr< PointSet > clone() const
Returns a copy of the point set.
OrientedConvexHullBoundingBox computeConvexHullOrientedBoundingBox(bool &ok)
Computes an oriented bounding box for the shape's convex hull.
double length() const
Returns length of line geometry.
double area() const
Returns area of polygon geometry.
bool isClosed() const
Returns true if pointset is closed.
void createGeosGeom() const
void getCentroid(double &px, double &py, bool forceInside=false) const
const GEOSPreparedGeometry * preparedGeom() const
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py)
Gets a point a set distance along a line geometry.
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
static void splitPolygons(QLinkedList< PointSet * > &inputShapes, QLinkedList< PointSet * > &outputShapes, double xrm, double yrm)
Split a concave shape into several convex shapes.
void createCandidateAtOrderedPositionOverPoint(double &labelX, double &labelY, LabelPosition::Quadrant &quadrant, double x, double y, double labelWidth, double labelHeight, QgsPalLayerSettings::PredefinedPointPosition position, double distanceToLabel, const QgsMargins &visualMargin, double symbolWidthOffset, double symbolHeightOffset)
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Represents the minimum area, oriented bounding box surrounding a convex hull.