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 )
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 );
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 ) );
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;
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 );
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 ) );
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 );
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;
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;
1572 p = _createCurvedCandidate( slp.get(), angle_avg, 0 );
1573 p->setCost( p->cost() + 0.002 );
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< CHullBox > boxes;
1658 boxes.reserve( shapes_final.size() );
1661 while ( !shapes_final.isEmpty() )
1663 PointSet *shape = shapes_final.takeFirst();
1670 if (
pal->isCanceled() )
1674 double densityY = densityX;
1681 std::size_t numberCandidatesGenerated = 0;
1696 double dx = densityX;
1697 double dy = densityY;
1698 if ( numTry == 0 && maxPolygonCandidates > 0 )
1701 const double boxArea = box.width * box.length;
1702 double maxThisBox = targetPolygonCandidates * boxArea / totalArea;
1703 dx = std::max( dx, std::sqrt( boxArea / maxThisBox ) * 0.8 );
1707 if (
pal->isCanceled() )
1708 return numberCandidatesGenerated;
1727 bool enoughPlace =
false;
1731 px = ( box.x[0] + box.x[2] ) / 2 - labelWidth;
1732 py = ( box.y[0] + box.y[2] ) / 2 - labelHeight;
1738 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1740 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1744 enoughPlace =
false;
1760 else if ( box.length > 1.5 * labelWidth && box.width > 1.5 * labelWidth )
1762 if ( box.alpha <= M_PI_4 )
1768 alpha = box.alpha - M_PI_2;
1771 else if ( box.length > box.width )
1773 alpha = box.alpha - M_PI_2;
1780 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1786 dlx = std::cos( beta ) * diago;
1787 dly = std::sin( beta ) * diago;
1789 double px0 = box.width / 2.0;
1790 double py0 = box.length / 2.0;
1792 px0 -= std::ceil( px0 / dx ) * dx;
1793 py0 -= std::ceil( py0 / dy ) * dy;
1795 for ( px = px0; px <= box.width; px += dx )
1797 if (
pal->isCanceled() )
1800 for ( py = py0; py <= box.length; py += dy )
1803 rx = std::cos( box.alpha ) * px + std::cos( box.alpha - M_PI_2 ) * py;
1804 ry = std::sin( box.alpha ) * px + std::sin( box.alpha - M_PI_2 ) * py;
1814 lPos.emplace_back( qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver ) );
1815 numberCandidatesGenerated++;
1826 std::unique_ptr< LabelPosition > potentialCandidate = qgis::make_unique< LabelPosition >(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this,
false,
LabelPosition::QuadrantOver );
1828 lPos.emplace_back( std::move( potentialCandidate ) );
1829 numberCandidatesGenerated++;
1836 nbp = numberCandidatesGenerated;
1837 if ( maxPolygonCandidates > 0 && nbp < targetPolygonCandidates )
1848 while ( numTry < maxTry );
1850 nbp = numberCandidatesGenerated;
1864 std::size_t candidatesCreated = 0;
1924 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( gg.get() );
1926 return candidatesCreated;
1930 return candidatesCreated;
1934 const double ringLength = ring->
length();
1935 const double circleArea = std::pow( ringLength, 2 ) / ( 4 * M_PI );
1937 const std::size_t targetPolygonCandidates = std::max(
static_cast< std::size_t
>( 16 ), maxPolygonCandidates > 0 ? std::min( maxPolygonCandidates, candidatesForArea ) : candidatesForArea );
1940 const double delta = ringLength / targetPolygonCandidates;
1943 const double maxDistCentroidToLabelX = std::max(
xmax - cx, cx -
xmin ) + distanceToLabel;
1944 const double maxDistCentroidToLabelY = std::max(
ymax - cy, cy -
ymin ) + distanceToLabel;
1945 const double estimateOfMaxPossibleDistanceCentroidToLabel = std::sqrt( maxDistCentroidToLabelX * maxDistCentroidToLabelX + maxDistCentroidToLabelY * maxDistCentroidToLabelY );
1948 const double labelAngle = 0;
1950 std::size_t i = lPos.size();
1958 createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant,
x,
y, labelWidth, labelHeight, position, distanceToLabel * 0.5, visualMargin, 0, 0 );
1960 std::unique_ptr< LabelPosition > candidate = qgis::make_unique< LabelPosition >( i, labelX, labelY, labelWidth, labelHeight, labelAngle, 0,
this,
false, quadrant );
1961 if ( candidate->intersects( preparedBuffer.get() ) )
1979 const double centroidDistance = candidate->getDistanceToPoint( cx, cy );
1980 const double centroidCost = centroidDistance / estimateOfMaxPossibleDistanceCentroidToLabel;
1981 candidate->setCost( centroidCost );
1983 lPos.emplace_back( std::move( candidate ) );
1984 candidatesCreated++;
1989 double startSegmentX,
double startSegmentY,
double,
double,
1990 double endSegmentX,
double endSegmentY,
double,
double )
1993 float angle = atan2(
static_cast< float >( endSegmentY - startSegmentY ),
static_cast< float >( endSegmentX - startSegmentX ) ) * 180 / M_PI;
2003 else if (
angle <= 85 )
2007 else if (
angle <= 90 )
2013 else if (
angle <= 95 )
2018 else if (
angle <= 175 )
2022 else if (
angle <= 180 )
2028 else if (
angle <= 185 )
2033 else if (
angle <= 265 )
2037 else if (
angle <= 270 )
2042 else if (
angle <= 275 )
2047 else if (
angle <= 355 )
2057 return !
pal->isCanceled();
2060 return candidatesCreated;
2065 std::vector< std::unique_ptr< LabelPosition > > lPos;
2085 case GEOS_LINESTRING:
2099 const bool allowOutside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2100 const bool allowInside =
mLF->
polygonPlacementFlags() & QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon;
2108 else if ( allowOutside && ( std::fabs(
xmax -
xmin ) < labelWidth ||
2109 std::fabs(
ymax -
ymin ) < labelHeight ) )
2116 std::size_t created = 0;
2174 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
2176 double sizeCost = 0;
2177 if ( geomType == GEOS_LINESTRING )
2179 const double l =
length();
2182 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
2183 if ( l >= bbox_length / 4 )
2186 sizeCost = 1 - ( l / ( bbox_length / 4 ) );
2188 else if ( geomType == GEOS_POLYGON )
2190 const double a =
area();
2193 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
2194 if ( a >= bbox_area / 16 )
2197 sizeCost = 1 - ( a / ( bbox_area / 16 ) );
2203 for ( std::unique_ptr< LabelPosition > &pos : lPos )
2205 pos->setCost( pos->cost() + sizeCost / 100 );
2218 catch ( GEOSException &e )
2220 qWarning(
"GEOS exception: %s", e.what() );
2230 if ( !other->
mGeos )
2236 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
2237 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
2238 GEOSGeometry *geoms[2] = { g1, g2 };
2239 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2242 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
2250 mGeos = gTmp.release();
2259 catch ( GEOSException &e )
2261 qWarning(
"GEOS exception: %s", e.what() );
2282 bool uprightLabel =
false;
2287 uprightLabel =
true;
2293 uprightLabel =
true;
2299 uprightLabel =
true;
2301 return uprightLabel;
2305 double &characterStartX,
double &characterStartY,
double &characterEndX,
double &characterEndY )
const
2314 double segmentStartX = path_positions->
x[index - 1];
2315 double segmentStartY = path_positions->
y[index - 1];
2317 double segmentEndX = path_positions->
x[index];
2318 double segmentEndY = path_positions->
y[index];
2320 double segmentDx = segmentEndX - segmentStartX;
2321 double segmentDy = segmentEndY - segmentStartY;
2323 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2324 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2330 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
2333 currentDistanceAlongSegment += charWidth;
2334 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
2335 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
2343 segmentStartX = segmentEndX;
2344 segmentStartY = segmentEndY;
2346 if ( index >= path_positions->
nbPoints )
2350 segmentEndX = path_positions->
x[index];
2351 segmentEndY = path_positions->
y[index];
2353 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth );
2359 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );