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 )
226 return mTotalRepeats;
242 double cost = 0.0001;
245 double xdiff = -labelW / 2.0;
246 double ydiff = -labelH / 2.0;
261 double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
262 double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
298 double lx = x + xdiff;
299 double ly = y + ydiff;
309 lPos <<
new LabelPosition(
id, lx, ly, labelW, labelH, angle, cost,
this,
false, quadrantFromOffset() );
322 const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
323 GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
324 GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
327 catch ( GEOSException &e )
347 double cost = 0.0001;
349 const auto constPositions = positions;
363 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
364 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
370 deltaX = -labelWidth / 4.0 - visualMargin.
left();
371 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
377 deltaX = -labelWidth / 2.0;
378 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
384 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
385 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
391 deltaX = - visualMargin.
left() + symbolWidthOffset;
392 deltaY = -visualMargin.
bottom() + symbolHeightOffset;
398 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
399 deltaY = -labelHeight / 2.0;
405 deltaX = -visualMargin.
left() + symbolWidthOffset;
406 deltaY = -labelHeight / 2.0;
412 deltaX = -labelWidth + visualMargin.
right() - symbolWidthOffset;
413 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
419 deltaX = -labelWidth / 4.0 - visualMargin.
left();
420 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
426 deltaX = -labelWidth / 2.0;
427 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
433 deltaX = -labelWidth * 3.0 / 4.0 + visualMargin.
right();
434 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
440 deltaX = -visualMargin.
left() + symbolWidthOffset;
441 deltaY = -labelHeight + visualMargin.
top() - symbolHeightOffset;
446 double referenceX = std::cos( alpha ) * distanceToLabel +
x;
447 double referenceY = std::sin( alpha ) * distanceToLabel +
y;
449 double labelX = referenceX + deltaX;
450 double labelY = referenceY + deltaY;
454 lPos <<
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant );
457 if ( lPos.size() >= maxNumberCandidates )
477 double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates;
482 double a270 = a180 + a90;
483 double a360 = 2 * M_PI;
485 double gamma1, gamma2;
487 if ( distanceToLabel > 0 )
489 gamma1 = std::atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
490 gamma2 = std::atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
494 gamma1 = gamma2 = a90 / 3.0;
497 if ( gamma1 > a90 / 3.0 )
500 if ( gamma2 > a90 / 3.0 )
503 QList< LabelPosition * > candidates;
506 double angleToCandidate;
507 for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
512 if ( angleToCandidate > a360 )
513 angleToCandidate -= a360;
517 if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 )
519 labelX += distanceToLabel;
520 double iota = ( angleToCandidate + gamma1 );
521 if ( iota > a360 - gamma1 )
525 labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
529 else if ( angleToCandidate < a90 - gamma2 )
531 labelX += distanceToLabel * std::cos( angleToCandidate );
532 labelY += distanceToLabel * std::sin( angleToCandidate );
535 else if ( angleToCandidate < a90 + gamma2 )
538 labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
539 labelY += distanceToLabel;
542 else if ( angleToCandidate < a180 - gamma1 )
544 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
545 labelY += distanceToLabel * std::sin( angleToCandidate );
548 else if ( angleToCandidate < a180 + gamma1 )
550 labelX += -distanceToLabel - labelWidth;
552 labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
555 else if ( angleToCandidate < a270 - gamma2 )
557 labelX += distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
558 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
561 else if ( angleToCandidate < a270 + gamma2 )
563 labelY += -distanceToLabel - labelHeight;
565 labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
568 else if ( angleToCandidate < a360 )
570 labelX += distanceToLabel * std::cos( angleToCandidate );
571 labelY += distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
577 if ( maxNumberCandidates == 1 )
580 cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
591 candidates <<
new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost,
this,
false, quadrant );
595 if ( icost == maxNumberCandidates )
597 icost = maxNumberCandidates - 1;
600 else if ( icost > maxNumberCandidates )
602 icost = maxNumberCandidates - 2;
608 if ( !candidates.isEmpty() )
610 for (
int i = 0; i < candidates.count(); ++i )
612 lPos << candidates.at( i );
616 return candidates.count();
623 double shapeLength = mapShape->
length();
636 if ( candidates < mLF->
layer()->maximumLineLabelCandidates() )
654 QVector< int > extremeAngleNodes;
657 std::vector< double > &
x = line->
x;
658 std::vector< double > &
y = line->
y;
662 for (
int i = 1; i <= numberNodes - ( closedLine ? 1 : 2 ); ++i )
664 double x1 = x[i - 1];
666 double x3 = x[ i == numberNodes - 1 ? 1 : i + 1];
667 double y1 = y[i - 1];
669 double y3 = y[ i == numberNodes - 1 ? 1 : i + 1];
674 double vertexAngle = M_PI - ( std::atan2( y3 - y2, x3 - x2 ) - std::atan2( y2 - y1, x2 - x1 ) );
678 if ( vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0 )
679 extremeAngleNodes << i;
681 extremeAngleNodes << numberNodes - 1;
683 if ( extremeAngleNodes.isEmpty() )
690 double *segmentLengths =
new double[ numberNodes - 1 ];
691 double *distanceToSegment =
new double[ numberNodes ];
692 double totalLineLength = 0.0;
693 QVector< double > straightSegmentLengths;
694 QVector< double > straightSegmentAngles;
695 straightSegmentLengths.reserve( extremeAngleNodes.size() + 1 );
696 straightSegmentAngles.reserve( extremeAngleNodes.size() + 1 );
697 double currentStraightSegmentLength = 0;
698 double longestSegmentLength = 0;
699 int segmentIndex = 0;
700 double segmentStartX = x[0];
701 double segmentStartY = y[0];
702 for (
int i = 0; i < numberNodes - 1; i++ )
705 distanceToSegment[i] = 0;
707 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
710 totalLineLength += segmentLengths[i];
711 if ( extremeAngleNodes.contains( i ) )
714 straightSegmentLengths << currentStraightSegmentLength;
716 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
718 currentStraightSegmentLength = 0;
719 segmentStartX = x[i];
720 segmentStartY = y[i];
722 currentStraightSegmentLength += segmentLengths[i];
724 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
725 straightSegmentLengths << currentStraightSegmentLength;
727 longestSegmentLength = std::max( longestSegmentLength, currentStraightSegmentLength );
728 double middleOfLine = totalLineLength / 2.0;
730 if ( totalLineLength < labelWidth )
732 delete[] segmentLengths;
733 delete[] distanceToSegment;
737 double lineStepDistance = ( totalLineLength - labelWidth );
740 double distanceToEndOfSegment = 0.0;
741 int lastNodeInSegment = 0;
743 for (
int i = 0; i < straightSegmentLengths.count(); ++i )
745 currentStraightSegmentLength = straightSegmentLengths.at( i );
746 double currentSegmentAngle = straightSegmentAngles.at( i );
747 lastNodeInSegment = extremeAngleNodes.at( i );
748 double distanceToStartOfSegment = distanceToEndOfSegment;
749 distanceToEndOfSegment = distanceToSegment[ lastNodeInSegment ];
750 double distanceToCenterOfSegment = 0.5 * ( distanceToEndOfSegment + distanceToStartOfSegment );
752 if ( currentStraightSegmentLength < labelWidth )
756 double currentDistanceAlongLine = distanceToStartOfSegment;
757 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
758 double candidateLength = 0.0;
764 double segmentCost = 1.0 - ( distanceToEndOfSegment - distanceToStartOfSegment ) / longestSegmentLength;
765 double segmentAngleCost = 1 - std::fabs( std::fmod( currentSegmentAngle, M_PI ) - M_PI_2 ) / M_PI_2;
767 while ( currentDistanceAlongLine + labelWidth < distanceToEndOfSegment )
770 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
771 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
773 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
779 cost = candidateLength / labelWidth;
785 cost = ( 1 - cost ) / 100;
789 double labelCenter = currentDistanceAlongLine + labelWidth / 2.0;
790 double costCenter = 2 * std::fabs( labelCenter - distanceToCenterOfSegment ) / ( distanceToEndOfSegment - distanceToStartOfSegment );
791 cost += costCenter * 0.0005;
798 double costLineCenter = 2 * std::fabs( labelCenter - middleOfLine ) / totalLineLength;
799 cost += costLineCenter * 0.0005;
802 cost += segmentCost * 0.0005;
803 cost += segmentAngleCost * 0.0001;
810 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
814 beta = angle + M_PI_2;
819 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
829 const double candidateCost = cost + ( reversed ? 0 : 0.001 );
830 lPos.append(
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
837 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
838 lPos.append(
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
845 const double candidateCost = cost + 0.002;
846 lPos.append(
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
852 lPos.append(
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
859 currentDistanceAlongLine += lineStepDistance;
863 delete[] segmentLengths;
864 delete[] distanceToSegment;
882 QList<LabelPosition *> positions;
886 std::vector< double > &
x = line->
x;
887 std::vector< double > &
y = line->
y;
889 double *segmentLengths =
new double[nbPoints - 1];
890 double *distanceToSegment =
new double[
nbPoints];
892 double totalLineLength = 0.0;
893 for (
int i = 0; i < line->
nbPoints - 1; i++ )
896 distanceToSegment[i] = 0;
898 distanceToSegment[i] = distanceToSegment[i - 1] + segmentLengths[i - 1];
901 totalLineLength += segmentLengths[i];
903 distanceToSegment[line->
nbPoints - 1] = totalLineLength;
905 double lineStepDistance = ( totalLineLength - labelWidth );
906 double currentDistanceAlongLine = 0;
908 if ( totalLineLength > labelWidth )
914 currentDistanceAlongLine = - ( labelWidth - totalLineLength ) / 2.0;
915 lineStepDistance = -1;
916 totalLineLength = labelWidth;
921 currentDistanceAlongLine = std::numeric_limits< double >::max();
924 double candidateLength;
926 double candidateStartX, candidateStartY, candidateEndX, candidateEndY;
928 while ( currentDistanceAlongLine < totalLineLength - labelWidth )
931 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine, &candidateStartX, &candidateStartY );
932 line->
getPointByDistance( segmentLengths, distanceToSegment, currentDistanceAlongLine + labelWidth, &candidateEndX, &candidateEndY );
934 if ( currentDistanceAlongLine < 0 )
937 candidateLength = std::sqrt( ( x[nbPoints - 1] - x[0] ) * ( x[nbPoints - 1] - x[0] )
938 + ( y[nbPoints - 1] - y[0] ) * ( y[nbPoints - 1] - y[0] ) );
942 candidateLength = std::sqrt( ( candidateEndX - candidateStartX ) * ( candidateEndX - candidateStartX ) + ( candidateEndY - candidateStartY ) * ( candidateEndY - candidateStartY ) );
945 cost = candidateLength / labelWidth;
951 cost = ( 1 - cost ) / 100;
955 double costCenter = std::fabs( totalLineLength / 2 - ( currentDistanceAlongLine + labelWidth / 2 ) ) / totalLineLength;
956 cost += costCenter / 1000;
964 angle = std::atan2( candidateEndY - candidateStartY, candidateEndX - candidateStartX );
968 beta = angle + M_PI_2;
973 bool isRightToLeft = ( angle > M_PI_2 || angle <= -M_PI_2 );
983 const double candidateCost = cost + ( !reversed ? 0 : 0.001 );
984 positions.append(
new LabelPosition( i, candidateStartX + std::cos( beta ) *distanceLineToLabel, candidateStartY + std::sin( beta ) *distanceLineToLabel, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
991 const double candidateCost = cost + ( !reversed ? 0.001 : 0 );
992 positions.append(
new LabelPosition( i, candidateStartX - std::cos( beta ) * ( distanceLineToLabel + labelHeight ), candidateStartY - std::sin( beta ) * ( distanceLineToLabel + labelHeight ), labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
999 const double candidateCost = cost + 0.002;
1000 positions.append(
new LabelPosition( i, candidateStartX - labelHeight * std::cos( beta ) / 2, candidateStartY - labelHeight * std::sin( beta ) / 2, labelWidth, labelHeight, angle, candidateCost,
this, isRightToLeft ) );
1006 positions.append(
new LabelPosition( i, candidateStartX - labelWidth / 2, candidateStartY - labelHeight / 2, labelWidth, labelHeight, 0, cost,
this ) );
1013 currentDistanceAlongLine += lineStepDistance;
1017 if ( lineStepDistance < 0 )
1023 delete[] segmentLengths;
1024 delete[] distanceToSegment;
1026 lPos.append( positions );
1033 double offsetAlongSegment = offsetAlongLine;
1036 while ( index < path_positions->
nbPoints && offsetAlongSegment > path_distances[index] )
1038 offsetAlongSegment -= path_distances[index];
1041 if ( index >= path_positions->
nbPoints )
1050 const double segment_length = path_distances[index];
1057 if ( orientation == 0 )
1061 double _distance = offsetAlongSegment;
1062 int endindex = index;
1064 double startLabelX = 0;
1065 double startLabelY = 0;
1066 double endLabelX = 0;
1067 double endLabelY = 0;
1068 for (
int i = 0; i < li->
char_num; i++ )
1071 double characterStartX, characterStartY;
1072 if ( !
nextCharPosition( ci.
width, path_distances[endindex], path_positions, endindex, _distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
1078 startLabelX = characterStartX;
1079 startLabelY = characterStartY;
1084 double dx = endLabelX - startLabelX;
1085 double dy = endLabelY - startLabelY;
1086 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
1088 bool isRightToLeft = ( lineAngle > 90 || lineAngle < -90 );
1089 reversed = isRightToLeft;
1090 orientation = isRightToLeft ? -1 : 1;
1095 if ( orientation < 0 )
1098 reversed = !reversed;
1106 double old_x = path_positions->
x[index - 1];
1107 double old_y = path_positions->
y[index - 1];
1109 double new_x = path_positions->
x[index];
1110 double new_y = path_positions->
y[index];
1112 double dx = new_x - old_x;
1113 double dy = new_y - old_y;
1115 double angle = std::atan2( -dy, dx );
1117 for (
int i = 0; i < li->
char_num; i++ )
1119 double last_character_angle =
angle;
1127 double start_x, start_y, end_x, end_y;
1128 if ( !
nextCharPosition( ci.
width, path_distances[index], path_positions, index, offsetAlongSegment, start_x, start_y, end_x, end_y ) )
1135 angle = std::atan2( start_y - end_y, end_x - start_x );
1140 double angle_delta = last_character_angle -
angle;
1142 while ( angle_delta > M_PI ) angle_delta -= 2 * M_PI;
1143 while ( angle_delta < -M_PI ) angle_delta += 2 * M_PI;
1147 && angle_delta < li->max_char_angle_outside * ( M_PI / 180 ) ) )
1156 if ( orientation < 0 )
1161 start_x += dist * std::cos( angle + M_PI_2 );
1162 start_y -= dist * std::sin( angle + M_PI_2 );
1164 double render_angle =
angle;
1166 double render_x = start_x;
1167 double render_y = start_y;
1173 if ( orientation < 0 )
1176 render_x += ci.
width * std::cos( render_angle );
1177 render_y -= ci.
width * std::sin( render_angle );
1178 render_angle += M_PI;
1190 while ( render_angle >= 2 * M_PI ) render_angle -= 2 * M_PI;
1191 while ( render_angle < 0 ) render_angle += 2 * M_PI;
1193 if ( render_angle > M_PI_2 && render_angle < 1.5 * M_PI )
1218 double totalCharacterWidth = 0;
1219 for (
int i = 0; i < li->
char_num; ++i )
1222 std::unique_ptr< PointSet > expanded;
1223 double shapeLength = mapShape->
length();
1226 allowOverrun =
false;
1232 if ( totalCharacterWidth > shapeLength )
1234 if ( !allowOverrun || shapeLength < totalCharacterWidth - 2 * overrun )
1241 if ( allowOverrun && overrun > 0 )
1244 expanded = mapShape->
clone();
1246 mapShape = expanded.get();
1247 shapeLength = mapShape->
length();
1251 std::unique_ptr< double [] > path_distances = qgis::make_unique<double[]>( mapShape->
nbPoints );
1252 double total_distance = 0;
1253 double old_x = -1.0, old_y = -1.0;
1254 for (
int i = 0; i < mapShape->
nbPoints; i++ )
1257 path_distances[i] = 0;
1259 path_distances[i] = std::sqrt( std::pow( old_x - mapShape->
x[i], 2 ) + std::pow( old_y - mapShape->
y[i], 2 ) );
1260 old_x = mapShape->
x[i];
1261 old_y = mapShape->
y[i];
1263 total_distance += path_distances[i];
1271 QLinkedList<LabelPosition *> positions;
1279 for (
double distanceAlongLineToStartCandidate = 0; distanceAlongLineToStartCandidate < total_distance; distanceAlongLineToStartCandidate += delta )
1283 bool reversed =
false;
1286 int orientation = 0;
1305 orientation = -orientation;
1306 slp =
curvedPlacementAtOffset( mapShape, path_distances.get(), orientation, distanceAlongLineToStartCandidate, reversed, flip );
1313 double angle_diff = 0.0, angle_last = 0.0, diff;
1315 double sin_avg = 0, cos_avg = 0;
1320 diff = std::fabs( tmp->
getAlpha() - angle_last );
1321 if ( diff > 2 * M_PI ) diff -= 2 * M_PI;
1322 diff = std::min( diff, 2 * M_PI - diff );
1326 sin_avg += std::sin( tmp->
getAlpha() );
1327 cos_avg += std::cos( tmp->
getAlpha() );
1332 double angle_diff_avg = li->
char_num > 1 ? ( angle_diff / ( li->
char_num - 1 ) ) : 0;
1333 double cost = angle_diff_avg / 100;
1334 if ( cost < 0.0001 ) cost = 0.0001;
1337 double labelCenter = distanceAlongLineToStartCandidate +
getLabelWidth() / 2;
1338 double costCenter = std::fabs( total_distance / 2 - labelCenter ) / total_distance;
1339 cost += costCenter / 100;
1343 double angle_avg = std::atan2( sin_avg / li->
char_num, cos_avg / li->
char_num );
1344 bool localreversed = flip ? !reversed : reversed;
1346 for (
int i = 0; i <= 2; ++i )
1353 p = _createCurvedCandidate( slp, angle_avg, 0 );
1356 if ( i == 2 && ( ( !localreversed && ( flags & FLAG_BELOW_LINE ) ) || ( localreversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
1366 while ( within && currentPos )
1379 positions.append( p );
1386 int nbp = positions.size();
1387 for (
int i = 0; i < nbp; i++ )
1389 lPos << positions.takeFirst();
1415 QLinkedList<PointSet *> shapes_toProcess;
1416 QLinkedList<PointSet *> shapes_final;
1418 mapShape->
parent =
nullptr;
1420 shapes_toProcess.append( mapShape );
1422 splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight );
1426 if ( !shapes_final.isEmpty() )
1428 QLinkedList<LabelPosition *> positions;
1438 double diago = std::sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
1444 while ( !shapes_final.isEmpty() )
1446 PointSet *shape = shapes_final.takeFirst();
1456 dx = labelWidth / 2.0;
1457 dy = labelHeight / 2.0;
1467 for ( bbid = 0; bbid < j; bbid++ )
1488 bool enoughPlace =
false;
1492 px = ( box->
x[0] + box->
x[2] ) / 2 - labelWidth;
1493 py = ( box->
y[0] + box->
y[2] ) / 2 - labelHeight;
1499 for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
1501 for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
1505 enoughPlace =
false;
1521 else if ( box->
length > 1.5 * labelWidth && box->
width > 1.5 * labelWidth )
1523 if ( box->
alpha <= M_PI_4 )
1529 alpha = box->
alpha - M_PI_2;
1534 alpha = box->
alpha - M_PI_2;
1541 beta = std::atan2( labelHeight, labelWidth ) + alpha;
1547 dlx = std::cos( beta ) * diago;
1548 dly = std::sin( beta ) * diago;
1552 px0 = box->
width / 2.0;
1555 px0 -= std::ceil( px0 / dx ) * dx;
1556 py0 -= std::ceil( py0 / dy ) * dy;
1558 for ( px = px0; px <= box->
width; px += dx )
1560 for ( py = py0; py <= box->
length; py += dy )
1563 rx = std::cos( box->
alpha ) * px + std::cos( box->
alpha - M_PI_2 ) * py;
1564 ry = std::sin( box->
alpha ) * px + std::sin( box->
alpha - M_PI_2 ) * py;
1572 if ( candidateAcceptable )
1575 positions.append(
new LabelPosition(
id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001,
this ) );
1581 nbp = positions.size();
1589 while ( nbp == 0 && numTry < maxTry );
1591 nbp = positions.size();
1593 for ( i = 0; i < nbp; i++ )
1595 lPos << positions.takeFirst();
1598 for ( bbid = 0; bbid < j; bbid++ )
1614 PointSet *mapShape, RTree<LabelPosition *, double, 2, double> *candidates )
1616 QList<LabelPosition *> lPos;
1635 case GEOS_LINESTRING:
1669 QMutableListIterator< LabelPosition *> i( lPos );
1670 while ( i.hasNext() )
1673 bool outside =
false;
1678 outside = !pos->
within( mapBoundary );
1700 int geomType = GEOSGeomTypeId_r( ctxt,
mGeos );
1702 double sizeCost = 0;
1703 if ( geomType == GEOS_LINESTRING )
1708 if ( GEOSLength_r( ctxt,
mGeos, &length ) != 1 )
1711 catch ( GEOSException &e )
1716 double bbox_length = std::max( bbx[2] - bbx[0], bby[2] - bby[0] );
1717 if ( length >= bbox_length / 4 )
1720 sizeCost = 1 - ( length / ( bbox_length / 4 ) );
1722 else if ( geomType == GEOS_POLYGON )
1727 if ( GEOSArea_r( ctxt,
mGeos, &area ) != 1 )
1730 catch ( GEOSException &e )
1735 double bbox_area = ( bbx[2] - bbx[0] ) * ( bby[2] - bby[0] );
1736 if ( area >= bbox_area / 16 )
1739 sizeCost = 1 - ( area / ( bbox_area / 16 ) );
1745 for (
int i = 0; i < nbp; i++ )
1747 lPos.at( i )->setCost( lPos.at( i )->cost() + sizeCost / 100 );
1760 catch ( GEOSException &e )
1771 if ( !other->
mGeos )
1777 GEOSGeometry *g1 = GEOSGeom_clone_r( ctxt,
mGeos );
1778 GEOSGeometry *g2 = GEOSGeom_clone_r( ctxt, other->
mGeos );
1779 GEOSGeometry *geoms[2] = { g1, g2 };
1780 geos::unique_ptr g( GEOSGeom_createCollection_r( ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
1783 if ( GEOSGeomTypeId_r( ctxt, gTmp.get() ) != GEOS_LINESTRING )
1791 mGeos = gTmp.release();
1800 catch ( GEOSException &e )
1822 bool uprightLabel =
false;
1827 uprightLabel =
true;
1833 uprightLabel =
true;
1839 uprightLabel =
true;
1841 return uprightLabel;
1845 double &characterStartX,
double &characterStartY,
double &characterEndX,
double &characterEndY )
const 1854 double segmentStartX = path_positions->
x[index - 1];
1855 double segmentStartY = path_positions->
y[index - 1];
1857 double segmentEndX = path_positions->
x[index];
1858 double segmentEndY = path_positions->
y[index];
1860 double segmentDx = segmentEndX - segmentStartX;
1861 double segmentDy = segmentEndY - segmentStartY;
1863 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
1864 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
1870 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
1873 currentDistanceAlongSegment += charWidth;
1874 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
1875 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
1883 segmentStartX = segmentEndX;
1884 segmentStartY = segmentEndY;
1886 if ( index >= path_positions->
nbPoints )
1890 segmentEndX = path_positions->
x[index];
1891 segmentEndY = path_positions->
y[index];
1893 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth );
1899 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.
int createCandidatesAlongLine(QList< LabelPosition *> &lPos, PointSet *mapShape, bool allowOverrun=false)
Generate candidates for line feature.
void setTotalRepeats(int repeats)
Returns the total number of repeating labels associated with this label.
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)...
int maximumPointLabelCandidates() const
Returns the maximum number of point label candidates to generate for features in this layer...
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
Returns the distance from the anchor point to the label.
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.
int createCurvedCandidatesAlongLine(QList< LabelPosition *> &lPos, PointSet *mapShape, bool allowOverrun=false)
Generate curved candidates for line features.
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.
std::unique_ptr< LabelPosition > createCandidatePointOnSurface(PointSet *mapShape)
Creates a single candidate using the "point on sruface" algorithm.
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)
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.
double fixedAngle() const
Angle in degrees of the fixed angle (relevant only if hasFixedAngle() returns true) ...
Label below point, slightly right of center.
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.
QList< LabelPosition * > createCandidates(const GEOSPreparedGeometry *mapBoundary, PointSet *mapShape, RTree< LabelPosition *, double, 2, double > *candidates)
Generic method to generate label candidates for the feature.
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'...
static int reorderPolygon(int nbPoints, std::vector< double > &x, std::vector< double > &y)
Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside...
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...
std::unique_ptr< PointSet > clone() const
Returns a copy of the point set.
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
int maximumLineLabelCandidates() const
Returns the maximum number of line label candidates to generate for features in this layer...
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.
LabelPosition is a candidate feature label position.
double overrunSmoothDistance() const
Returns the distance (in map units) with which the ends of linear features are averaged over when cal...
Label directly above point.
Quadrant
Position of label candidate relative to feature.
const GEOSPreparedGeometry * preparedGeom() const
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...
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
double left() const
Returns the left margin.
bool isCurved() const
Returns true if the layer has curved labels.
double getLabelWidth(double angle=0.0) const
Returns the width of the label, optionally taking an angle into account.
double overrunDistance() const
Returns the permissible distance (in map units) which labels are allowed to overrun the start or end ...
bool showUprightLabels() const
Returns true if feature's label must be displayed upright.
double getLabelHeight(double angle=0.0) const
Returns the height of the label, optionally taking an angle into account.
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 totalRepeats() const
Returns the total number of repeating labels associated with this label.
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...