43 #include <QLinkedList> 53 mGeos =
const_cast<GEOSGeometry *
>( geom );
59 for (
int i = 0; i <
mHoles.count(); i++ )
61 mHoles.at( i )->holeOf =
this;
73 mHoles.last()->holeOf =
this;
87 const GEOSCoordSequence *coordSeq =
nullptr;
90 type = GEOSGeomTypeId_r( geosctxt, geom );
92 if (
type == GEOS_POLYGON )
94 if ( GEOSGetNumInteriorRings_r( geosctxt, geom ) > 0 )
96 int numHoles = GEOSGetNumInteriorRings_r( geosctxt, geom );
98 for (
int i = 0; i < numHoles; ++i )
100 const GEOSGeometry *interior = GEOSGetInteriorRingN_r( geosctxt, geom, i );
112 geom = GEOSGetExteriorRing_r( geosctxt, geom );
121 nbPoints = GEOSGetNumCoordinates_r( geosctxt, geom );
122 coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, geom );
125 xmin =
ymin = std::numeric_limits<double>::max();
126 xmax =
ymax = std::numeric_limits<double>::lowest();
133 for (
int i = 0; i <
nbPoints; ++i )
135 GEOSCoordSeq_getX_r( geosctxt, coordSeq, i, &
x[i] );
136 GEOSCoordSeq_getY_r( geosctxt, coordSeq, i, &
y[i] );
175 qreal quadOffsetX = quadOffset.x(), quadOffsetY = quadOffset.y();
177 if ( quadOffsetX < 0 )
179 if ( quadOffsetY < 0 )
183 else if ( quadOffsetY > 0 )
192 else if ( quadOffsetX > 0 )
194 if ( quadOffsetY < 0 )
198 else if ( quadOffsetY > 0 )
209 if ( quadOffsetY < 0 )
213 else if ( quadOffsetY > 0 )
232 double cost = 0.0001;
235 double xdiff = -labelW / 2.0;
236 double ydiff = -labelH / 2.0;
251 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
252 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
288 double lx = x + xdiff;
289 double ly = y + ydiff;
299 lPos <<
new LabelPosition(
id, lx, ly, labelW, labelH, angle, cost,
this,
false, quadrantFromOffset() );
314 double cost = 0.0001;
327 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
328 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
334 deltaX = -labelWidth / 4.0 - visualMargin.
left();
335 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
341 deltaX = -labelWidth / 2.0;
342 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
348 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
349 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
355 deltaX = - visualMargin.
left() + symbolWidthOffset;
356 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
362 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
363 deltaY = -labelHeight / 2.0;
369 deltaX = -visualMargin.
left() + symbolWidthOffset;
370 deltaY = -labelHeight / 2.0;
376 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
377 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
383 deltaX = -labelWidth / 4.0 - visualMargin.
left();
384 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
390 deltaX = -labelWidth / 2.0;
391 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
397 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
398 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
404 deltaX = -visualMargin.
left() + symbolWidthOffset;
405 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
410 double referenceX = std::cos( alpha ) * distanceToLabel +
x;
411 double referenceY = std::sin( alpha ) * distanceToLabel +
y;
413 double labelX = referenceX + deltaX;
414 double labelY = referenceY + deltaY;
418 lPos <<
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant );
440 double candidateAngleIncrement = 2 * M_PI / numberCandidates;
445 double a270 = a180 + a90;
446 double a360 = 2 * M_PI;
448 double gamma1, gamma2;
450 if ( distanceToLabel > 0 )
452 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
453 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
457 gamma1 = gamma2 = a90 / 3.0;
460 if ( gamma1 > a90 / 3.0 )
463 if ( gamma2 > a90 / 3.0 )
466 QList< LabelPosition * > candidates;
469 double angleToCandidate;
470 for ( i = 0, angleToCandidate = M_PI_4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement )
475 if ( angleToCandidate > a360 )
476 angleToCandidate -= a360;
480 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
482 labelX += distanceToLabel;
483 double iota = ( angleToCandidate + gamma1 );
484 if ( iota > a360 - gamma1 )
488 labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
492 else if ( angleToCandidate < a90 - gamma2 )
494 labelX += distanceToLabel * std::cos( angleToCandidate );
495 labelY += distanceToLabel * std::sin( angleToCandidate );
498 else if ( angleToCandidate < a90 + gamma2 )
501 labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
502 labelY += distanceToLabel;
505 else if ( angleToCandidate < a180 - gamma1 )
507 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
508 labelY += distanceToLabel * std::sin( angleToCandidate );
511 else if ( angleToCandidate < a180 + gamma1 )
513 labelX += -distanceToLabel - labelWidth;
515 labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
518 else if ( angleToCandidate < a270 - gamma2 )
520 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
521 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
524 else if ( angleToCandidate < a270 + gamma2 )
526 labelY += -distanceToLabel - labelHeight;
528 labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
531 else if ( angleToCandidate < a360 )
533 labelX += distanceToLabel * std::cos( angleToCandidate );
534 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
540 if ( numberCandidates == 1 )
543 cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );
554 candidates <<
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant );
558 if ( icost == numberCandidates )
560 icost = numberCandidates - 1;
563 else if ( icost > numberCandidates )
565 icost = numberCandidates - 2;
571 if ( !candidates.isEmpty() )
573 for (
int i = 0; i < candidates.count(); ++i )
575 lPos << candidates.at( i );
579 return candidates.count();
587 if ( candidates < mLF->
layer()->
pal->line_p )
605 QVector< int > extremeAngleNodes;
613 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
615 double x1 = x[i - 1];
617 double x3 = x[ i == numberNodes - 1 ? 1 : i + 1];
618 double y1 = y[i - 1];
620 double y3 = y[ i == numberNodes - 1 ? 1 : i + 1];
625 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
629 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
630 extremeAngleNodes << i;
632 extremeAngleNodes << numberNodes - 1;
634 if ( extremeAngleNodes.isEmpty() )
641 double *segmentLengths =
new double[ numberNodes - 1 ];
642 double *distanceToSegment =
new double[ numberNodes ];
643 double totalLineLength = 0.0;
644 QVector< double > straightSegmentLengths;
645 QVector< double > straightSegmentAngles;
646 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
647 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
648 double currentStraightSegmentLength = 0;
649 double longestSegmentLength = 0;
650 int segmentIndex = 0;
651 double segmentStartX = x[0];
652 double segmentStartY = y[0];
653 for (
int i = 0; i < numberNodes - 1; i++ )
656 distanceToSegment[i] = 0;
658 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
661 totalLineLength += segmentLengths[i];
662 if ( extremeAngleNodes.contains( i ) )
665 straightSegmentLengths << currentStraightSegmentLength;
667 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
669 currentStraightSegmentLength = 0;
670 segmentStartX = x[i];
671 segmentStartY = y[i];
673 currentStraightSegmentLength += segmentLengths[i];
675 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
676 straightSegmentLengths << currentStraightSegmentLength;
678 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
679 double middleOfLine = totalLineLength / 2.0;
681 if ( totalLineLength < labelWidth )
683 delete[] segmentLengths;
684 delete[] distanceToSegment;
688 double lineStepDistance = ( totalLineLength - labelWidth );
689 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance /
mLF->
layer()->
pal->line_p );
691 double distanceToEndOfSegment = 0.0;
692 int lastNodeInSegment = 0;
694 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
696 currentStraightSegmentLength = straightSegmentLengths.at( i );
697 double currentSegmentAngle = straightSegmentAngles.at( i );
698 lastNodeInSegment = extremeAngleNodes.at( i );
699 double distanceToStartOfSegment = distanceToEndOfSegment;
700 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
701 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
703 if ( currentStraightSegmentLength < labelWidth )
707 double currentDistanceAlongLine = distanceToStartOfSegment;
708 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
709 double candidateLength = 0.0;
715 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
716 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
718 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
721 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
722 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
724 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
730 cost = candidateLength / labelWidth;
736 cost = ( 1 - cost ) / 100;
740 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
741 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
742 cost += costCenter * 0.0005;
749 double costLineCenter = 2 * std::fabs( labelCenter - middleOfLine ) / totalLineLength;
750 cost += costLineCenter * 0.0005;
753 cost += segmentCost * 0.0005;
754 cost += segmentAngleCost * 0.0001;
761 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
763 beta = angle + M_PI_2;
768 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
778 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
779 lPos.append(
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
786 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
787 lPos.append(
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
794 const double candidateCost = cost + 0.002;
795 lPos.append(
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
801 lPos.append(
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
808 currentDistanceAlongLine += lineStepDistance;
812 delete[] segmentLengths;
813 delete[] distanceToSegment;
831 QList<LabelPosition *> positions;
838 double *segmentLengths =
new double[nbPoints - 1];
839 double *distanceToSegment =
new double[
nbPoints];
841 double totalLineLength = 0.0;
842 for (
int i = 0; i < line->
nbPoints - 1; i++ )
845 distanceToSegment[i] = 0;
847 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
850 totalLineLength += segmentLengths[i];
852 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
854 double lineStepDistance = ( totalLineLength - labelWidth );
855 double currentDistanceAlongLine = 0;
857 if ( totalLineLength > labelWidth )
859 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance /
mLF->
layer()->
pal->line_p );
863 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
864 lineStepDistance = -1;
865 totalLineLength = labelWidth;
870 currentDistanceAlongLine = std::numeric_limits< double >::max();
873 double candidateLength;
875 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
877 while ( currentDistanceAlongLine < totalLineLength - labelWidth )
880 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
881 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
883 if ( currentDistanceAlongLine < 0 )
886 candidateLength = std::sqrt( ( x[nbPoints - 1] - x[0] ) * ( x[nbPoints - 1] - x[0] )
887 + ( y[nbPoints - 1] - y[0] ) * ( y[nbPoints - 1] - y[0] ) );
891 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
894 cost = candidateLength / labelWidth;
900 cost = ( 1 - cost ) / 100;
904 double costCenter = std::fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
905 cost += costCenter / 1000;
913 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
915 beta = angle + M_PI_2;
920 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
930 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
931 positions.append(
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
938 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
939 positions.append(
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
946 const double candidateCost = cost + 0.002;
947 positions.append(
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
953 positions.append(
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
960 currentDistanceAlongLine += lineStepDistance;
964 if ( lineStepDistance < 0 )
970 delete[] segmentLengths;
971 delete[] distanceToSegment;
973 lPos.append( positions );
980 double offsetAlongSegment = offsetAlongLine;
983 while ( index < path_positions->
nbPoints && offsetAlongSegment > path_distances[index] )
985 offsetAlongSegment -= path_distances[index];
988 if ( index >= path_positions->
nbPoints )
997 const double segment_length = path_distances[index];
1004 if ( orientation == 0 )
1008 double _distance = offsetAlongSegment;
1009 int endindex = index;
1011 double startLabelX = 0;
1012 double startLabelY = 0;
1013 double endLabelX = 0;
1014 double endLabelY = 0;
1015 for (
int i = 0; i < li->
char_num; i++ )
1018 double characterStartX, characterStartY;
1019 if ( !
nextCharPosition( ci.
width, path_distances[endindex], path_positions, endindex, _distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
1025 startLabelX = characterStartX;
1026 startLabelY = characterStartY;
1031 double dx = endLabelX - startLabelX;
1032 double dy = endLabelY - startLabelY;
1033 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
1035 bool isRightToLeft = ( lineAngle > 90 || lineAngle < -90 );
1036 reversed = isRightToLeft;
1037 orientation = isRightToLeft ? -1 : 1;
1042 if ( orientation < 0 )
1045 reversed = !reversed;
1053 double old_x = path_positions->
x[index - 1];
1054 double old_y = path_positions->
y[index - 1];
1056 double new_x = path_positions->
x[index];
1057 double new_y = path_positions->
y[index];
1059 double dx = new_x - old_x;
1060 double dy = new_y - old_y;
1062 double angle = std::atan2( -dy, dx );
1064 for (
int i = 0; i < li->
char_num; i++ )
1066 double last_character_angle =
angle;
1074 double start_x, start_y, end_x, end_y;
1075 if ( !
nextCharPosition( ci.
width, path_distances[index], path_positions, index, offsetAlongSegment, start_x, start_y, end_x, end_y ) )
1082 angle = std::atan2( start_y - end_y, end_x - start_x );
1087 double angle_delta = last_character_angle -
angle;
1089 while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
1090 while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
1094 && angle_delta < li->max_char_angle_outside * ( M_PI / 180 ) ) )
1103 if ( orientation < 0 )
1108 start_x += dist * std::cos( angle + M_PI_2 );
1109 start_y -= dist * std::sin( angle + M_PI_2 );
1111 double render_angle =
angle;
1113 double render_x = start_x;
1114 double render_y = start_y;
1120 if ( orientation < 0 )
1123 render_x += ci.
width * std::cos( render_angle );
1124 render_y -= ci.
width * std::sin( render_angle );
1125 render_angle += M_PI;
1137 while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
1138 while ( render_angle < 0 ) render_angle += 2 * M_PI;
1140 if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI )
1163 std::unique_ptr< double [] > path_distances = qgis::make_unique<double[]>( mapShape->
nbPoints );
1164 double total_distance = 0;
1165 double old_x = -1.0, old_y = -1.0;
1166 for (
int i = 0; i < mapShape->
nbPoints; i++ )
1169 path_distances[i] = 0;
1171 path_distances[i] = std::sqrt( std::pow( old_x - mapShape->
x[i], 2 ) + std::pow( old_y - mapShape->
y[i], 2 ) );
1172 old_x = mapShape->
x[i];
1173 old_y = mapShape->
y[i];
1175 total_distance += path_distances[i];
1183 double totalCharacterWidth = 0;
1184 for (
int i = 0; i < li->
char_num; ++i )
1187 if ( totalCharacterWidth > total_distance )
1194 QLinkedList<LabelPosition *> positions;
1202 for (
double distanceAlongLineToStartCandidate = 0; distanceAlongLineToStartCandidate < total_distance; distanceAlongLineToStartCandidate += delta )
1206 bool reversed =
false;
1209 int orientation = 0;
1228 orientation = -orientation;
1229 slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
1236 double angle_diff = 0.0, angle_last = 0.0, diff;
1238 double sin_avg = 0, cos_avg = 0;
1243 diff = std::fabs( tmp->
getAlpha() - angle_last );
1244 if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
1245 diff = std::min( diff, 2 * M_PI - diff );
1249 sin_avg += std::sin( tmp->
getAlpha() );
1250 cos_avg += std::cos( tmp->
getAlpha() );
1255 double angle_diff_avg = li->
char_num > 1 ? ( angle_diff / ( li->
char_num - 1 ) ) : 0;
1256 double cost = angle_diff_avg / 100;
1257 if ( cost < 0.0001 ) cost = 0.0001;
1260 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1261 double costCenter = std::fabs( total_distance / 2 - labelCenter ) / total_distance;
1262 cost += costCenter / 100;
1266 double angle_avg = std::atan2( sin_avg / li->
char_num, cos_avg / li->
char_num );
1267 bool localreversed = flip ? !reversed : reversed;
1269 for (
int i = 0; i <= 2; ++i )
1276 p = _createCurvedCandidate( slp, angle_avg, 0 );
1279 if ( i == 2 && ( ( !localreversed && ( flags & FLAG_BELOW_LINE ) ) || ( localreversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
1289 while ( within && currentPos )
1302 positions.append( p );
1309 int nbp = positions.size();
1310 for (
int i = 0; i < nbp; i++ )
1312 lPos << positions.takeFirst();
1338 QLinkedList<PointSet *> shapes_toProcess;
1339 QLinkedList<PointSet *> shapes_final;
1341 mapShape->
parent =
nullptr;
1343 shapes_toProcess.append( mapShape );
1345 splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight );
1349 if ( !shapes_final.isEmpty() )
1351 QLinkedList<LabelPosition *> positions;
1361 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1367 while ( !shapes_final.isEmpty() )
1369 PointSet *shape = shapes_final.takeFirst();
1379 dx = labelWidth / 2.0;
1380 dy = labelHeight / 2.0;
1390 for ( bbid = 0; bbid < j; bbid++ )
1411 bool enoughPlace =
false;
1415 px = ( box->
x[0] + box->
x[2] ) / 2 - labelWidth;
1416 py = ( box->
y[0] + box->
y[2] ) / 2 - labelHeight;
1422 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1424 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1428 enoughPlace =
false;
1444 else if ( box->
length > 1.5 * labelWidth && box->
width > 1.5 * labelWidth )
1446 if ( box->
alpha <= M_PI_4 )
1452 alpha = box->
alpha - M_PI_2;
1457 alpha = box->
alpha - M_PI_2;
1464 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1470 dlx = std::cos( beta ) * diago;
1471 dly = std::sin( beta ) * diago;
1475 px0 = box->
width / 2.0;
1478 px0 -= std::ceil( px0 / dx ) * dx;
1479 py0 -= std::ceil( py0 / dy ) * dy;
1481 for ( px = px0; px <= box->
width; px += dx )
1483 for ( py = py0; py <= box->
length; py += dy )
1486 rx = std::cos( box->
alpha ) * px + std::cos( box->
alpha - M_PI_2 ) * py;
1487 ry = std::sin( box->
alpha ) * px + std::sin( box->
alpha - M_PI_2 ) * py;
1495 if ( candidateAcceptable )
1498 positions.append(
new LabelPosition(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this ) );
1504 nbp = positions.size();
1512 while ( nbp == 0 && numTry < maxTry );
1514 nbp = positions.size();
1516 for ( i = 0; i < nbp; i++ )
1518 lPos << positions.takeFirst();
1521 for ( bbid = 0; bbid < j; bbid++ )
1537 const GEOSPreparedGeometry *mapBoundary,
1538 PointSet *mapShape, RTree<LabelPosition *, double, 2, double> *candidates )
1558 case GEOS_LINESTRING:
1592 QMutableListIterator< LabelPosition *> i( lPos );
1593 while ( i.hasNext() )
1596 bool outside =
false;
1601 outside = !pos->
within( mapBoundary );
1614 return lPos.count();
1623 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
1625 double sizeCost = 0;
1626 if ( geomType == GEOS_LINESTRING )
1631 if ( GEOSLength_r( ctxt,
mGeos, &length ) != 1 )
1634 catch ( GEOSException &e )
1639 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
1640 if ( length >= bbox_length / 4 )
1643 sizeCost = 1 - ( length / ( bbox_length / 4 ) );
1645 else if ( geomType == GEOS_POLYGON )
1650 if ( GEOSArea_r( ctxt,
mGeos, &area ) != 1 )
1653 catch ( GEOSException &e )
1658 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
1659 if ( area >= bbox_area / 16 )
1662 sizeCost = 1 - ( area / ( bbox_area / 16 ) );
1668 for (
int i = 0; i < nbp; i++ )
1670 lPos.at( i )->setCost( lPos.at( i )->cost() + sizeCost / 100 );
1683 catch ( GEOSException &e )
1694 if ( !other->
mGeos )
1700 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
1701 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
1702 GEOSGeometry *geoms[2] = { g1, g2 };
1703 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
1706 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
1714 mGeos = gTmp.release();
1723 catch ( GEOSException &e )
1745 bool uprightLabel =
false;
1750 uprightLabel =
true;
1756 uprightLabel =
true;
1762 uprightLabel =
true;
1764 return uprightLabel;
1768 double &characterStartX,
double &characterStartY,
double &characterEndX,
double &characterEndY )
const 1777 double segmentStartX = path_positions->
x[index - 1];
1778 double segmentStartY = path_positions->
y[index - 1];
1780 double segmentEndX = path_positions->
x[index];
1781 double segmentEndY = path_positions->
y[index];
1783 double segmentDx = segmentEndX - segmentStartX;
1784 double segmentDy = segmentEndY - segmentStartY;
1786 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
1787 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
1793 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
1796 currentDistanceAlongSegment += charWidth;
1797 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
1798 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
1806 segmentStartX = segmentEndX;
1807 segmentStartY = segmentEndY;
1809 if ( index >= path_positions->
nbPoints )
1813 segmentEndX = path_positions->
x[index];
1814 segmentEndY = path_positions->
y[index];
1816 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth );
1822 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
QgsPointXY positionOffset() const
Applies only to "offset from point" placement strategy.
Label below point, slightly right of center.
pal::Layer * layer() const
Gets PAL layer of the label feature. Should be only used internally in PAL.
UpsideDownLabels upsidedownLabels() const
Returns how upside down labels are handled within the layer.
Label on bottom-left of point.
int upsideDownCharCount() const
Returns the number of upside down characters for this label position.
static bool candidateSortGrow(const LabelPosition *c1, const LabelPosition *c2)
Sorts label candidates in ascending order of cost.
bool hasFixedPosition() const
Returns true if the feature's label has a fixed position.
double max_char_angle_outside
bool alwaysShow() const
Whether label should be always shown (sets very high label priority)
double getLabelHeight() const
double fixedAngle() const
Returns the fixed angle for the feature's label.
Label on top-left of point.
void setCost(double newCost)
Sets the candidate label position's geographical cost.
int incrementUpsideDownCharCount()
Increases the count of upside down characters for this label position.
A set of features which influence the labeling process.
PredefinedPointPosition
Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode...
int createCandidatesAlongLine(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for line feature.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
const GEOSPreparedGeometry * permissibleZonePrepared() const
Returns a GEOS prepared geometry representing the label's permissibleZone().
void offsetPosition(double xOffset, double yOffset)
Shift the label by specified offset.
int createCandidatesAroundPoint(double x, double y, QList< LabelPosition * > &lPos, double angle)
Generate candidates for point feature, located around a specified point.
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
double cost() const
Returns the candidate label position's geographical cost.
friend class LabelPosition
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static void findLineCircleIntersection(double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes)
Label on top of point, slightly right of center.
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
void createGeosGeom() const
double getLabelWidth() const
double priority() const
Returns the layer's priority, between 0 and 1.
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged)...
bool centroidInside() const
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
double priority() const
Returns the feature's labeling priority.
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
void addSizePenalty(int nbp, QList< LabelPosition * > &lPos, double bbx[4], double bby[4])
void getCentroid(double &px, double &py, bool forceInside=false) const
double left() const
Returns the left margin.
int createCandidates(QList< LabelPosition * > &lPos, const GEOSPreparedGeometry *mapBoundary, PointSet *mapShape, RTree< LabelPosition *, double, 2, double > *candidates)
Generic method to generate label candidates for the feature.
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)
static GEOSContextHandle_t getGEOSHandler()
int createCurvedCandidatesAlongLine(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate curved candidates for line features.
QgsPalLayerSettings::OffsetType offsetType() const
Returns the offset type, which determines how offsets and distance to label behaves.
Arranges candidates following the curvature of a line feature. Applies to line layers only...
CharacterInfo * char_info
pal::LabelInfo * curvedLabelInfo() const
Gets additional infor required for curved label placement. Returns null if not set.
QgsPalLayerSettings::Placement arrangement() const
Returns the layer's arrangement policy.
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
bool isClosed() const
Returns true if pointset is closed.
const GEOSPreparedGeometry * preparedGeom() const
LabelPosition * curvedPlacementAtOffset(PointSet *path_positions, double *path_distances, int &orientation, double distance, bool &reversed, bool &flip)
Returns the label position for a curved label at a specific offset along a path.
QgsGeometry permissibleZone() const
Returns the label's permissible zone geometry.
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.
bool getShowPartial()
Returns whether partial labels should be allowed.
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py)
Gets a point a set distance along a line geometry.
double calculatePriority() const
Calculates the priority for the feature.
bool hasSameLabelFeatureAs(FeaturePart *part) const
Tests whether this feature part belongs to the same QgsLabelFeature as another feature part...
double getLabelDistance() const
const QSizeF & symbolSize() const
Returns the size of the rendered symbol associated with this feature, if applicable.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
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).
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.
int createCandidatesAlongLineNearStraightSegments(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for line feature, by trying to place candidates towards the middle of the longest...
double getY(int i=0) const
Returns the down-left y coordinate.
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
Optional additional info about label (for curved labels)
Layer * layer()
Returns the layer that feature belongs to.
int createCandidatesOverPoint(double x, double y, QList< LabelPosition * > &lPos, double angle)
Generate one candidate over or offset the specified point.
bool hasFixedRotation() const
Returns true if the feature's label has a fixed rotation.
double length() const
Returns length of line geometry.
void insertIntoIndex(RTree< LabelPosition *, double, 2, double > *index)
static double dist_euc2d(double x1, double y1, double x2, double y2)
bool showUprightLabels() const
Returns true if feature's label must be displayed upright.
LabelPosition * getNextPart() const
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
double getX(int i=0) const
Returns the down-left x coordinate.
Main class to handle feature.
Offset distance applies from rendered symbol bounds.
const QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
static void splitPolygons(QLinkedList< PointSet * > &shapes_toProcess, QLinkedList< PointSet * > &shapes_final, double xrm, double yrm)
Split a concave shape into several convex shapes.
double getAlpha() const
Returns the angle to rotate text (in rad).
QgsPointXY fixedPosition() const
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
int createCandidatesForPolygon(QList< LabelPosition * > &lPos, PointSet *mapShape)
Generate candidates for polygon features.
void setNextPart(LabelPosition *next)
CHullBox * compute_chull_bbox()
int createCandidatesAlongLineNearMidpoint(QList< LabelPosition * > &lPos, PointSet *mapShape, double initialCost=0.0)
Generate candidates for line feature, by trying to place candidates as close as possible to the line'...
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
double right() const
Returns the right margin.
double bottom() const
Returns the bottom margin.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
double top() const
Returns the top margin.
~FeaturePart() override
Delete the feature.
int connectedFeatureId(QgsFeatureId featureId) const
Returns the connected feature ID for a label feature ID, which is unique for all features which have ...
QList< FeaturePart * > mHoles
QVector< QgsPalLayerSettings::PredefinedPointPosition > predefinedPositionOrder() const
Returns the priority ordered list of predefined positions for label candidates.
double max_char_angle_inside
int createCandidatesAtOrderedPositionsOverPoint(double x, double y, QList< LabelPosition * > &lPos, double angle)
Generates candidates following a prioritized list of predefined positions around a point...
Label below point, slightly left of center.
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom
double distLabel() const
Applies to "around point" placement strategy or linestring features.
LabelPosition is a candidate feature label position.
QString name() const
Returns the layer's name.
Label on top of point, slightly left of center.
Quadrant
Position of label candidate relative to feature.
static int reorderPolygon(int nbPoints, double *x, double *y)
Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside...
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
FeaturePart(QgsLabelFeature *lf, const GEOSGeometry *geom)
Creates a new generic feature.
bool within(const GEOSPreparedGeometry *geometry)
Returns true if the label position is within a geometry.
bool intersects(const GEOSPreparedGeometry *geometry)
Returns true if the label position intersects a geometry.
double width() const
Returns the width of the rectangle.
bool isCurved() const
Returns true if the layer has curved labels.
double fixedAngle() const
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true) ...
The QgsMargins class defines the four margins of a rectangle.
double height() const
Returns the height of the rectangle.
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)
pal::LineArrangementFlags arrangementFlags() const
Returns the feature's arrangement flags.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...