37using namespace Qt::StringLiterals;
44 : QgsTextLabelFeature( id, std::move( geometry ), size )
48 QgsTextFormat mFormat;
54 QgsLinearReferencingSymbolLayerLabelProvider()
55 : QgsAbstractLabelProvider( nullptr )
65 ~QgsLinearReferencingSymbolLayerLabelProvider()
override { qDeleteAll( mLabels ); }
67 void addLabel(
const QPointF &painterPoint,
double angleRadians,
const QString &text, QgsRenderContext &context,
const QgsTextFormat &format )
70 QgsPoint mapPoint( painterPoint );
78 auto feature = std::make_unique< QgsTextLabelFeatureWithFormat >( mLabels.size(),
QgsGeos::asGeos( &mapPoint ), QSizeF( size.width() * uPP, size.height() * uPP ), format );
80 feature->setDocument( doc, documentMetrics );
81 feature->setFixedAngle( angleRadians );
82 feature->setHasFixedAngle(
true );
85 feature->setQuadOffset( QPointF( 1, 1 ) );
87 mLabels.append( feature.release() );
90 QList<QgsLabelFeature *>
labelFeatures( QgsRenderContext & )
final {
return mLabels; }
92 void drawLabel( QgsRenderContext &context, pal::LabelPosition *label )
const final
99 QgsTextLabelFeatureWithFormat *lf = qgis::down_cast<QgsTextLabelFeatureWithFormat *>( label->getFeaturePart()->feature() );
104 QList<QgsLabelFeature *> mLabels;
111 mNumericFormat = std::make_unique< QgsBasicNumericFormat >();
118 auto res = std::make_unique< QgsLinearReferencingSymbolLayer >();
125 const double skipMultiples =
properties.value( u
"skip_multiples"_s ).toDouble( &ok );
127 res->setSkipMultiplesOf( skipMultiples );
128 res->setRotateLabels(
properties.value( u
"rotate"_s,
true ).toBool() );
129 res->setShowMarker(
properties.value( u
"show_marker"_s,
false ).toBool() );
136 const QString textFormatXml =
properties.value( u
"text_format"_s ).toString();
137 if ( !textFormatXml.isEmpty() )
141 doc.setContent( textFormatXml );
142 elem = doc.documentElement();
149 const QString numericFormatXml =
properties.value( u
"numeric_format"_s ).toString();
150 if ( !numericFormatXml.isEmpty() )
153 doc.setContent( numericFormatXml );
157 if (
properties.contains( u
"label_offset"_s ) )
161 if (
properties.contains( u
"label_offset_unit"_s ) )
165 if (
properties.contains( ( u
"label_offset_map_unit_scale"_s ) ) )
169 if (
properties.contains( u
"average_angle_length"_s ) )
171 res->setAverageAngleLength(
properties[u
"average_angle_length"_s].toDouble() );
173 if (
properties.contains( u
"average_angle_unit"_s ) )
177 if (
properties.contains( ( u
"average_angle_map_unit_scale"_s ) ) )
182 return res.release();
187 auto res = std::make_unique< QgsLinearReferencingSymbolLayer >();
188 res->setPlacement( mPlacement );
189 res->setLabelSource( mLabelSource );
190 res->setInterval( mInterval );
191 res->setSkipMultiplesOf( mSkipMultiplesOf );
192 res->setRotateLabels( mRotateLabels );
193 res->setLabelOffset( mLabelOffset );
194 res->setLabelOffsetUnit( mLabelOffsetUnit );
195 res->setLabelOffsetMapUnitScale( mLabelOffsetMapUnitScale );
196 res->setShowMarker( mShowMarker );
197 res->setAverageAngleLength( mAverageAngleLength );
198 res->setAverageAngleUnit( mAverageAngleLengthUnit );
199 res->setAverageAngleMapUnitScale( mAverageAngleLengthMapUnitScale );
201 res->mTextFormat = mTextFormat;
202 res->mMarkerSymbol.reset( mMarkerSymbol ? mMarkerSymbol->clone() :
nullptr );
203 if ( mNumericFormat )
204 res->mNumericFormat.reset( mNumericFormat->clone() );
208 return res.release();
213 QDomDocument textFormatDoc;
218 const QDomElement textElem = mTextFormat.writeXml( textFormatDoc, rwContext );
219 textFormatDoc.appendChild( textElem );
221 QDomDocument numericFormatDoc;
222 QDomElement numericFormatElem = numericFormatDoc.createElement( u
"numericFormat"_s );
223 mNumericFormat->writeXml( numericFormatElem, numericFormatDoc, rwContext );
224 numericFormatDoc.appendChild( numericFormatElem );
229 { u
"interval"_s, mInterval },
230 { u
"rotate"_s, mRotateLabels },
231 { u
"show_marker"_s, mShowMarker },
232 { u
"text_format"_s, textFormatDoc.toString() },
233 { u
"numeric_format"_s, numericFormatDoc.toString() },
237 { u
"average_angle_length"_s, mAverageAngleLength },
242 if ( mSkipMultiplesOf >= 0 )
244 res.insert( u
"skip_multiples"_s, mSkipMultiplesOf );
252 return u
"LinearReferencing"_s;
262 return mShowMarker ? mMarkerSymbol.get() :
nullptr;
269 mMarkerSymbol.reset( qgis::down_cast<QgsMarkerSymbol *>( symbol ) );
283 mMarkerSymbol->setRenderHints( hints );
292 QgsLinearReferencingSymbolLayerLabelProvider *provider =
new QgsLinearReferencingSymbolLayerLabelProvider();
293 mLabelProviderId = labelingEngine->addProvider( provider );
305void QgsLinearReferencingSymbolLayer::renderGeometryPart(
306 QgsSymbolRenderContext &context,
const QgsAbstractGeometry *geometry,
double labelOffsetPainterUnitsX,
double labelOffsetPainterUnitsY,
double skipMultiples,
double averageAngleDistancePainterUnits,
bool showMarker
311 renderLineString( context, line, labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits,
showMarker );
316 for (
int i = 0; i < polygon->numInteriorRings(); ++i )
323void QgsLinearReferencingSymbolLayer::renderLineString(
324 QgsSymbolRenderContext &context,
const QgsLineString *line,
double labelOffsetPainterUnitsX,
double labelOffsetPainterUnitsY,
double skipMultiples,
double averageAngleDistancePainterUnits,
bool showMarker
330 switch ( mPlacement )
335 renderPolylineInterval( line, context, skipMultiples, QPointF( labelOffsetPainterUnitsX, labelOffsetPainterUnitsY ), averageAngleDistancePainterUnits,
showMarker );
339 renderPolylineVertex( line, context, skipMultiples, QPointF( labelOffsetPainterUnitsX, labelOffsetPainterUnitsY ), averageAngleDistancePainterUnits,
showMarker );
359 double skipMultiples = mSkipMultiplesOf;
366 double labelOffsetX = mLabelOffset.x();
367 double labelOffsetY = mLabelOffset.y();
369 double averageOver = mAverageAngleLength;
385 const double averageAngleDistancePainterUnits = context.
renderContext().
convertToPainterUnits( averageOver, mAverageAngleLengthUnit, mAverageAngleLengthMapUnitScale ) / 2;
389 renderGeometryPart( context, *partIt, labelOffsetPainterUnitsX, labelOffsetPainterUnitsY, skipMultiples, averageAngleDistancePainterUnits,
showMarker );
395 double targetPointDistanceAlongSegment,
396 double segmentLengthPainterUnits,
397 double averageAngleLengthPainterUnits,
398 double prevXPainterUnits,
399 double prevYPainterUnits,
400 double thisXPainterUnits,
401 double thisYPainterUnits,
402 const double *xPainterUnits,
403 const double *yPainterUnits,
409 double painterDistRemaining = averageAngleLengthPainterUnits + targetPointDistanceAlongSegment;
410 double startAverageSegmentX = prevXPainterUnits;
411 double startAverageSegmentY = prevYPainterUnits;
412 double endAverageSegmentX = thisXPainterUnits;
413 double endAverageSegmentY = thisYPainterUnits;
414 double averagingSegmentLengthPainterUnits = segmentLengthPainterUnits;
415 const double *xAveragingData = xPainterUnits;
416 const double *yAveragingData = yPainterUnits;
419 while ( painterDistRemaining > averagingSegmentLengthPainterUnits )
421 if ( j >= totalPoints - 1 )
424 painterDistRemaining -= averagingSegmentLengthPainterUnits;
425 startAverageSegmentX = endAverageSegmentX;
426 startAverageSegmentY = endAverageSegmentY;
428 endAverageSegmentX = *xAveragingData++;
429 endAverageSegmentY = *yAveragingData++;
431 averagingSegmentLengthPainterUnits =
QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
434 double endAverageXPainterUnits;
435 double endAverageYPainterUnits;
436 if ( painterDistRemaining < averagingSegmentLengthPainterUnits )
439 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, endAverageXPainterUnits, endAverageYPainterUnits,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr );
443 endAverageXPainterUnits = endAverageSegmentX;
444 endAverageYPainterUnits = endAverageSegmentY;
449 painterDistRemaining = ( segmentLengthPainterUnits - targetPointDistanceAlongSegment ) + averageAngleLengthPainterUnits;
450 startAverageSegmentX = thisXPainterUnits;
451 startAverageSegmentY = thisYPainterUnits;
452 endAverageSegmentX = prevXPainterUnits;
453 endAverageSegmentY = prevYPainterUnits;
454 averagingSegmentLengthPainterUnits = segmentLengthPainterUnits;
455 xAveragingData = xPainterUnits - 2;
456 yAveragingData = yPainterUnits - 2;
457 while ( painterDistRemaining > averagingSegmentLengthPainterUnits )
462 painterDistRemaining -= averagingSegmentLengthPainterUnits;
463 startAverageSegmentX = endAverageSegmentX;
464 startAverageSegmentY = endAverageSegmentY;
466 endAverageSegmentX = *xAveragingData--;
467 endAverageSegmentY = *yAveragingData--;
469 averagingSegmentLengthPainterUnits =
QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
472 double startAverageXPainterUnits;
473 double startAverageYPainterUnits;
474 if ( painterDistRemaining < averagingSegmentLengthPainterUnits )
477 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, startAverageXPainterUnits, startAverageYPainterUnits,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr );
481 startAverageXPainterUnits = endAverageSegmentX;
482 startAverageYPainterUnits = endAverageSegmentY;
485 double calculatedAngle = std::fmod(
QgsGeometryUtilsBase::azimuth( startAverageXPainterUnits, startAverageYPainterUnits, endAverageXPainterUnits, endAverageYPainterUnits ) + 360, 360 );
486 if ( calculatedAngle > 90 && calculatedAngle < 270 )
487 calculatedAngle += 180;
489 return calculatedAngle;
492typedef std::function<bool(
double x,
double y,
double z,
double m,
double distanceFromStart,
double angle )>
VisitPointFunction;
502 double distanceTraversed = 0;
503 const int totalPoints = line->
numPoints();
504 if ( totalPoints == 0 )
507 const double *x = line->
xData();
508 const double *y = line->
yData();
509 const double *z = line->
is3D() ? line->
zData() :
nullptr;
512 const double *xPainterUnits = linePainterUnits->
xData();
513 const double *yPainterUnits = linePainterUnits->
yData();
517 double prevZ = z ? *z++ : 0.0;
518 double prevM = m ? *m++ : 0.0;
520 double prevXPainterUnits = *xPainterUnits++;
521 double prevYPainterUnits = *yPainterUnits++;
525 visitPoint( prevX, prevY, prevZ, prevM, 0, 0 );
529 double pZ = std::numeric_limits<double>::quiet_NaN();
530 double pM = std::numeric_limits<double>::quiet_NaN();
531 double nextPointDistance = emitFirstPoint ? 0 : distance;
532 for (
int i = 1; i < totalPoints; ++i )
536 double thisZ = z ? *z++ : 0.0;
537 double thisM = m ? *m++ : 0.0;
538 double thisXPainterUnits = *xPainterUnits++;
539 double thisYPainterUnits = *yPainterUnits++;
541 double angle = std::fmod(
QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 );
542 if ( angle > 90 && angle < 270 )
548 while ( nextPointDistance < distanceTraversed + segmentLength ||
qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
551 const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
554 pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY, z ? &prevZ :
nullptr, z ? &thisZ :
nullptr, z ? &pZ :
nullptr, m ? &prevM :
nullptr, m ? &thisM :
nullptr, m ? &pM :
nullptr );
556 double calculatedAngle = angle;
557 if ( averageAngleLengthPainterUnits > 0 )
559 const double targetPointFractionAlongSegment = distanceToPoint / segmentLength;
560 const double targetPointDistanceAlongSegment = targetPointFractionAlongSegment * segmentLengthPainterUnits;
563 =
calculateAveragedAngle( targetPointDistanceAlongSegment, segmentLengthPainterUnits, averageAngleLengthPainterUnits, prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits, xPainterUnits, yPainterUnits, totalPoints, i );
566 if ( !visitPoint( pX, pY, pZ, pM, nextPointDistance, calculatedAngle ) )
569 nextPointDistance += distance;
572 distanceTraversed += segmentLength;
577 prevXPainterUnits = thisXPainterUnits;
578 prevYPainterUnits = thisYPainterUnits;
584 return a + ( b - a ) * fraction;
595 double distanceTraversed = 0;
596 const int totalPoints = line->
numPoints();
597 if ( totalPoints < 2 )
600 const double *x = line->
xData();
601 const double *y = line->
yData();
602 const double *z = line->
is3D() ? line->
zData() :
nullptr;
605 const double *xPainterUnits = linePainterUnits->
xData();
606 const double *yPainterUnits = linePainterUnits->
yData();
610 double prevZ = z ? *z++ : 0.0;
611 double prevM = m ? *m++ : 0.0;
613 double prevXPainterUnits = *xPainterUnits++;
614 double prevYPainterUnits = *yPainterUnits++;
618 visitPoint( prevX, prevY, prevZ, prevM, 0, 0 );
622 double prevValue = useZ ? prevZ : prevM;
623 bool isFirstPoint =
true;
624 for (
int i = 1; i < totalPoints; ++i )
628 double thisZ = z ? *z++ : 0.0;
629 double thisM = m ? *m++ : 0.0;
630 const double thisValue = useZ ? thisZ : thisM;
631 double thisXPainterUnits = *xPainterUnits++;
632 double thisYPainterUnits = *yPainterUnits++;
634 double angle = std::fmod(
QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 );
635 if ( angle > 90 && angle < 270 )
642 const int direction = ( thisValue > prevValue ) ? 1 : ( thisValue < prevValue ) ? -1 : 0;
643 if ( direction != 0 )
646 double nextStepValue = direction > 0 ? std::ceil( prevValue / step ) * step : std::floor( prevValue / step ) * step;
648 while ( ( direction > 0 && ( nextStepValue <= thisValue ||
qgsDoubleNear( nextStepValue, thisValue ) ) )
649 || ( direction < 0 && ( nextStepValue >= thisValue ||
qgsDoubleNear( nextStepValue, thisValue ) ) ) )
651 const double targetPointFractionAlongSegment = ( nextStepValue - prevValue ) / ( thisValue - prevValue );
652 const double targetPointDistanceAlongSegment = targetPointFractionAlongSegment * segmentLengthPainterUnits;
657 const double pZ = useZ ? nextStepValue :
interpolateValue( prevZ, thisZ, targetPointFractionAlongSegment );
658 const double pM = useZ ?
interpolateValue( prevM, thisM, targetPointFractionAlongSegment ) : nextStepValue;
660 double calculatedAngle = angle;
661 if ( averageAngleLengthPainterUnits > 0 )
664 =
calculateAveragedAngle( targetPointDistanceAlongSegment, segmentLengthPainterUnits, averageAngleLengthPainterUnits, prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits, xPainterUnits, yPainterUnits, totalPoints, i );
667 if ( !
qgsDoubleNear( targetPointFractionAlongSegment, 0 ) || isFirstPoint )
669 if ( !visitPoint( pX, pY, pZ, pM, distanceTraversed + segmentLength * targetPointFractionAlongSegment, calculatedAngle ) )
673 nextStepValue += direction * step;
676 else if ( isFirstPoint && emitFirstPoint )
678 if ( !visitPoint( prevX, prevY, prevZ, prevM, distanceTraversed, std::fmod(
QgsGeometryUtilsBase::azimuth( prevXPainterUnits, prevYPainterUnits, thisXPainterUnits, thisYPainterUnits ) + 360, 360 ) ) )
681 isFirstPoint =
false;
687 prevXPainterUnits = thisXPainterUnits;
688 prevYPainterUnits = thisYPainterUnits;
689 prevValue = thisValue;
690 distanceTraversed += segmentLength;
705 visitPointsByInterpolatedZM( line, linePainterUnits, emitFirstPoint, distance, averageAngleLengthPainterUnits, visitPoint,
false );
708QPointF QgsLinearReferencingSymbolLayer::pointToPainter(
QgsSymbolRenderContext &context,
double x,
double y,
double z )
714 pt = QPointF( x, y );
718 pt = QPointF( x, y );
725void QgsLinearReferencingSymbolLayer::renderPolylineInterval(
726 const QgsLineString *line,
QgsSymbolRenderContext &context,
double skipMultiples,
const QPointF &labelOffsetPainterUnits,
double averageAngleLengthPainterUnits,
bool showMarker
729 double distance = mInterval;
736 QgsNumericFormatContext numericContext;
739 std::unique_ptr< QgsLineString > painterUnitsGeometry( line->
clone() );
746 const bool hasZ = line->
is3D();
752 switch ( mPlacement )
770 QgsLinearReferencingSymbolLayerLabelProvider *labelProvider =
nullptr;
775 labelProvider = qgis::down_cast< QgsLinearReferencingSymbolLayerLabelProvider * >( labelingEngine->providerById( mLabelProviderId ) );
780 painterUnitsGeometry.get(),
783 averageAngleLengthPainterUnits,
784 [&context, &numericContext, skipMultiples,
showMarker, labelOffsetPainterUnits, hasZ, hasM, labelProvider,
this](
double x,
double y,
double z,
double m,
double distanceFromStart,
double angle ) ->
bool {
785 if ( context.renderContext().renderingStopped() )
788 double labelValue = 0;
789 bool labelVertex = true;
790 switch ( mLabelSource )
792 case Qgis::LinearReferencingLabelSource::CartesianDistance2D:
793 labelValue = distanceFromStart;
795 case Qgis::LinearReferencingLabelSource::Z:
797 labelVertex = hasZ && !std::isnan( labelValue );
799 case Qgis::LinearReferencingLabelSource::M:
801 labelVertex = hasM && !std::isnan( labelValue );
808 if ( skipMultiples > 0 &&
qgsDoubleNear( std::fmod( labelValue, skipMultiples ), 0 ) )
811 const QPointF pt = pointToPainter( context, x, y, z );
816 mMarkerSymbol->setLineAngle( 90 - angle );
820 const double angleRadians = ( mRotateLabels ?
angle : 0 ) * M_PI / 180.0;
821 const double dx = labelOffsetPainterUnits.x() * std::sin( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::sin( angleRadians );
822 const double dy = labelOffsetPainterUnits.x() * std::cos( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::cos( angleRadians );
824 const QString text = mNumericFormat->formatDouble( labelValue, numericContext );
825 if ( !labelProvider )
833 labelProvider->addLabel( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, text, context.
renderContext(), mTextFormat );
841void QgsLinearReferencingSymbolLayer::renderPolylineVertex(
842 const QgsLineString *line,
QgsSymbolRenderContext &context,
double skipMultiples,
const QPointF &labelOffsetPainterUnits,
double averageAngleLengthPainterUnits,
bool showMarker
848 averageAngleLengthPainterUnits = std::max( averageAngleLengthPainterUnits, 0.1 );
850 QgsNumericFormatContext numericContext;
853 QgsLinearReferencingSymbolLayerLabelProvider *labelProvider =
nullptr;
858 labelProvider = qgis::down_cast< QgsLinearReferencingSymbolLayerLabelProvider * >( labelingEngine->providerById( mLabelProviderId ) );
861 const double *xData = line->
xData();
862 const double *yData = line->
yData();
863 const double *zData = line->
is3D() ? line->
zData() :
nullptr;
864 const double *mData = line->
isMeasure() ? line->
mData() :
nullptr;
869 std::unique_ptr< QgsLineString > painterUnitsGeometry( line->
clone() );
875 const double *xPainterUnits = painterUnitsGeometry->xData();
876 const double *yPainterUnits = painterUnitsGeometry->yData();
878 double currentDistance = 0;
879 double prevX = *xData;
880 double prevY = *yData;
882 for (
int i = 0; i < size; ++i )
887 double thisX = *xData++;
888 double thisY = *yData++;
889 double thisZ = zData ? *zData++ : 0;
890 double thisM = mData ? *mData++ : 0;
891 double thisXPainterUnits = *xPainterUnits++;
892 double thisYPainterUnits = *yPainterUnits++;
895 currentDistance += thisSegmentLength;
897 if ( skipMultiples > 0 &&
qgsDoubleNear( std::fmod( currentDistance, skipMultiples ), 0 ) )
904 const QPointF pt = pointToPainter( context, thisX, thisY, thisZ );
907 double painterDistRemaining = averageAngleLengthPainterUnits;
908 double startAverageSegmentX = thisXPainterUnits;
909 double startAverageSegmentY = thisYPainterUnits;
911 const double *xAveragingData = xPainterUnits;
912 const double *yAveragingData = yPainterUnits;
913 double endAverageSegmentX = *xAveragingData;
914 double endAverageSegmentY = *yAveragingData;
915 double averagingSegmentLengthPainterUnits =
QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
918 while ( ( j < size - 1 ) && ( painterDistRemaining > averagingSegmentLengthPainterUnits ) )
920 painterDistRemaining -= averagingSegmentLengthPainterUnits;
921 startAverageSegmentX = endAverageSegmentX;
922 startAverageSegmentY = endAverageSegmentY;
924 endAverageSegmentX = *xAveragingData++;
925 endAverageSegmentY = *yAveragingData++;
927 averagingSegmentLengthPainterUnits =
QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
930 double endAverageXPainterUnits = thisXPainterUnits;
931 double endAverageYPainterUnits = thisYPainterUnits;
932 if ( ( j < size - 1 ) && painterDistRemaining < averagingSegmentLengthPainterUnits )
935 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, endAverageXPainterUnits, endAverageYPainterUnits,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr );
937 else if ( i < size - 2 )
939 endAverageXPainterUnits = endAverageSegmentX;
940 endAverageYPainterUnits = endAverageSegmentY;
945 painterDistRemaining = averageAngleLengthPainterUnits;
946 startAverageSegmentX = thisXPainterUnits;
947 startAverageSegmentY = thisYPainterUnits;
949 xAveragingData = xPainterUnits - 2;
950 yAveragingData = yPainterUnits - 2;
952 endAverageSegmentX = *xAveragingData;
953 endAverageSegmentY = *yAveragingData;
954 averagingSegmentLengthPainterUnits =
QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
956 while ( j > 0 && painterDistRemaining > averagingSegmentLengthPainterUnits )
958 painterDistRemaining -= averagingSegmentLengthPainterUnits;
959 startAverageSegmentX = endAverageSegmentX;
960 startAverageSegmentY = endAverageSegmentY;
962 endAverageSegmentX = *xAveragingData--;
963 endAverageSegmentY = *yAveragingData--;
965 averagingSegmentLengthPainterUnits =
QgsGeometryUtilsBase::distance2D( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY );
968 double startAverageXPainterUnits = thisXPainterUnits;
969 double startAverageYPainterUnits = thisYPainterUnits;
970 if ( j > 0 && painterDistRemaining < averagingSegmentLengthPainterUnits )
973 pointOnLineWithDistance( startAverageSegmentX, startAverageSegmentY, endAverageSegmentX, endAverageSegmentY, painterDistRemaining, startAverageXPainterUnits, startAverageYPainterUnits,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr );
977 startAverageXPainterUnits = endAverageSegmentX;
978 startAverageYPainterUnits = endAverageSegmentY;
981 double calculatedAngle = std::fmod(
QgsGeometryUtilsBase::azimuth( startAverageXPainterUnits, startAverageYPainterUnits, endAverageXPainterUnits, endAverageYPainterUnits ) + 360, 360 );
983 if ( calculatedAngle > 90 && calculatedAngle < 270 )
984 calculatedAngle += 180;
989 mMarkerSymbol->setLineAngle( 90 - calculatedAngle );
993 const double angleRadians = mRotateLabels ? ( calculatedAngle * M_PI / 180.0 ) : 0;
994 const double dx = labelOffsetPainterUnits.x() * std::sin( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::sin( angleRadians );
995 const double dy = labelOffsetPainterUnits.x() * std::cos( angleRadians + M_PI_2 ) + labelOffsetPainterUnits.y() * std::cos( angleRadians );
997 double labelValue = 0;
998 bool labelVertex =
true;
999 switch ( mLabelSource )
1002 labelValue = currentDistance;
1006 labelVertex =
static_cast< bool >( zData ) && !std::isnan( labelValue );
1010 labelVertex =
static_cast< bool >( mData ) && !std::isnan( labelValue );
1017 const QString text = mNumericFormat->formatDouble( labelValue, numericContext );
1018 if ( !labelProvider )
1026 labelProvider->addLabel( QPointF( pt.x() + dx, pt.y() + dy ), angleRadians, text, context.
renderContext(), mTextFormat );
1040 mTextFormat = format;
1045 return mNumericFormat.get();
1050 mNumericFormat.reset( format );
1065 return mSkipMultiplesOf;
1081 if ( show && !mMarkerSymbol )
1099 return mLabelSource;
1104 mLabelSource = source;
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ DisableFeatureClipping
If present, indicates that features should never be clipped to the map extent during rendering.
@ AffectsLabeling
If present, indicates that the symbol layer will participate in the map labeling problem.
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
@ Point
Text at point of origin layout mode.
@ Horizontal
Horizontally oriented text.
LinearReferencingPlacement
Defines how/where the labels should be placed in a linear referencing symbol layer.
@ IntervalZ
Place labels at regular intervals, linearly interpolated using Z values.
@ Vertex
Place labels on every vertex in the line.
@ IntervalM
Place labels at regular intervals, linearly interpolated using M values.
@ IntervalCartesian2D
Place labels at regular intervals, using Cartesian distance calculations on a 2D plane.
LinearReferencingLabelSource
Defines what quantity to use for the labels shown in a linear referencing symbol layer.
@ CartesianDistance2D
Distance along line, calculated using Cartesian calculations on a 2D plane.
QFlags< SymbolRenderHint > SymbolRenderHints
Symbol render hints.
Abstract base class for all geometries.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary const part after the last part of the geometry.
const_part_iterator const_parts_begin() const
Returns STL-style iterator pointing to the const first part of the geometry.
An abstract interface class for label providers.
virtual QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context)=0
Returns list of label features (they are owned by the provider and thus deleted on its destruction).
virtual void drawLabel(QgsRenderContext &context, pal::LabelPosition *label) const =0
Draw this label at the position determined by the labeling engine.
static QgsNumericFormatRegistry * numericFormatRegistry()
Gets the registry of available numeric formats.
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double azimuth(double x1, double y1, double x2, double y2)
Calculates Cartesian azimuth between points (x1, y1) and (x2, y2) (clockwise in degree,...
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlags())
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Provides map labeling functionality.
Line string geometry type, with support for z-dimension and m-values.
const double * yData() const
Returns a const pointer to the y vertex data.
const double * xData() const
Returns a const pointer to the x vertex data.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
int numPoints() const override
Returns the number of points in the curve.
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
QgsLineSymbolLayer(const QgsLineSymbolLayer &other)=delete
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setShowMarker(bool show)
Sets whether a marker symbol should be shown corresponding to the labeled point on line.
void setSkipMultiplesOf(double multiple)
Sets the multiple distance to skip labels for.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
QgsTextFormat textFormat() const
Returns the text format used to render the layer.
QString layerType() const override
Returns a string that represents this layer type.
bool showMarker() const
Returns true if a marker symbol should be shown corresponding to the labeled point on line.
void setNumericFormat(QgsNumericFormat *format)
Sets the numeric format used to format the labels for the layer.
QgsNumericFormat * numericFormat() const
Returns the numeric format used to format the labels for the layer.
void setInterval(double interval)
Sets the interval between labels.
~QgsLinearReferencingSymbolLayer() override
void setLabelSource(Qgis::LinearReferencingLabelSource source)
Sets the label source, which dictates what quantity to use for the labels shown.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void setTextFormat(const QgsTextFormat &format)
Sets the text format used to render the layer.
QgsLinearReferencingSymbolLayer()
Qgis::LinearReferencingLabelSource labelSource() const
Returns the label source, which dictates what quantity to use for the labels shown.
double skipMultiplesOf() const
Returns the multiple distance to skip labels for.
QgsLinearReferencingSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setPlacement(Qgis::LinearReferencingPlacement placement)
Sets the placement mode for the labels.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsLinearReferencingSymbolLayer, using the specified properties.
double interval() const
Returns the interval between labels.
Qgis::LinearReferencingPlacement placement() const
Returns the placement mode for the labels.
void setMapRotation(double degrees, double cx, double cy)
Sets map rotation in degrees (clockwise).
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
void transformInPlace(double &x, double &y) const
Transforms map coordinates to device coordinates.
static std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context to use when evaluating QgsExpressions.
QPointF toQPointF() const
Converts a point to a QPointF.
A container for the context for various read/write operations on objects.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr).
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
const QgsAbstractGeometry * geometry() const
Returns pointer to the unsegmentized geometry.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
void copyCommonProperties(QgsSymbolLayer *destLayer) const
Copies all common base class properties from this layer to another symbol layer.
@ SkipMultiples
Skip multiples of.
@ ShowMarker
Show markers.
@ AverageAngleLength
Length to average symbol angles over.
@ Interval
Line marker interval.
QgsPropertyCollection mDataDefinedProperties
QgsSymbolLayer(const QgsSymbolLayer &other)
Encapsulates the context in which a symbol is being rendered.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Qgis::SymbolType type() const
Returns the symbol's type.
static QgsTextDocumentMetrics calculateMetrics(const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor=1.0, const QgsTextDocumentRenderContext &documentContext=QgsTextDocumentRenderContext())
Returns precalculated text metrics for a text document, when rendered using the given base format and...
static QgsTextDocument fromTextAndFormat(const QStringList &lines, const QgsTextFormat &format)
Constructor for QgsTextDocument consisting of a set of lines, respecting settings from a text format.
Container for all settings relating to text rendering.
Adds extra information to QgsLabelFeature for text labels.
static void drawDocument(const QRectF &rect, const QgsTextFormat &format, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, QgsRenderContext &context, Qgis::TextHorizontalAlignment horizontalAlignment=Qgis::TextHorizontalAlignment::Left, Qgis::TextVerticalAlignment verticalAlignment=Qgis::TextVerticalAlignment::Top, double rotation=0, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags())
Draws a text document within a rectangle using the specified settings.
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
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).
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
T qgsgeometry_cast(QgsAbstractGeometry *geom)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
std::function< bool(double x, double y, double z, double m, double distanceFromStart, double angle)> VisitPointFunction
void visitPointsByInterpolatedM(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint)
void visitPointsByRegularDistance(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint)
double calculateAveragedAngle(double targetPointDistanceAlongSegment, double segmentLengthPainterUnits, double averageAngleLengthPainterUnits, double prevXPainterUnits, double prevYPainterUnits, double thisXPainterUnits, double thisYPainterUnits, const double *xPainterUnits, const double *yPainterUnits, int totalPoints, int i)
std::function< void(const QgsLineString *, const QgsLineString *, bool, double, double, const VisitPointFunction &) > VisitPointAtDistanceFunction
void visitPointsByInterpolatedZM(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double step, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint, bool useZ)
void visitPointsByInterpolatedZ(const QgsLineString *line, const QgsLineString *linePainterUnits, bool emitFirstPoint, const double distance, const double averageAngleLengthPainterUnits, const VisitPointFunction &visitPoint)
double interpolateValue(double a, double b, double fraction)