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;
316 const auto constPositions = positions;
328 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
329 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
335 deltaX = -labelWidth / 4.0 - visualMargin.
left();
336 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
342 deltaX = -labelWidth / 2.0;
343 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
349 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
350 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
356 deltaX = - visualMargin.
left() + symbolWidthOffset;
357 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
363 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
364 deltaY = -labelHeight / 2.0;
370 deltaX = -visualMargin.
left() + symbolWidthOffset;
371 deltaY = -labelHeight / 2.0;
377 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
378 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
384 deltaX = -labelWidth / 4.0 - visualMargin.
left();
385 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
391 deltaX = -labelWidth / 2.0;
392 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
398 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
399 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
405 deltaX = -visualMargin.
left() + symbolWidthOffset;
406 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
411 double referenceX = std::cos( alpha ) * distanceToLabel +
x;
412 double referenceY = std::sin( alpha ) * distanceToLabel +
y;
414 double labelX = referenceX + deltaX;
415 double labelY = referenceY + deltaY;
419 lPos <<
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant );
441 double candidateAngleIncrement = 2 * M_PI / numberCandidates;
446 double a270 = a180 + a90;
447 double a360 = 2 * M_PI;
449 double gamma1, gamma2;
451 if ( distanceToLabel > 0 )
453 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
454 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
458 gamma1 = gamma2 = a90 / 3.0;
461 if ( gamma1 > a90 / 3.0 )
464 if ( gamma2 > a90 / 3.0 )
467 QList< LabelPosition * > candidates;
470 double angleToCandidate;
471 for ( i = 0, angleToCandidate = M_PI_4; i < numberCandidates; i++, angleToCandidate += candidateAngleIncrement )
476 if ( angleToCandidate > a360 )
477 angleToCandidate -= a360;
481 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
483 labelX += distanceToLabel;
484 double iota = ( angleToCandidate + gamma1 );
485 if ( iota > a360 - gamma1 )
489 labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
493 else if ( angleToCandidate < a90 - gamma2 )
495 labelX += distanceToLabel * std::cos( angleToCandidate );
496 labelY += distanceToLabel * std::sin( angleToCandidate );
499 else if ( angleToCandidate < a90 + gamma2 )
502 labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
503 labelY += distanceToLabel;
506 else if ( angleToCandidate < a180 - gamma1 )
508 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
509 labelY += distanceToLabel * std::sin( angleToCandidate );
512 else if ( angleToCandidate < a180 + gamma1 )
514 labelX += -distanceToLabel - labelWidth;
516 labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
519 else if ( angleToCandidate < a270 - gamma2 )
521 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
522 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
525 else if ( angleToCandidate < a270 + gamma2 )
527 labelY += -distanceToLabel - labelHeight;
529 labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
532 else if ( angleToCandidate < a360 )
534 labelX += distanceToLabel * std::cos( angleToCandidate );
535 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
541 if ( numberCandidates == 1 )
544 cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );
555 candidates <<
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant );
559 if ( icost == numberCandidates )
561 icost = numberCandidates - 1;
564 else if ( icost > numberCandidates )
566 icost = numberCandidates - 2;
572 if ( !candidates.isEmpty() )
574 for (
int i = 0; i < candidates.count(); ++i )
576 lPos << candidates.at( i );
580 return candidates.count();
588 if ( candidates < mLF->
layer()->
pal->line_p )
606 QVector< int > extremeAngleNodes;
614 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
616 double x1 = x[i - 1];
618 double x3 = x[ i == numberNodes - 1 ? 1 : i + 1];
619 double y1 = y[i - 1];
621 double y3 = y[ i == numberNodes - 1 ? 1 : i + 1];
626 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
630 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
631 extremeAngleNodes << i;
633 extremeAngleNodes << numberNodes - 1;
635 if ( extremeAngleNodes.isEmpty() )
642 double *segmentLengths =
new double[ numberNodes - 1 ];
643 double *distanceToSegment =
new double[ numberNodes ];
644 double totalLineLength = 0.0;
645 QVector< double > straightSegmentLengths;
646 QVector< double > straightSegmentAngles;
647 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
648 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
649 double currentStraightSegmentLength = 0;
650 double longestSegmentLength = 0;
651 int segmentIndex = 0;
652 double segmentStartX = x[0];
653 double segmentStartY = y[0];
654 for (
int i = 0; i < numberNodes - 1; i++ )
657 distanceToSegment[i] = 0;
659 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
662 totalLineLength += segmentLengths[i];
663 if ( extremeAngleNodes.contains( i ) )
666 straightSegmentLengths << currentStraightSegmentLength;
668 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
670 currentStraightSegmentLength = 0;
671 segmentStartX = x[i];
672 segmentStartY = y[i];
674 currentStraightSegmentLength += segmentLengths[i];
676 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
677 straightSegmentLengths << currentStraightSegmentLength;
679 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
680 double middleOfLine = totalLineLength / 2.0;
682 if ( totalLineLength < labelWidth )
684 delete[] segmentLengths;
685 delete[] distanceToSegment;
689 double lineStepDistance = ( totalLineLength - labelWidth );
690 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance /
mLF->
layer()->
pal->line_p );
692 double distanceToEndOfSegment = 0.0;
693 int lastNodeInSegment = 0;
695 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
697 currentStraightSegmentLength = straightSegmentLengths.at( i );
698 double currentSegmentAngle = straightSegmentAngles.at( i );
699 lastNodeInSegment = extremeAngleNodes.at( i );
700 double distanceToStartOfSegment = distanceToEndOfSegment;
701 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
702 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
704 if ( currentStraightSegmentLength < labelWidth )
708 double currentDistanceAlongLine = distanceToStartOfSegment;
709 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
710 double candidateLength = 0.0;
716 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
717 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
719 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
722 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
723 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
725 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
731 cost = candidateLength / labelWidth;
737 cost = ( 1 - cost ) / 100;
741 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
742 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
743 cost += costCenter * 0.0005;
750 double costLineCenter = 2 * std::fabs( labelCenter - middleOfLine ) / totalLineLength;
751 cost += costLineCenter * 0.0005;
754 cost += segmentCost * 0.0005;
755 cost += segmentAngleCost * 0.0001;
762 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
764 beta = angle + M_PI_2;
769 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
779 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
780 lPos.append(
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
787 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
788 lPos.append(
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
795 const double candidateCost = cost + 0.002;
796 lPos.append(
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
802 lPos.append(
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
809 currentDistanceAlongLine += lineStepDistance;
813 delete[] segmentLengths;
814 delete[] distanceToSegment;
832 QList<LabelPosition *> positions;
839 double *segmentLengths =
new double[nbPoints - 1];
840 double *distanceToSegment =
new double[
nbPoints];
842 double totalLineLength = 0.0;
843 for (
int i = 0; i < line->
nbPoints - 1; i++ )
846 distanceToSegment[i] = 0;
848 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
851 totalLineLength += segmentLengths[i];
853 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
855 double lineStepDistance = ( totalLineLength - labelWidth );
856 double currentDistanceAlongLine = 0;
858 if ( totalLineLength > labelWidth )
860 lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance /
mLF->
layer()->
pal->line_p );
864 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
865 lineStepDistance = -1;
866 totalLineLength = labelWidth;
871 currentDistanceAlongLine = std::numeric_limits< double >::max();
874 double candidateLength;
876 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
878 while ( currentDistanceAlongLine < totalLineLength - labelWidth )
881 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
882 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
884 if ( currentDistanceAlongLine < 0 )
887 candidateLength = std::sqrt( ( x[nbPoints - 1] - x[0] ) * ( x[nbPoints - 1] - x[0] )
888 + ( y[nbPoints - 1] - y[0] ) * ( y[nbPoints - 1] - y[0] ) );
892 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
895 cost = candidateLength / labelWidth;
901 cost = ( 1 - cost ) / 100;
905 double costCenter = std::fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
906 cost += costCenter / 1000;
914 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
916 beta = angle + M_PI_2;
921 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
931 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
932 positions.append(
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
939 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
940 positions.append(
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
947 const double candidateCost = cost + 0.002;
948 positions.append(
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
954 positions.append(
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
961 currentDistanceAlongLine += lineStepDistance;
965 if ( lineStepDistance < 0 )
971 delete[] segmentLengths;
972 delete[] distanceToSegment;
974 lPos.append( positions );
981 double offsetAlongSegment = offsetAlongLine;
984 while ( index < path_positions->
nbPoints && offsetAlongSegment > path_distances[index] )
986 offsetAlongSegment -= path_distances[index];
989 if ( index >= path_positions->
nbPoints )
998 const double segment_length = path_distances[index];
1005 if ( orientation == 0 )
1009 double _distance = offsetAlongSegment;
1010 int endindex = index;
1012 double startLabelX = 0;
1013 double startLabelY = 0;
1014 double endLabelX = 0;
1015 double endLabelY = 0;
1016 for (
int i = 0; i < li->
char_num; i++ )
1019 double characterStartX, characterStartY;
1020 if ( !
nextCharPosition( ci.
width, path_distances[endindex], path_positions, endindex, _distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
1026 startLabelX = characterStartX;
1027 startLabelY = characterStartY;
1032 double dx = endLabelX - startLabelX;
1033 double dy = endLabelY - startLabelY;
1034 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
1036 bool isRightToLeft = ( lineAngle > 90 || lineAngle < -90 );
1037 reversed = isRightToLeft;
1038 orientation = isRightToLeft ? -1 : 1;
1043 if ( orientation < 0 )
1046 reversed = !reversed;
1054 double old_x = path_positions->
x[index - 1];
1055 double old_y = path_positions->
y[index - 1];
1057 double new_x = path_positions->
x[index];
1058 double new_y = path_positions->
y[index];
1060 double dx = new_x - old_x;
1061 double dy = new_y - old_y;
1063 double angle = std::atan2( -dy, dx );
1065 for (
int i = 0; i < li->
char_num; i++ )
1067 double last_character_angle =
angle;
1075 double start_x, start_y, end_x, end_y;
1076 if ( !
nextCharPosition( ci.
width, path_distances[index], path_positions, index, offsetAlongSegment, start_x, start_y, end_x, end_y ) )
1083 angle = std::atan2( start_y - end_y, end_x - start_x );
1088 double angle_delta = last_character_angle -
angle;
1090 while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
1091 while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
1095 && angle_delta < li->max_char_angle_outside * ( M_PI / 180 ) ) )
1104 if ( orientation < 0 )
1109 start_x += dist * std::cos( angle + M_PI_2 );
1110 start_y -= dist * std::sin( angle + M_PI_2 );
1112 double render_angle =
angle;
1114 double render_x = start_x;
1115 double render_y = start_y;
1121 if ( orientation < 0 )
1124 render_x += ci.
width * std::cos( render_angle );
1125 render_y -= ci.
width * std::sin( render_angle );
1126 render_angle += M_PI;
1138 while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
1139 while ( render_angle < 0 ) render_angle += 2 * M_PI;
1141 if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI )
1164 std::unique_ptr< double [] > path_distances = qgis::make_unique<double[]>( mapShape->
nbPoints );
1165 double total_distance = 0;
1166 double old_x = -1.0, old_y = -1.0;
1167 for (
int i = 0; i < mapShape->
nbPoints; i++ )
1170 path_distances[i] = 0;
1172 path_distances[i] = std::sqrt( std::pow( old_x - mapShape->
x[i], 2 ) + std::pow( old_y - mapShape->
y[i], 2 ) );
1173 old_x = mapShape->
x[i];
1174 old_y = mapShape->
y[i];
1176 total_distance += path_distances[i];
1184 double totalCharacterWidth = 0;
1185 for (
int i = 0; i < li->
char_num; ++i )
1188 if ( totalCharacterWidth > total_distance )
1195 QLinkedList<LabelPosition *> positions;
1203 for (
double distanceAlongLineToStartCandidate = 0; distanceAlongLineToStartCandidate < total_distance; distanceAlongLineToStartCandidate += delta )
1207 bool reversed =
false;
1210 int orientation = 0;
1229 orientation = -orientation;
1230 slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
1237 double angle_diff = 0.0, angle_last = 0.0, diff;
1239 double sin_avg = 0, cos_avg = 0;
1244 diff = std::fabs( tmp->
getAlpha() - angle_last );
1245 if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
1246 diff = std::min( diff, 2 * M_PI - diff );
1250 sin_avg += std::sin( tmp->
getAlpha() );
1251 cos_avg += std::cos( tmp->
getAlpha() );
1256 double angle_diff_avg = li->
char_num > 1 ? ( angle_diff / ( li->
char_num - 1 ) ) : 0;
1257 double cost = angle_diff_avg / 100;
1258 if ( cost < 0.0001 ) cost = 0.0001;
1261 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1262 double costCenter = std::fabs( total_distance / 2 - labelCenter ) / total_distance;
1263 cost += costCenter / 100;
1267 double angle_avg = std::atan2( sin_avg / li->
char_num, cos_avg / li->
char_num );
1268 bool localreversed = flip ? !reversed : reversed;
1270 for (
int i = 0; i <= 2; ++i )
1277 p = _createCurvedCandidate( slp, angle_avg, 0 );
1280 if ( i == 2 && ( ( !localreversed && ( flags & FLAG_BELOW_LINE ) ) || ( localreversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
1290 while ( within && currentPos )
1303 positions.append( p );
1310 int nbp = positions.size();
1311 for (
int i = 0; i < nbp; i++ )
1313 lPos << positions.takeFirst();
1339 QLinkedList<PointSet *> shapes_toProcess;
1340 QLinkedList<PointSet *> shapes_final;
1342 mapShape->
parent =
nullptr;
1344 shapes_toProcess.append( mapShape );
1346 splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight );
1350 if ( !shapes_final.isEmpty() )
1352 QLinkedList<LabelPosition *> positions;
1362 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1368 while ( !shapes_final.isEmpty() )
1370 PointSet *shape = shapes_final.takeFirst();
1380 dx = labelWidth / 2.0;
1381 dy = labelHeight / 2.0;
1391 for ( bbid = 0; bbid < j; bbid++ )
1412 bool enoughPlace =
false;
1416 px = ( box->
x[0] + box->
x[2] ) / 2 - labelWidth;
1417 py = ( box->
y[0] + box->
y[2] ) / 2 - labelHeight;
1423 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1425 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1429 enoughPlace =
false;
1445 else if ( box->
length > 1.5 * labelWidth && box->
width > 1.5 * labelWidth )
1447 if ( box->
alpha <= M_PI_4 )
1453 alpha = box->
alpha - M_PI_2;
1458 alpha = box->
alpha - M_PI_2;
1465 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1471 dlx = std::cos( beta ) * diago;
1472 dly = std::sin( beta ) * diago;
1476 px0 = box->
width / 2.0;
1479 px0 -= std::ceil( px0 / dx ) * dx;
1480 py0 -= std::ceil( py0 / dy ) * dy;
1482 for ( px = px0; px <= box->
width; px += dx )
1484 for ( py = py0; py <= box->
length; py += dy )
1487 rx = std::cos( box->
alpha ) * px + std::cos( box->
alpha - M_PI_2 ) * py;
1488 ry = std::sin( box->
alpha ) * px + std::sin( box->
alpha - M_PI_2 ) * py;
1496 if ( candidateAcceptable )
1499 positions.append(
new LabelPosition(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this ) );
1505 nbp = positions.size();
1513 while ( nbp == 0 && numTry < maxTry );
1515 nbp = positions.size();
1517 for ( i = 0; i < nbp; i++ )
1519 lPos << positions.takeFirst();
1522 for ( bbid = 0; bbid < j; bbid++ )
1538 const GEOSPreparedGeometry *mapBoundary,
1539 PointSet *mapShape, RTree<LabelPosition *, double, 2, double> *candidates )
1559 case GEOS_LINESTRING:
1593 QMutableListIterator< LabelPosition *> i( lPos );
1594 while ( i.hasNext() )
1597 bool outside =
false;
1602 outside = !pos->
within( mapBoundary );
1615 return lPos.count();
1624 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
1626 double sizeCost = 0;
1627 if ( geomType == GEOS_LINESTRING )
1632 if ( GEOSLength_r( ctxt,
mGeos, &length ) != 1 )
1635 catch ( GEOSException &e )
1640 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
1641 if ( length >= bbox_length / 4 )
1644 sizeCost = 1 - ( length / ( bbox_length / 4 ) );
1646 else if ( geomType == GEOS_POLYGON )
1651 if ( GEOSArea_r( ctxt,
mGeos, &area ) != 1 )
1654 catch ( GEOSException &e )
1659 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
1660 if ( area >= bbox_area / 16 )
1663 sizeCost = 1 - ( area / ( bbox_area / 16 ) );
1669 for (
int i = 0; i < nbp; i++ )
1671 lPos.at( i )->setCost( lPos.at( i )->cost() + sizeCost / 100 );
1684 catch ( GEOSException &e )
1695 if ( !other->
mGeos )
1701 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
1702 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
1703 GEOSGeometry *geoms[2] = { g1, g2 };
1704 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
1707 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
1715 mGeos = gTmp.release();
1724 catch ( GEOSException &e )
1746 bool uprightLabel =
false;
1751 uprightLabel =
true;
1757 uprightLabel =
true;
1763 uprightLabel =
true;
1765 return uprightLabel;
1769 double &characterStartX,
double &characterStartY,
double &characterEndX,
double &characterEndY )
const 1778 double segmentStartX = path_positions->
x[index - 1];
1779 double segmentStartY = path_positions->
y[index - 1];
1781 double segmentEndX = path_positions->
x[index];
1782 double segmentEndY = path_positions->
y[index];
1784 double segmentDx = segmentEndX - segmentStartX;
1785 double segmentDy = segmentEndY - segmentStartY;
1787 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
1788 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
1794 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
1797 currentDistanceAlongSegment += charWidth;
1798 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
1799 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
1807 segmentStartX = segmentEndX;
1808 segmentStartY = segmentEndY;
1810 if ( index >= path_positions->
nbPoints )
1814 segmentEndX = path_positions->
x[index];
1815 segmentEndY = path_positions->
y[index];
1817 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth );
1823 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
Label on bottom right of point.
double right() const
Returns the right margin.
bool centroidInside() const
Returns whether labels placed at the centroid of features within the layer are forced to be placed in...
Label on bottom-left of point.
int createCandidatesForPolygon(QList< LabelPosition *> &lPos, PointSet *mapShape)
Generate candidates for polygon features.
Label on top of point, slightly left of center.
double distLabel() const
Applies to "around point" placement strategy or linestring features.
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
double fixedAngle() const
Returns the fixed angle for the feature's label.
QgsFeatureId featureId() const
Returns the unique ID of the feature.
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)
static bool candidateSortGrow(const LabelPosition *c1, const LabelPosition *c2)
Sorts label candidates in ascending order of cost.
double max_char_angle_outside
double priority() const
Returns the feature's labeling priority.
bool alwaysShow() const
Whether label should be always shown (sets very high label priority)
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.
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
bool hasFixedRotation() const
Returns true if the feature's label has a fixed rotation.
double getY(int i=0) const
Returns the down-left y coordinate.
A set of features which influence the labeling process.
PredefinedPointPosition
Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
void offsetPosition(double xOffset, double yOffset)
Shift the label by specified offset.
int createCandidatesAlongLineNearStraightSegments(QList< LabelPosition *> &lPos, PointSet *mapShape)
Generate candidates for line feature, by trying to place candidates towards the middle of the longest...
void createGeosGeom() const
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
friend class LabelPosition
const GEOSPreparedGeometry * permissibleZonePrepared() const
Returns a GEOS prepared geometry representing the label's permissibleZone().
static void findLineCircleIntersection(double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes)
Label on top-right of point.
const QSizeF & symbolSize() const
Returns the size of the rendered symbol associated with this feature, if applicable.
bool isClosed() const
Returns true if pointset is closed.
bool mergeWithFeaturePart(FeaturePart *other)
Merge other (connected) part with this one and save the result in this part (other is unchanged)...
UpsideDownLabels upsidedownLabels() const
Returns how upside down labels are handled within the layer.
bool isConnected(FeaturePart *p2)
Check whether this part is connected with some other part.
const QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
double getLabelDistance() const
int createCandidatesAtOrderedPositionsOverPoint(double x, double y, QList< LabelPosition *> &lPos, double angle)
Generates candidates following a prioritized list of predefined positions around a point...
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()
QgsPointXY positionOffset() const
Applies only to "offset from point" placement strategy.
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
CharacterInfo * char_info
int createCandidatesAroundPoint(double x, double y, QList< LabelPosition *> &lPos, double angle)
Generate candidates for point feature, located around a specified point.
double priority() const
Returns the layer's priority, between 0 and 1.
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.
double cost() const
Returns the candidate label position's geographical cost.
pal::LabelInfo * curvedLabelInfo() const
Gets additional infor required for curved label placement. Returns nullptr if not set...
bool hasFixedQuadrant() const
Returns whether the quadrant for the label is fixed.
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.
QgsGeometry permissibleZone() const
Returns the label's permissible zone geometry.
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.
void addSizePenalty(int nbp, QList< LabelPosition *> &lPos, double bbx[4], double bby[4])
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.
double bottom() const
Returns the bottom margin.
double width() const
Returns the width of the rectangle.
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).
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
LabelPosition * getNextPart() const
Optional additional info about label (for curved labels)
Layer * layer()
Returns the layer that feature belongs to.
double calculatePriority() const
Calculates the priority for the feature.
QgsPalLayerSettings::Placement arrangement() const
Returns the layer's arrangement policy.
bool hasFixedAngle() const
Whether the label should use a fixed angle instead of using angle from automatic placement.
double top() const
Returns the top margin.
void insertIntoIndex(RTree< LabelPosition *, double, 2, double > *index)
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.
pal::LineArrangementFlags arrangementFlags() const
Returns the feature's arrangement flags.
static double dist_euc2d(double x1, double y1, double x2, double y2)
pal::Layer * layer() const
Gets PAL layer of the label feature. Should be only used internally in PAL.
int createCandidatesAlongLine(QList< LabelPosition *> &lPos, PointSet *mapShape)
Generate candidates for line feature.
double fixedAngle() const
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true) ...
Label below point, slightly right of center.
double getLabelHeight() const
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
Main class to handle feature.
int upsideDownCharCount() const
Returns the number of upside down characters for this label position.
Offset distance applies from rendered symbol bounds.
bool hasFixedPosition() const
Returns true if the feature's label has a fixed position.
int createCandidatesOverPoint(double x, double y, QList< LabelPosition *> &lPos, double angle)
Generate one candidate over or offset the specified point.
QPointF quadOffset() const
Applies to "offset from point" placement strategy and "around point" (in case hasFixedQuadrant() retu...
void setNextPart(LabelPosition *next)
CHullBox * compute_chull_bbox()
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
bool hasFixedPosition() const
Whether the label should use a fixed position instead of being automatically placed.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
bool hasSameLabelFeatureAs(FeaturePart *part) const
Tests whether this feature part belongs to the same QgsLabelFeature as another feature part...
double getAlpha() const
Returns the angle to rotate text (in rad).
double length() const
Returns length of line geometry.
QgsPalLayerSettings::OffsetType offsetType() const
Returns the offset type, which determines how offsets and distance to label behaves.
Label below point, slightly left of center.
~FeaturePart() override
Delete the feature.
QList< FeaturePart * > mHoles
double getX(int i=0) const
Returns the down-left x coordinate.
double max_char_angle_inside
Label on top of point, slightly right of center.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
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'...
Label directly below point.
void extractCoords(const GEOSGeometry *geom)
read coordinates from a GEOS geom
QString name() const
Returns the layer's name.
double getLabelWidth() const
LabelPosition is a candidate feature label position.
Label directly above point.
Quadrant
Position of label candidate relative to feature.
const GEOSPreparedGeometry * preparedGeom() const
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...
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.
QgsPointXY fixedPosition() const
Coordinates of the fixed position (relevant only if hasFixedPosition() returns true) ...
QVector< QgsPalLayerSettings::PredefinedPointPosition > predefinedPositionOrder() const
Returns the priority ordered list of predefined positions for label candidates.
bool intersects(const GEOSPreparedGeometry *geometry)
Returns true if the label position intersects a geometry.
void getCentroid(double &px, double &py, bool forceInside=false) const
static void splitPolygons(QLinkedList< PointSet *> &shapes_toProcess, QLinkedList< PointSet *> &shapes_final, double xrm, double yrm)
Split a concave shape into several convex shapes.
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only...
double left() const
Returns the left margin.
bool isCurved() const
Returns true if the layer has curved labels.
bool showUprightLabels() const
Returns true if feature's label must be displayed upright.
The QgsMargins class defines the four margins of a rectangle.
double height() const
Returns the height of the rectangle.
int connectedFeatureId(QgsFeatureId featureId) const
Returns the connected feature ID for a label feature ID, which is unique for all features which have ...
int createCurvedCandidatesAlongLine(QList< LabelPosition *> &lPos, PointSet *mapShape)
Generate curved candidates for line features.
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...