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 )
428 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 )
438 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
439 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
445 deltaX = -labelWidth / 4.0 - visualMargin.
left();
446 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
452 deltaX = -labelWidth / 2.0;
453 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
459 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
460 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
466 deltaX = - visualMargin.
left() + symbolWidthOffset;
467 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
473 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
474 deltaY = -labelHeight / 2.0;
480 deltaX = -visualMargin.
left() + symbolWidthOffset;
481 deltaY = -labelHeight / 2.0;
487 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
488 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
494 deltaX = -labelWidth / 4.0 - visualMargin.
left();
495 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
501 deltaX = -labelWidth / 2.0;
502 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
508 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
509 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
515 deltaX = -visualMargin.
left() + symbolWidthOffset;
516 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
521 double referenceX = std::cos( alpha ) * distanceToLabel + x;
522 double referenceY = std::sin( alpha ) * distanceToLabel + y;
524 labelX = referenceX + deltaX;
525 labelY = referenceY + deltaY;
539 double cost = 0.0001;
540 std::size_t i = lPos.size();
543 std::size_t created = 0;
550 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset );
554 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
558 if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
574 if ( maxNumberCandidates == 0 )
575 maxNumberCandidates = 16;
579 int id = lPos.size();
581 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
586 double a270 = a180 + a90;
587 double a360 = 2 * M_PI;
589 double gamma1, gamma2;
591 if ( distanceToLabel > 0 )
593 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
594 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
598 gamma1 = gamma2 = a90 / 3.0;
601 if ( gamma1 > a90 / 3.0 )
604 if ( gamma2 > a90 / 3.0 )
607 std::size_t numberCandidatesGenerated = 0;
610 double angleToCandidate;
611 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
616 if ( angleToCandidate > a360 )
617 angleToCandidate -= a360;
621 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
623 labelX += distanceToLabel;
624 double iota = ( angleToCandidate + gamma1 );
625 if ( iota > a360 - gamma1 )
629 labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
633 else if ( angleToCandidate < a90 - gamma2 )
635 labelX += distanceToLabel * std::cos( angleToCandidate );
636 labelY += distanceToLabel * std::sin( angleToCandidate );
639 else if ( angleToCandidate < a90 + gamma2 )
642 labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
643 labelY += distanceToLabel;
646 else if ( angleToCandidate < a180 - gamma1 )
648 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
649 labelY += distanceToLabel * std::sin( angleToCandidate );
652 else if ( angleToCandidate < a180 + gamma1 )
654 labelX += -distanceToLabel - labelWidth;
656 labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
659 else if ( angleToCandidate < a270 - gamma2 )
661 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
662 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
665 else if ( angleToCandidate < a270 + gamma2 )
667 labelY += -distanceToLabel - labelHeight;
669 labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
672 else if ( angleToCandidate < a360 )
674 labelX += distanceToLabel * std::cos( angleToCandidate );
675 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
681 if ( maxNumberCandidates == 1 )
684 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
695 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id + i, labelX, labelY, labelWidth, labelHeight,
angle, cost,
this,
false, quadrant ) );
696 numberCandidatesGenerated++;
700 if ( icost ==
static_cast< int >( maxNumberCandidates ) )
702 icost =
static_cast< int >( maxNumberCandidates ) - 1;
705 else if ( icost >
static_cast< int >( maxNumberCandidates ) )
707 icost =
static_cast< int >( maxNumberCandidates ) - 2;
713 return numberCandidatesGenerated;
720 double shapeLength = mapShape->
length();
734 if ( candidates < candidateTargetCount )
749 std::vector< double > &
x = line->
x;
750 std::vector< double > &
y = line->
y;
752 std::vector< double > segmentLengths(
nbPoints - 1 );
753 std::vector< double >distanceToSegment(
nbPoints );
755 double totalLineLength = 0.0;
756 for (
int i = 0; i < line->
nbPoints - 1; i++ )
759 distanceToSegment[i] = 0;
761 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
764 totalLineLength += segmentLengths[i];
766 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
769 const double lineStepDistance = totalLineLength / ( candidateTargetCount + 1 );
770 double currentDistanceAlongLine = lineStepDistance;
772 double candidateCenterX, candidateCenterY;
774 while ( currentDistanceAlongLine < totalLineLength )
776 if (
pal->isCanceled() )
781 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateCenterX, &candidateCenterY );
784 double cost = std::fabs( totalLineLength / 2 - currentDistanceAlongLine ) / totalLineLength;
787 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateCenterX - labelWidth / 2, candidateCenterY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
789 currentDistanceAlongLine += lineStepDistance;
793 if ( lineStepDistance < 0 )
807 flags = QgsLabeling::LinePlacementFlag::OnLine;
810 QVector< int > extremeAngleNodes;
813 std::vector< double > &
x = line->
x;
814 std::vector< double > &
y = line->
y;
818 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
820 double x1 =
x[i - 1];
822 double x3 =
x[ i == numberNodes - 1 ? 1 : i + 1];
823 double y1 =
y[i - 1];
825 double y3 =
y[ i == numberNodes - 1 ? 1 : i + 1];
830 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
834 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
835 extremeAngleNodes << i;
837 extremeAngleNodes << numberNodes - 1;
839 if ( extremeAngleNodes.isEmpty() )
846 std::vector< double > segmentLengths( numberNodes - 1 );
847 std::vector< double > distanceToSegment( numberNodes );
848 double totalLineLength = 0.0;
849 QVector< double > straightSegmentLengths;
850 QVector< double > straightSegmentAngles;
851 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
852 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
853 double currentStraightSegmentLength = 0;
854 double longestSegmentLength = 0;
855 int segmentIndex = 0;
856 double segmentStartX =
x[0];
857 double segmentStartY =
y[0];
858 for (
int i = 0; i < numberNodes - 1; i++ )
861 distanceToSegment[i] = 0;
863 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
866 totalLineLength += segmentLengths[i];
867 if ( extremeAngleNodes.contains( i ) )
870 straightSegmentLengths << currentStraightSegmentLength;
872 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
874 currentStraightSegmentLength = 0;
875 segmentStartX =
x[i];
876 segmentStartY =
y[i];
878 currentStraightSegmentLength += segmentLengths[i];
880 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
881 straightSegmentLengths << currentStraightSegmentLength;
883 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
884 double middleOfLine = totalLineLength / 2.0;
886 if ( totalLineLength < labelWidth )
892 double lineStepDistance = ( totalLineLength - labelWidth );
893 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
895 double distanceToEndOfSegment = 0.0;
896 int lastNodeInSegment = 0;
898 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
900 currentStraightSegmentLength = straightSegmentLengths.at( i );
901 double currentSegmentAngle = straightSegmentAngles.at( i );
902 lastNodeInSegment = extremeAngleNodes.at( i );
903 double distanceToStartOfSegment = distanceToEndOfSegment;
904 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
905 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
907 if ( currentStraightSegmentLength < labelWidth )
911 double currentDistanceAlongLine = distanceToStartOfSegment;
912 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
913 double candidateLength = 0.0;
919 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
920 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
922 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
924 if (
pal->isCanceled() )
930 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
931 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
933 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
939 cost = candidateLength / labelWidth;
945 cost = ( 1 - cost ) / 100;
949 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
950 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
951 cost += costCenter * 0.0005;
958 double costLineCenter = 2 * std::fabs( labelCenter - middleOfLine ) / totalLineLength;
959 cost += costLineCenter * 0.0005;
962 cost += segmentCost * 0.0005;
963 cost += segmentAngleCost * 0.0001;
970 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
974 beta =
angle + M_PI_2;
979 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
981 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
982 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
983 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
989 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
990 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 ) );
997 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
998 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 ) );
1001 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1005 const double candidateCost = cost + 0.002;
1006 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 ) );
1012 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1019 currentDistanceAlongLine += lineStepDistance;
1038 flags = QgsLabeling::LinePlacementFlag::OnLine;
1042 std::vector< double > &
x = line->
x;
1043 std::vector< double > &
y = line->
y;
1045 std::vector< double > segmentLengths(
nbPoints - 1 );
1046 std::vector< double >distanceToSegment(
nbPoints );
1048 double totalLineLength = 0.0;
1049 for (
int i = 0; i < line->
nbPoints - 1; i++ )
1052 distanceToSegment[i] = 0;
1054 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
1057 totalLineLength += segmentLengths[i];
1059 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
1061 double lineStepDistance = ( totalLineLength - labelWidth );
1062 double currentDistanceAlongLine = 0;
1066 if ( totalLineLength > labelWidth )
1068 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
1072 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
1073 lineStepDistance = -1;
1074 totalLineLength = labelWidth;
1079 currentDistanceAlongLine = std::numeric_limits< double >::max();
1082 double candidateLength;
1084 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
1086 while ( currentDistanceAlongLine < totalLineLength - labelWidth )
1088 if (
pal->isCanceled() )
1094 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine, &candidateStartX, &candidateStartY );
1095 line->
getPointByDistance( segmentLengths.data(), distanceToSegment.data(), currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
1097 if ( currentDistanceAlongLine < 0 )
1105 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
1108 cost = candidateLength / labelWidth;
1114 cost = ( 1 - cost ) / 100;
1118 double costCenter = std::fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
1119 cost += costCenter / 1000;
1120 cost += initialCost;
1127 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
1131 beta =
angle + M_PI_2;
1136 bool isRightToLeft = (
angle > M_PI_2 ||
angle <= -M_PI_2 );
1138 bool reversed = ( ( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) ? isRightToLeft :
false );
1139 bool aboveLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) );
1140 bool belowLine = ( !reversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( reversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) );
1146 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
1147 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 ) );
1154 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
1155 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 ) );
1158 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
1162 const double candidateCost = cost + 0.002;
1163 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 ) );
1169 lPos.emplace_back( qgis::make_unique< LabelPosition >( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this,
false,
LabelPosition::QuadrantOver ) );
1176 currentDistanceAlongLine += lineStepDistance;
1180 if ( lineStepDistance < 0 )
1190 double offsetAlongSegment = offsetAlongLine;
1193 while ( index < path_positions->
nbPoints && offsetAlongSegment > path_distances[index] )
1195 offsetAlongSegment -= path_distances[index];
1198 if ( index >= path_positions->
nbPoints )
1207 const double segment_length = path_distances[index];
1214 if ( orientation == 0 )
1218 double _distance = offsetAlongSegment;
1219 int endindex = index;
1221 double startLabelX = 0;
1222 double startLabelY = 0;
1223 double endLabelX = 0;
1224 double endLabelY = 0;
1225 for (
int i = 0; i < li->
char_num; i++ )
1228 double characterStartX, characterStartY;
1229 if ( !
nextCharPosition( ci.
width, path_distances[endindex], path_positions, endindex, _distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
1235 startLabelX = characterStartX;
1236 startLabelY = characterStartY;
1241 double dx = endLabelX - startLabelX;
1242 double dy = endLabelY - startLabelY;
1243 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
1245 bool isRightToLeft = ( lineAngle > 90 || lineAngle < -90 );
1246 reversed = isRightToLeft;
1247 orientation = isRightToLeft ? -1 : 1;
1252 if ( orientation < 0 )
1255 reversed = !reversed;
1260 std::unique_ptr< LabelPosition > slp;
1263 double old_x = path_positions->
x[index - 1];
1264 double old_y = path_positions->
y[index - 1];
1266 double new_x = path_positions->
x[index];
1267 double new_y = path_positions->
y[index];
1269 double dx = new_x - old_x;
1270 double dy = new_y - old_y;
1272 double angle = std::atan2( -dy, dx );
1274 for (
int i = 0; i < li->
char_num; i++ )
1276 double last_character_angle =
angle;
1284 double start_x, start_y, end_x, end_y;
1285 if ( !
nextCharPosition( ci.
width, path_distances[index], path_positions, index, offsetAlongSegment, start_x, start_y, end_x, end_y ) )
1291 angle = std::atan2( start_y - end_y, end_x - start_x );
1296 double angle_delta = last_character_angle -
angle;
1298 while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
1299 while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
1303 && angle_delta < li->max_char_angle_outside * ( M_PI / 180 ) ) )
1311 if ( orientation < 0 )
1316 start_x += dist * std::cos(
angle + M_PI_2 );
1317 start_y -= dist * std::sin(
angle + M_PI_2 );
1319 double render_angle =
angle;
1321 double render_x = start_x;
1322 double render_y = start_y;
1328 if ( orientation < 0 )
1331 render_x += ci.
width * std::cos( render_angle );
1332 render_y -= ci.
width * std::sin( render_angle );
1333 render_angle += M_PI;
1336 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 );
1337 tmp->setPartId( orientation > 0 ? i : li->
char_num - i - 1 );
1340 slp = std::move( tmp );
1346 while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
1347 while ( render_angle < 0 ) render_angle += 2 * M_PI;
1349 if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI )
1356 static std::unique_ptr< LabelPosition > _createCurvedCandidate(
LabelPosition *lp,
double angle,
double dist )
1358 std::unique_ptr< LabelPosition > newLp = qgis::make_unique< LabelPosition >( *lp );
1359 newLp->offsetPosition( dist * std::cos(
angle + M_PI_2 ), dist * std::sin(
angle + M_PI_2 ) );
1374 double totalCharacterWidth = 0;
1375 for (
int i = 0; i < li->
char_num; ++i )
1378 std::unique_ptr< PointSet > expanded;
1379 double shapeLength = mapShape->
length();
1382 allowOverrun =
false;
1388 if ( totalCharacterWidth > shapeLength )
1390 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1397 if ( allowOverrun && overrun > 0 )
1400 expanded = mapShape->
clone();
1402 mapShape = expanded.get();
1403 shapeLength = mapShape->
length();
1407 std::unique_ptr< double [] > path_distances = qgis::make_unique<double[]>( mapShape->
nbPoints );
1408 double total_distance = 0;
1409 double old_x = -1.0, old_y = -1.0;
1410 for (
int i = 0; i < mapShape->
nbPoints; i++ )
1413 path_distances[i] = 0;
1415 path_distances[i] = std::sqrt( std::pow( old_x - mapShape->
x[i], 2 ) + std::pow( old_y - mapShape->
y[i], 2 ) );
1416 old_x = mapShape->
x[i];
1417 old_y = mapShape->
y[i];
1419 total_distance += path_distances[i];
1427 if (
pal->isCanceled() )
1430 std::vector< std::unique_ptr< LabelPosition >> positions;
1432 double delta = std::max( li->
label_height / 6, total_distance / candidateTargetCount );
1436 flags = QgsLabeling::LinePlacementFlag::OnLine;
1439 for (
double distanceAlongLineToStartCandidate = 0; distanceAlongLineToStartCandidate < total_distance; distanceAlongLineToStartCandidate += delta )
1443 bool reversed =
false;
1445 if (
pal->isCanceled() )
1449 int orientation = 0;
1450 if ( !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
1457 std::unique_ptr< LabelPosition > slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
1462 if ( slp->upsideDownCharCount() >= li->
char_num / 2.0 )
1467 orientation = -orientation;
1468 slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
1475 double angle_diff = 0.0, angle_last = 0.0, diff;
1477 double sin_avg = 0, cos_avg = 0;
1480 if ( tmp != slp.get() )
1482 diff = std::fabs( tmp->
getAlpha() - angle_last );
1483 if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
1484 diff = std::min( diff, 2 * M_PI - diff );
1488 sin_avg += std::sin( tmp->
getAlpha() );
1489 cos_avg += std::cos( tmp->
getAlpha() );
1494 double angle_diff_avg = li->
char_num > 1 ? ( angle_diff / ( li->
char_num - 1 ) ) : 0;
1495 double cost = angle_diff_avg / 100;
1496 if ( cost < 0.0001 ) cost = 0.0001;
1499 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1500 double costCenter = std::fabs( total_distance / 2 - labelCenter ) / total_distance;
1501 cost += costCenter / 100;
1502 slp->setCost( cost );
1505 double angle_avg = std::atan2( sin_avg / li->
char_num, cos_avg / li->
char_num );
1506 bool localreversed = flip ? !reversed : reversed;
1508 for (
int i = 0; i <= 2; ++i )
1510 std::unique_ptr< LabelPosition > p;
1511 if ( i == 0 && ( ( !localreversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) || ( localreversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) ) )
1513 if ( i == 1 && flags & QgsLabeling::LinePlacementFlag::OnLine )
1515 p = _createCurvedCandidate( slp.get(), angle_avg, 0 );
1516 p->setCost( p->cost() + 0.002 );
1518 if ( i == 2 && ( ( !localreversed && ( flags & QgsLabeling::LinePlacementFlag::BelowLine ) ) || ( localreversed && ( flags & QgsLabeling::LinePlacementFlag::AboveLine ) ) ) )
1521 p->setCost( p->cost() + 0.001 );
1528 while ( within && currentPos )
1531 currentPos = currentPos->
nextPart();
1540 positions.emplace_back( std::move( p ) );
1544 for ( std::unique_ptr< LabelPosition > &pos : positions )
1546 lPos.emplace_back( std::move( pos ) );
1549 return positions.size();
1573 QLinkedList<PointSet *> shapes_toProcess;
1574 QLinkedList<PointSet *> shapes_final;
1575 const double totalArea =
area();
1577 mapShape->
parent =
nullptr;
1579 if (
pal->isCanceled() )
1582 shapes_toProcess.append( mapShape );
1584 splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight );
1586 std::size_t nbp = 0;
1588 if ( !shapes_final.isEmpty() )
1596 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1598 std::vector< CHullBox > boxes;
1599 boxes.reserve( shapes_final.size() );
1602 while ( !shapes_final.isEmpty() )
1604 PointSet *shape = shapes_final.takeFirst();
1611 if (
pal->isCanceled() )
1615 double densityY = densityX;
1622 std::size_t numberCandidatesGenerated = 0;
1637 double dx = densityX;
1638 double dy = densityY;
1639 if ( numTry == 0 && maxPolygonCandidates > 0 )
1642 const double boxArea = box.width * box.length;
1643 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1644 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1648 if (
pal->isCanceled() )
1649 return numberCandidatesGenerated;
1668 bool enoughPlace =
false;
1672 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1673 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1679 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1681 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1685 enoughPlace =
false;
1701 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1703 if ( box.alpha <= M_PI_4 )
1709 alpha = box.alpha - M_PI_2;
1712 else if ( box.length > box.width )
1714 alpha = box.alpha - M_PI_2;
1721 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1727 dlx = std::cos( beta ) * diago;
1728 dly = std::sin( beta ) * diago;
1730 double px0 = box.width / 2.0;
1731 double py0 = box.length / 2.0;
1733 px0 -= std::ceil( px0 / dx ) * dx;
1734 py0 -= std::ceil( py0 / dy ) * dy;
1736 for ( px = px0; px <= box.width; px += dx )
1738 if (
pal->isCanceled() )
1741 for ( py = py0; py <= box.length; py += dy )
1744 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1745 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1755 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1756 numberCandidatesGenerated++;
1767 std::unique_ptr< LabelPosition > potentialCandidate = qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1769 lPos.emplace_back( std::move( potentialCandidate ) );
1770 numberCandidatesGenerated++;
1777 nbp = numberCandidatesGenerated;
1778 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1789 while ( numTry < maxTry );
1791 nbp = numberCandidatesGenerated;
1805 std::size_t candidatesCreated = 0;
1865 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1867 return candidatesCreated;
1871 return candidatesCreated;
1875 const double ringLength = ring->
length();
1876 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
1878 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
1881 const double delta = ringLength / targetPolygonCandidates;
1884 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
1885 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
1886 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
1889 const double labelAngle = 0;
1891 std::size_t i = lPos.size();
1899 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0 );
1901 std::unique_ptr< LabelPosition > candidate = qgis::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
1902 if ( candidate->intersects( preparedBuffer.get() ) )
1920 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
1921 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
1922 candidate->setCost( centroidCost );
1924 lPos.emplace_back( std::move( candidate ) );
1925 candidatesCreated++;
1930 double startSegmentX,
double startSegmentY,
double,
double,
1931 double endSegmentX,
double endSegmentY,
double,
double )
1934 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
1944 else if (
angle <= 85 )
1948 else if (
angle <= 90 )
1954 else if (
angle <= 95 )
1959 else if (
angle <= 175 )
1963 else if (
angle <= 180 )
1969 else if (
angle <= 185 )
1974 else if (
angle <= 265 )
1978 else if (
angle <= 270 )
1983 else if (
angle <= 275 )
1988 else if (
angle <= 355 )
1998 return !
pal->isCanceled();
2001 return candidatesCreated;
2006 std::vector< std::unique_ptr< LabelPosition > > lPos;
2026 case GEOS_LINESTRING:
2040 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2041 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2049 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2050 std::fabs(
ymax -
ymin ) < labelHeight ) )
2057 std::size_t created = 0;
2115 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2117 double sizeCost = 0;
2118 if ( geomType == GEOS_LINESTRING )
2120 const double l =
length();
2123 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2124 if ( l >= bbox_length / 4 )
2127 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2129 else if ( geomType == GEOS_POLYGON )
2131 const double a =
area();
2134 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2135 if ( a >= bbox_area / 16 )
2138 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2144 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2146 pos->setCost( pos->cost() + sizeCost / 100 );
2159 catch ( GEOSException &e )
2170 if ( !other->
mGeos )
2176 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2177 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2178 GEOSGeometry *geoms[2] = { g1, g2 };
2179 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2182 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2190 mGeos = gTmp.release();
2199 catch ( GEOSException &e )
2221 bool uprightLabel =
false;
2226 uprightLabel =
true;
2232 uprightLabel =
true;
2238 uprightLabel =
true;
2240 return uprightLabel;
2244 double &characterStartX,
double &characterStartY,
double &characterEndX,
double &characterEndY )
const
2253 double segmentStartX = path_positions->
x[index - 1];
2254 double segmentStartY = path_positions->
y[index - 1];
2256 double segmentEndX = path_positions->
x[index];
2257 double segmentEndY = path_positions->
y[index];
2259 double segmentDx = segmentEndX - segmentStartX;
2260 double segmentDy = segmentEndY - segmentStartY;
2262 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2263 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2269 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
2272 currentDistanceAlongSegment += charWidth;
2273 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2274 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2282 segmentStartX = segmentEndX;
2283 segmentStartY = segmentEndY;
2285 if ( index >= path_positions->
nbPoints )
2289 segmentEndX = path_positions->
x[index];
2290 segmentEndY = path_positions->
y[index];
2292 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth );
2298 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );