From 5d06c1d48daf500ce18f7efed2cd15a8190f80cc Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 18 Mar 2021 09:47:52 +1000 Subject: [PATCH] [FEATURE] When the "show pinned labels" option is enabled, also highlight any pinned callout start or end points This allows users to immediately see which callouts points have been manually placed vs are automatically placed. --- .../auto_generated/callouts/qgscallout.sip.in | 8 ++- .../labeling/qgscalloutposition.sip.in | 44 ++++++++++++ src/app/labeling/qgsmaptoolpinlabels.cpp | 52 +++++++++++--- src/app/labeling/qgsmaptoolpinlabels.h | 5 ++ src/core/callouts/qgscallout.cpp | 29 +++++--- src/core/callouts/qgscallout.h | 9 ++- src/core/labeling/qgscalloutposition.h | 43 ++++++++++++ tests/src/core/testqgslabelingengine.cpp | 68 +++++++++++++++---- 8 files changed, 223 insertions(+), 35 deletions(-) diff --git a/python/core/auto_generated/callouts/qgscallout.sip.in b/python/core/auto_generated/callouts/qgscallout.sip.in index b21058bc56b1..5fab9268bcbc 100644 --- a/python/core/auto_generated/callouts/qgscallout.sip.in +++ b/python/core/auto_generated/callouts/qgscallout.sip.in @@ -409,20 +409,24 @@ Returns the anchor point geometry for a label with the given bounding box and `` QGIS 3.20 use :py:func:`~QgsCallout.calloutLabelPoint` instead %End - QgsGeometry calloutLabelPoint( QRectF bodyBoundingBox, double angle, LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCalloutContext &calloutContext ) const; + QgsGeometry calloutLabelPoint( QRectF bodyBoundingBox, double angle, LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const; %Docstring Returns the anchor point geometry for a label with the given bounding box and ``anchor`` point mode. +The ``pinned`` argument will be set to ``True`` if the callout label point is pinned (manually placed). + .. versionadded:: 3.20 %End - QgsGeometry calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext ) const; + QgsGeometry calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const; %Docstring Calculates the direct line from a label geometry to an anchor geometry part, respecting the various callout settings which influence how the callout end should be placed in the anchor geometry. Returns a null geometry if the callout line cannot be calculated. +The ``pinned`` argument will be set to ``True`` if the callout anchor point is pinned (manually placed). + .. versionadded:: 3.20 %End diff --git a/python/core/auto_generated/labeling/qgscalloutposition.sip.in b/python/core/auto_generated/labeling/qgscalloutposition.sip.in index 18ff59897425..ca12469c04d0 100644 --- a/python/core/auto_generated/labeling/qgscalloutposition.sip.in +++ b/python/core/auto_generated/labeling/qgscalloutposition.sip.in @@ -85,6 +85,50 @@ The destination of the callout line is the line point associated with the featur .. seealso:: :py:func:`destination` .. seealso:: :py:func:`setOrigin` +%End + + bool originIsPinned() const; +%Docstring +Returns ``True`` if the origin of the callout has pinned (manually placed). + +The origin of the callout line is the line point associated with the label text. + +.. seealso:: :py:func:`destinationIsPinned` + +.. seealso:: :py:func:`setOriginIsPinned` +%End + + void setOriginIsPinned( bool pinned ); +%Docstring +Sets whether the origin of the callout has pinned (manually placed). + +The origin of the callout line is the line point associated with the label text. + +.. seealso:: :py:func:`setDestinationIsPinned` + +.. seealso:: :py:func:`originIsPinned` +%End + + bool destinationIsPinned() const; +%Docstring +Returns ``True`` if the destination of the callout has pinned (manually placed). + +The destination of the callout line is the line point associated with the feature's geometry. + +.. seealso:: :py:func:`originIsPinned` + +.. seealso:: :py:func:`setDestinationIsPinned` +%End + + void setDestinationIsPinned( bool pinned ); +%Docstring +Sets whether the destination of the callout has pinned (manually placed). + +The destination of the callout line is the line point associated with the feature's geometry. + +.. seealso:: :py:func:`setOriginIsPinned` + +.. seealso:: :py:func:`destinationIsPinned` %End }; diff --git a/src/app/labeling/qgsmaptoolpinlabels.cpp b/src/app/labeling/qgsmaptoolpinlabels.cpp index c35e52a73641..37f1195412c7 100644 --- a/src/app/labeling/qgsmaptoolpinlabels.cpp +++ b/src/app/labeling/qgsmaptoolpinlabels.cpp @@ -154,6 +154,20 @@ void QgsMapToolPinLabels::highlightLabel( const QgsLabelPosition &labelpos, mHighlights.insert( id, rb ); } +void QgsMapToolPinLabels::highlightCallout( bool isOrigin, const QgsCalloutPosition &calloutPosition, const QString &id, const QColor &color ) +{ + double scaleFactor = mCanvas->fontMetrics().xHeight(); + + QgsRubberBand *rb = new QgsRubberBand( mCanvas, QgsWkbTypes::PointGeometry ); + rb->setWidth( 2 ); + rb->setSecondaryStrokeColor( QColor( 255, 255, 255, 100 ) ); + rb->setColor( color ); + rb->setIcon( QgsRubberBand::ICON_X ); + rb->setIconSize( scaleFactor ); + rb->addPoint( isOrigin ? calloutPosition.origin() : calloutPosition.destination() ); + mHighlights.insert( id, rb ); +} + // public slot to render highlight rectangles around pinned labels void QgsMapToolPinLabels::highlightPinnedLabels() { @@ -164,7 +178,7 @@ void QgsMapToolPinLabels::highlightPinnedLabels() return; } - QgsDebugMsg( QStringLiteral( "Highlighting pinned labels" ) ); + QgsDebugMsgLevel( QStringLiteral( "Highlighting pinned labels" ), 2 ); // get list of all drawn labels from all layers within given extent const QgsLabelingResults *labelingResults = mCanvas->labelingResults( false ); @@ -174,16 +188,14 @@ void QgsMapToolPinLabels::highlightPinnedLabels() } QgsRectangle ext = mCanvas->extent(); - QgsDebugMsg( QStringLiteral( "Getting labels from canvas extent" ) ); + QgsDebugMsgLevel( QStringLiteral( "Getting labels from canvas extent" ), 2 ); - QList labelPosList = labelingResults->labelsWithinRect( ext ); + const QList labelPosList = labelingResults->labelsWithinRect( ext ); QApplication::setOverrideCursor( Qt::WaitCursor ); QList::const_iterator it; - for ( it = labelPosList.constBegin() ; it != labelPosList.constEnd(); ++it ) + for ( const QgsLabelPosition &pos : labelPosList ) { - const QgsLabelPosition &pos = *it; - mCurrentLabel = LabelDetails( pos ); if ( isPinned() ) @@ -216,14 +228,38 @@ void QgsMapToolPinLabels::highlightPinnedLabels() highlightLabel( pos, labelStringID, lblcolor ); } } + + // highlight pinned callouts + const QList calloutPosList = labelingResults->calloutsWithinRectangle( ext ); + const QColor calloutColor = QColor( 54, 129, 255, 160 ); + for ( const QgsCalloutPosition &callout : calloutPosList ) + { + if ( callout.originIsPinned() ) + { + QString calloutStringID = QStringLiteral( "callout|%1|%2|origin" ).arg( callout.layerID, QString::number( callout.featureId ) ); + // don't highlight again + if ( mHighlights.contains( calloutStringID ) ) + continue; + + highlightCallout( true, callout, calloutStringID, calloutColor ); + } + if ( callout.destinationIsPinned() ) + { + QString calloutStringID = QStringLiteral( "callout|%1|%2|destination" ).arg( callout.layerID, QString::number( callout.featureId ) ); + // don't highlight again + if ( mHighlights.contains( calloutStringID ) ) + continue; + + highlightCallout( false, callout, calloutStringID, calloutColor ); + } + } QApplication::restoreOverrideCursor(); } void QgsMapToolPinLabels::removePinnedHighlights() { QApplication::setOverrideCursor( Qt::BusyCursor ); - const auto constMHighlights = mHighlights; - for ( QgsRubberBand *rb : constMHighlights ) + for ( QgsRubberBand *rb : qgis::as_const( mHighlights ) ) { delete rb; } diff --git a/src/app/labeling/qgsmaptoolpinlabels.h b/src/app/labeling/qgsmaptoolpinlabels.h index fe633fcb4ed9..3354d67de8b9 100644 --- a/src/app/labeling/qgsmaptoolpinlabels.h +++ b/src/app/labeling/qgsmaptoolpinlabels.h @@ -82,6 +82,11 @@ class APP_EXPORT QgsMapToolPinLabels: public QgsMapToolLabel const QString &id, const QColor &color ); + //! Highlights a given callout relative to whether its pinned and editable + void highlightCallout( bool isOrigin, const QgsCalloutPosition &labelpos, + const QString &id, + const QColor &color ); + //! Select valid labels to pin or unpin void pinUnpinLabels( const QgsRectangle &ext, QMouseEvent *e ); diff --git a/src/core/callouts/qgscallout.cpp b/src/core/callouts/qgscallout.cpp index 662440107e81..021584a5fe25 100644 --- a/src/core/callouts/qgscallout.cpp +++ b/src/core/callouts/qgscallout.cpp @@ -309,8 +309,9 @@ QgsGeometry QgsCallout::labelAnchorGeometry( QRectF rect, const double angle, La return label; } -QgsGeometry QgsCallout::calloutLabelPoint( QRectF rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext ) const +QgsGeometry QgsCallout::calloutLabelPoint( QRectF rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned ) const { + pinned = false; if ( dataDefinedProperties().isActive( QgsCallout::OriginX ) && dataDefinedProperties().isActive( QgsCallout::OriginY ) ) { bool ok = false; @@ -320,6 +321,7 @@ QgsGeometry QgsCallout::calloutLabelPoint( QRectF rect, const double angle, QgsC const double y = dataDefinedProperties().valueAsDouble( QgsCallout::OriginY, context.expressionContext(), 0, &ok ); if ( ok ) { + pinned = true; // data defined label point, use it directly QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) ); try @@ -384,8 +386,9 @@ QgsGeometry QgsCallout::calloutLabelPoint( QRectF rect, const double angle, QgsC return label; } -QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext ) const +QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const { + pinned = false; AnchorPoint anchor = anchorPoint(); const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry; std::unique_ptr< QgsAbstractGeometry > tempPartAnchor; @@ -399,6 +402,7 @@ QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, con const double y = dataDefinedProperties().valueAsDouble( QgsCallout::DestinationY, context.expressionContext(), 0, &ok ); if ( ok ) { + pinned = true; tempPartAnchor = std::make_unique< QgsPoint >( QgsWkbTypes::Point, x, y ); evaluatedPartAnchor = tempPartAnchor.get(); try @@ -603,13 +607,16 @@ void QgsSimpleLineCallout::draw( QgsRenderContext &context, QRectF rect, const d context.expressionContext().setOriginalValueVariable( encodedAnchor ); labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) ); } - const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext ); + + bool originPinned = false; + const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned ); if ( label.isNull() ) return; - auto drawCalloutLine = [this, &context, &calloutContext, &label]( const QgsAbstractGeometry * partAnchor ) + auto drawCalloutLine = [this, &context, &calloutContext, &label, originPinned]( const QgsAbstractGeometry * partAnchor ) { - QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext ); + bool destinationPinned = false; + QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned ); if ( line.isEmpty() ) return; @@ -657,7 +664,9 @@ void QgsSimpleLineCallout::draw( QgsRenderContext &context, QRectF rect, const d QgsCalloutPosition position; position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() ); + position.setOriginIsPinned( originPinned ); position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() ); + position.setDestinationIsPinned( destinationPinned ); calloutContext.addCalloutPosition( position ); mLineSymbol->renderPolyline( points, nullptr, context ); @@ -722,13 +731,15 @@ void QgsManhattanLineCallout::draw( QgsRenderContext &context, QRectF rect, cons context.expressionContext().setOriginalValueVariable( encodedAnchor ); labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) ); } - const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext ); + bool originPinned = false; + const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned ); if ( label.isNull() ) return; - auto drawCalloutLine = [this, &context, &calloutContext, &label]( const QgsAbstractGeometry * partAnchor ) + auto drawCalloutLine = [this, &context, &calloutContext, &label, originPinned]( const QgsAbstractGeometry * partAnchor ) { - QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext ); + bool destinationPinned = false; + QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned ); if ( line.isEmpty() ) return; @@ -779,7 +790,9 @@ void QgsManhattanLineCallout::draw( QgsRenderContext &context, QRectF rect, cons QgsCalloutPosition position; position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() ); + position.setOriginIsPinned( originPinned ); position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() ); + position.setDestinationIsPinned( destinationPinned ); calloutContext.addCalloutPosition( position ); lineSymbol()->renderPolyline( points, nullptr, context ); diff --git a/src/core/callouts/qgscallout.h b/src/core/callouts/qgscallout.h index 9953cd4c1438..bc1e1a5f95aa 100644 --- a/src/core/callouts/qgscallout.h +++ b/src/core/callouts/qgscallout.h @@ -424,9 +424,12 @@ class CORE_EXPORT QgsCallout /** * Returns the anchor point geometry for a label with the given bounding box and \a anchor point mode. + * + * The \a pinned argument will be set to TRUE if the callout label point is pinned (manually placed). + * * \since QGIS 3.20 */ - QgsGeometry calloutLabelPoint( QRectF bodyBoundingBox, double angle, LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCalloutContext &calloutContext ) const; + QgsGeometry calloutLabelPoint( QRectF bodyBoundingBox, double angle, LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const; /** * Calculates the direct line from a label geometry to an anchor geometry part, respecting the various @@ -434,9 +437,11 @@ class CORE_EXPORT QgsCallout * * Returns a null geometry if the callout line cannot be calculated. * + * The \a pinned argument will be set to TRUE if the callout anchor point is pinned (manually placed). + * * \since QGIS 3.20 */ - QgsGeometry calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext ) const; + QgsGeometry calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const; private: diff --git a/src/core/labeling/qgscalloutposition.h b/src/core/labeling/qgscalloutposition.h index 30b34e1cd7a4..4f9fae2c3f52 100644 --- a/src/core/labeling/qgscalloutposition.h +++ b/src/core/labeling/qgscalloutposition.h @@ -103,11 +103,54 @@ class CORE_EXPORT QgsCalloutPosition */ void setDestination( const QPointF &destination ) { mDestination = destination; } + /** + * Returns TRUE if the origin of the callout has pinned (manually placed). + * + * The origin of the callout line is the line point associated with the label text. + * + * \see destinationIsPinned() + * \see setOriginIsPinned() + */ + bool originIsPinned() const { return mOriginIsPinned; } + + /** + * Sets whether the origin of the callout has pinned (manually placed). + * + * The origin of the callout line is the line point associated with the label text. + * + * \see setDestinationIsPinned() + * \see originIsPinned() + */ + void setOriginIsPinned( bool pinned ) { mOriginIsPinned = pinned; } + + /** + * Returns TRUE if the destination of the callout has pinned (manually placed). + * + * The destination of the callout line is the line point associated with the feature's geometry. + * + * \see originIsPinned() + * \see setDestinationIsPinned() + */ + bool destinationIsPinned() const { return mDestinationIsPinned; } + + /** + * Sets whether the destination of the callout has pinned (manually placed). + * + * The destination of the callout line is the line point associated with the feature's geometry. + * + * \see setOriginIsPinned() + * \see destinationIsPinned() + */ + void setDestinationIsPinned( bool pinned ) { mDestinationIsPinned = pinned; } + private: QPointF mOrigin; QPointF mDestination; + + bool mOriginIsPinned = false; + bool mDestinationIsPinned = false; }; #endif // QGSCALLOUTPOSITION_H diff --git a/tests/src/core/testqgslabelingengine.cpp b/tests/src/core/testqgslabelingengine.cpp index b53a6814e629..aee609936972 100644 --- a/tests/src/core/testqgslabelingengine.cpp +++ b/tests/src/core/testqgslabelingengine.cpp @@ -2039,6 +2039,11 @@ void TestQgsLabelingEngine::labelingResultsWithCallouts() f.setAttributes( QgsAttributes() << 2 << -3424024 << 7849709 << -2713442 << 7628322 << -2567040 << 6974872 ); f.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( -2995532, 7242679 ) ) ); QVERIFY( vl3->dataProvider()->addFeature( f ) ); + + f.setAttributes( QgsAttributes() << 3 << -4024024 << 7849709 << QVariant() << QVariant() << QVariant() << QVariant() ); + f.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( -2995532, 7242679 ) ) ); + QVERIFY( vl3->dataProvider()->addFeature( f ) ); + vl3->updateExtents(); vl3->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary! @@ -2081,6 +2086,8 @@ void TestQgsLabelingEngine::labelingResultsWithCallouts() QGSCOMPARENEAR( callouts.at( 0 ).origin().y(), 7628322.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().x(), -2567040.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().y(), 6974872.0, 10 ); + QVERIFY( callouts.at( 0 ).originIsPinned() ); + QVERIFY( callouts.at( 0 ).destinationIsPinned() ); callouts = results->calloutsWithinRectangle( QgsRectangle( -2567340, 6974572, -2566740, 6975172 ) ); QCOMPARE( callouts.count(), 1 ); @@ -2090,6 +2097,8 @@ void TestQgsLabelingEngine::labelingResultsWithCallouts() QGSCOMPARENEAR( callouts.at( 0 ).origin().y(), 7628322.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().x(), -2567040.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().y(), 6974872.0, 10 ); + QVERIFY( callouts.at( 0 ).originIsPinned() ); + QVERIFY( callouts.at( 0 ).destinationIsPinned() ); callouts = results->calloutsWithinRectangle( QgsRectangle( -1242625, 7967227, -1242025, 7967827 ) ); QCOMPARE( callouts.count(), 1 ); @@ -2099,6 +2108,8 @@ void TestQgsLabelingEngine::labelingResultsWithCallouts() QGSCOMPARENEAR( callouts.at( 0 ).origin().y(), 7967527.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().x(), -424572.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().y(), 7567578.0, 10 ); + QVERIFY( callouts.at( 0 ).originIsPinned() ); + QVERIFY( callouts.at( 0 ).destinationIsPinned() ); callouts = results->calloutsWithinRectangle( QgsRectangle( -424872, 7567278, -424272, 7567878 ) ); QCOMPARE( callouts.count(), 1 ); @@ -2108,22 +2119,49 @@ void TestQgsLabelingEngine::labelingResultsWithCallouts() QGSCOMPARENEAR( callouts.at( 0 ).origin().y(), 7967527.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().x(), -424572.0, 10 ); QGSCOMPARENEAR( callouts.at( 0 ).destination().y(), 7567578.0, 10 ); + QVERIFY( callouts.at( 0 ).originIsPinned() ); + QVERIFY( callouts.at( 0 ).destinationIsPinned() ); + + callouts = results->calloutsWithinRectangle( QgsRectangle( -4104024, 7609709, -3804024, 8249709 ) ); + QCOMPARE( callouts.count(), 1 ); + QCOMPARE( callouts.at( 0 ).featureId, 2 ); + QCOMPARE( callouts.at( 0 ).layerID, vl3->id() ); + QGSCOMPARENEAR( callouts.at( 0 ).origin().x(), -3856062.0, 10 ); + QGSCOMPARENEAR( callouts.at( 0 ).origin().y(), 7849709.0, 10 ); + QGSCOMPARENEAR( callouts.at( 0 ).destination().x(), -2995532.0, 10 ); + QGSCOMPARENEAR( callouts.at( 0 ).destination().y(), 7242679.0, 10 ); + QVERIFY( !callouts.at( 0 ).originIsPinned() ); + QVERIFY( !callouts.at( 0 ).destinationIsPinned() ); callouts = results->calloutsWithinRectangle( mapSettings.visibleExtent() ); - QCOMPARE( callouts.count(), 2 ); - bool callout1IsFirstLayer = callouts.at( 0 ).layerID == vl2->id(); - QCOMPARE( callouts.at( callout1IsFirstLayer ? 0 : 1 ).featureId, 1 ); - QCOMPARE( callouts.at( callout1IsFirstLayer ? 0 : 1 ).layerID, vl2->id() ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 0 : 1 ).origin().x(), -1242325.0, 10 ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 0 : 1 ).origin().y(), 7967527.0, 10 ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 0 : 1 ).destination().x(), -424572.0, 10 ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 0 : 1 ).destination().y(), 7567578.0, 10 ); - QCOMPARE( callouts.at( callout1IsFirstLayer ? 1 : 0 ).featureId, 1 ); - QCOMPARE( callouts.at( callout1IsFirstLayer ? 1 : 0 ).layerID, vl3->id() ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 1 : 0 ).origin().x(), -2713442.0, 10 ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 1 : 0 ).origin().y(), 7628322.0, 10 ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 1 : 0 ).destination().x(), -2567040.0, 10 ); - QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 1 : 0 ).destination().y(), 6974872.0, 10 ); + QCOMPARE( callouts.count(), 3 ); + int callout1Index = callouts.at( 0 ).layerID == vl2->id() ? 0 : callouts.at( 1 ).layerID == vl2->id() ? 1 : 2; + int callout2Index = callouts.at( 0 ).layerID == vl3->id() && callouts.at( 0 ).featureId == 1 ? 0 : callouts.at( 1 ).layerID == vl3->id() && callouts.at( 1 ).featureId == 1 ? 1 : 2; + int callout3Index = callouts.at( 0 ).layerID == vl3->id() && callouts.at( 0 ).featureId == 2 ? 0 : callouts.at( 1 ).layerID == vl3->id() && callouts.at( 1 ).featureId == 2 ? 1 : 2; + QCOMPARE( callouts.at( callout1Index ).featureId, 1 ); + QCOMPARE( callouts.at( callout1Index ).layerID, vl2->id() ); + QGSCOMPARENEAR( callouts.at( callout1Index ).origin().x(), -1242325.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout1Index ).origin().y(), 7967527.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout1Index ).destination().x(), -424572.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout1Index ).destination().y(), 7567578.0, 10 ); + QVERIFY( callouts.at( callout1Index ).originIsPinned() ); + QVERIFY( callouts.at( callout1Index ).destinationIsPinned() ); + QCOMPARE( callouts.at( callout2Index ).featureId, 1 ); + QCOMPARE( callouts.at( callout2Index ).layerID, vl3->id() ); + QGSCOMPARENEAR( callouts.at( callout2Index ).origin().x(), -2713442.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout2Index ).origin().y(), 7628322.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout2Index ).destination().x(), -2567040.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout2Index ).destination().y(), 6974872.0, 10 ); + QVERIFY( callouts.at( callout2Index ).originIsPinned() ); + QVERIFY( callouts.at( callout2Index ).destinationIsPinned() ); + QCOMPARE( callouts.at( callout3Index ).featureId, 2 ); + QCOMPARE( callouts.at( callout3Index ).layerID, vl3->id() ); + QGSCOMPARENEAR( callouts.at( callout3Index ).origin().x(), -3856062.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout3Index ).origin().y(), 7849709.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout3Index ).destination().x(), -2995532.0, 10 ); + QGSCOMPARENEAR( callouts.at( callout3Index ).destination().y(), 7242679.0, 10 ); + QVERIFY( !callouts.at( callout3Index ).originIsPinned() ); + QVERIFY( !callouts.at( callout3Index ).destinationIsPinned() ); // with rotation mapSettings.setRotation( 60 ); @@ -2172,7 +2210,7 @@ void TestQgsLabelingEngine::labelingResultsWithCallouts() callouts = results->calloutsWithinRectangle( mapSettings.visibleExtent() ); QCOMPARE( callouts.count(), 2 ); - callout1IsFirstLayer = callouts.at( 0 ).layerID == vl2->id(); + bool callout1IsFirstLayer = callouts.at( 0 ).layerID == vl2->id(); QCOMPARE( callouts.at( callout1IsFirstLayer ? 0 : 1 ).featureId, 1 ); QCOMPARE( callouts.at( callout1IsFirstLayer ? 0 : 1 ).layerID, vl2->id() ); QGSCOMPARENEAR( callouts.at( callout1IsFirstLayer ? 0 : 1 ).origin().x(), -1242325.0, 10 );