Skip to content

Commit 1bc716f

Browse files
committedJun 3, 2019
[labeling] Fix incorrect bounding box of labels used when
map is rotated Fixes #24680
1 parent 004b7d8 commit 1bc716f

File tree

6 files changed

+118
-52
lines changed

6 files changed

+118
-52
lines changed
 

‎python/core/auto_generated/qgspallabeling.sip.in

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,39 @@ class QgsLabelPosition
1919
#include "qgspallabeling.h"
2020
%End
2121
public:
22-
QgsLabelPosition( QgsFeatureId id, double r, const QVector< QgsPointXY > &corners, const QgsRectangle &rect, double w, double h, const QString &layer, const QString &labeltext, const QFont &labelfont, bool upside_down, bool diagram = false, bool pinned = false, const QString &providerId = QString() );
22+
QgsLabelPosition( QgsFeatureId id, double r, const QVector< QgsPointXY > &corners, const QgsRectangle &rect, double w, double h, const QString &layer, const QString &labeltext, const QFont &labelfont, bool upside_down, bool diagram = false, bool pinned = false, const QString &providerId = QString(),
23+
const QgsGeometry &labelGeometry = QgsGeometry() );
2324

2425
QgsLabelPosition();
2526
%Docstring
2627
Constructor for QgsLabelPosition
2728
%End
2829

2930
QgsFeatureId featureId;
31+
3032
double rotation;
33+
3134
QVector< QgsPointXY > cornerPoints;
3235
QgsRectangle labelRect;
36+
37+
QgsGeometry labelGeometry;
38+
3339
double width;
40+
3441
double height;
42+
3543
QString layerID;
44+
3645
QString labelText;
46+
3747
QFont labelFont;
48+
3849
bool upsideDown;
50+
3951
bool isDiagram;
52+
4053
bool isPinned;
54+
4155
QString providerID;
4256
};
4357

‎src/app/qgsmaptoollabel.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,12 @@ void QgsMapToolLabel::createRubberBands()
6868
delete mFeatureRubberBand;
6969

7070
//label rubber band
71-
QgsRectangle rect = mCurrentLabel.pos.labelRect;
7271
mLabelRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::LineGeometry );
73-
mLabelRubberBand->addPoint( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) );
74-
mLabelRubberBand->addPoint( QgsPointXY( rect.xMinimum(), rect.yMaximum() ) );
75-
mLabelRubberBand->addPoint( QgsPointXY( rect.xMaximum(), rect.yMaximum() ) );
76-
mLabelRubberBand->addPoint( QgsPointXY( rect.xMaximum(), rect.yMinimum() ) );
77-
mLabelRubberBand->addPoint( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) );
72+
mLabelRubberBand->addPoint( mCurrentLabel.pos.cornerPoints.at( 0 ) );
73+
mLabelRubberBand->addPoint( mCurrentLabel.pos.cornerPoints.at( 1 ) );
74+
mLabelRubberBand->addPoint( mCurrentLabel.pos.cornerPoints.at( 2 ) );
75+
mLabelRubberBand->addPoint( mCurrentLabel.pos.cornerPoints.at( 3 ) );
76+
mLabelRubberBand->addPoint( mCurrentLabel.pos.cornerPoints.at( 0 ) );
7877
mLabelRubberBand->setColor( QColor( 0, 255, 0, 65 ) );
7978
mLabelRubberBand->setWidth( 3 );
8079
mLabelRubberBand->show();

‎src/app/qgsmaptoolpinlabels.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,12 @@ void QgsMapToolPinLabels::highlightLabel( const QgsLabelPosition &labelpos,
140140
const QString &id,
141141
const QColor &color )
142142
{
143-
QgsRectangle rect = labelpos.labelRect;
144143
QgsRubberBand *rb = new QgsRubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
145-
rb->addPoint( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) );
146-
rb->addPoint( QgsPointXY( rect.xMinimum(), rect.yMaximum() ) );
147-
rb->addPoint( QgsPointXY( rect.xMaximum(), rect.yMaximum() ) );
148-
rb->addPoint( QgsPointXY( rect.xMaximum(), rect.yMinimum() ) );
149-
rb->addPoint( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) );
144+
rb->addPoint( labelpos.cornerPoints.at( 0 ) );
145+
rb->addPoint( labelpos.cornerPoints.at( 1 ) );
146+
rb->addPoint( labelpos.cornerPoints.at( 2 ) );
147+
rb->addPoint( labelpos.cornerPoints.at( 3 ) );
148+
rb->addPoint( labelpos.cornerPoints.at( 0 ) );
150149
rb->setColor( color );
151150
rb->setWidth( 0 );
152151
rb->show();

‎src/core/qgslabelsearchtree.cpp

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void QgsLabelSearchTree::label( const QgsPointXY &point, QList<QgsLabelPosition
4545
QList<QgsLabelPosition *>::const_iterator resultIt = searchResults.constBegin();
4646
for ( ; resultIt != searchResults.constEnd(); ++resultIt )
4747
{
48-
if ( ( *resultIt )->labelRect.contains( p ) )
48+
if ( ( *resultIt )->labelGeometry.contains( &p ) )
4949
{
5050
posList.push_back( *resultIt );
5151
}
@@ -68,7 +68,10 @@ void QgsLabelSearchTree::labelsInRect( const QgsRectangle &r, QList<QgsLabelPosi
6868
QList<QgsLabelPosition *>::const_iterator resultIt = searchResults.constBegin();
6969
for ( ; resultIt != searchResults.constEnd(); ++resultIt )
7070
{
71-
posList.push_back( *resultIt );
71+
if ( ( *resultIt )->labelGeometry.intersects( r ) )
72+
{
73+
posList.push_back( *resultIt );
74+
}
7275
}
7376
}
7477

@@ -79,30 +82,32 @@ bool QgsLabelSearchTree::insertLabel( pal::LabelPosition *labelPos, QgsFeatureId
7982
return false;
8083
}
8184

82-
double c_min[2];
83-
double c_max[2];
84-
labelPos->getBoundingBox( c_min, c_max );
85-
86-
// we have to transform the bounding box to convert pre-rotated label positions back to real world locations
87-
double x1, y1;
88-
double x2, y2;
89-
mTransform.map( c_min[0], c_min[1], &x1, &y1 );
90-
mTransform.map( c_max[0], c_max[1], &x2, &y2 );
91-
c_min[0] = std::min( x1, x2 );
92-
c_min[1] = std::min( y1, y2 );
93-
c_max[0] = std::max( x1, x2 );
94-
c_max[1] = std::max( y1, y2 );
95-
9685
QVector<QgsPointXY> cornerPoints;
9786
cornerPoints.reserve( 4 );
87+
double xMin = std::numeric_limits< double >::max();
88+
double yMin = std::numeric_limits< double >::max();
89+
double xMax = std::numeric_limits< double >::lowest();
90+
double yMax = std::numeric_limits< double >::lowest();
9891
for ( int i = 0; i < 4; ++i )
9992
{
93+
// we have to transform the bounding box to convert pre-rotated label positions back to real world locations
10094
QPointF res = mTransform.map( QPointF( labelPos->getX( i ), labelPos->getY( i ) ) );
10195
cornerPoints.push_back( QgsPointXY( res ) );
96+
xMin = std::min( xMin, res.x() );
97+
xMax = std::max( xMax, res.x() );
98+
yMin = std::min( yMin, res.y() );
99+
yMax = std::max( yMax, res.y() );
102100
}
101+
double c_min[2];
102+
double c_max[2];
103+
c_min[0] = xMin;
104+
c_min[1] = yMin;
105+
c_max[0] = xMax;
106+
c_max[1] = yMax;
103107

108+
QgsGeometry labelGeometry( QgsGeometry::fromPolygonXY( QVector<QgsPolylineXY>() << cornerPoints ) );
104109
std::unique_ptr< QgsLabelPosition > newEntry = qgis::make_unique< QgsLabelPosition >( featureId, labelPos->getAlpha() + mMapSettings.rotation(), cornerPoints, QgsRectangle( c_min[0], c_min[1], c_max[0], c_max[1] ),
105-
labelPos->getWidth(), labelPos->getHeight(), layerName, labeltext, labelfont, labelPos->getUpsideDown(), diagram, pinned, providerId );
110+
labelPos->getWidth(), labelPos->getHeight(), layerName, labeltext, labelfont, labelPos->getUpsideDown(), diagram, pinned, providerId, labelGeometry );
106111
mSpatialIndex.Insert( c_min, c_max, newEntry.get() );
107112
mOwnedPositions.emplace_back( std::move( newEntry ) );
108113
return true;

‎src/core/qgspallabeling.h

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,13 @@ class QgsExpressionContext;
7979
class CORE_EXPORT QgsLabelPosition
8080
{
8181
public:
82-
QgsLabelPosition( QgsFeatureId id, double r, const QVector< QgsPointXY > &corners, const QgsRectangle &rect, double w, double h, const QString &layer, const QString &labeltext, const QFont &labelfont, bool upside_down, bool diagram = false, bool pinned = false, const QString &providerId = QString() )
82+
QgsLabelPosition( QgsFeatureId id, double r, const QVector< QgsPointXY > &corners, const QgsRectangle &rect, double w, double h, const QString &layer, const QString &labeltext, const QFont &labelfont, bool upside_down, bool diagram = false, bool pinned = false, const QString &providerId = QString(),
83+
const QgsGeometry &labelGeometry = QgsGeometry() )
8384
: featureId( id )
8485
, rotation( r )
8586
, cornerPoints( corners )
8687
, labelRect( rect )
88+
, labelGeometry( labelGeometry )
8789
, width( w )
8890
, height( h )
8991
, layerID( layer )
@@ -98,19 +100,69 @@ class CORE_EXPORT QgsLabelPosition
98100
//! Constructor for QgsLabelPosition
99101
QgsLabelPosition() = default;
100102

103+
/**
104+
* ID of feature associated with this label.
105+
*/
101106
QgsFeatureId featureId = FID_NULL;
107+
108+
/**
109+
* Rotation of label, in degrees clockwise.
110+
*/
102111
double rotation = 0;
112+
103113
QVector< QgsPointXY > cornerPoints;
104114
QgsRectangle labelRect;
115+
116+
/**
117+
* A polygon geometry representing the label's bounds in map coordinates.
118+
* \since QGIS 3.4.9
119+
*/
120+
QgsGeometry labelGeometry;
121+
122+
/**
123+
* Width of label bounding box, in map units.
124+
*/
105125
double width = 0;
126+
127+
/**
128+
* Heeght of label bounding box, in map units.
129+
*/
106130
double height = 0;
131+
132+
/**
133+
* ID of associated map layer.
134+
*/
107135
QString layerID;
136+
137+
/**
138+
* String shown in label.
139+
*/
108140
QString labelText;
141+
142+
/**
143+
* Font which the label is rendered using.
144+
*/
109145
QFont labelFont;
146+
147+
/**
148+
* TRUE if label is upside down.
149+
*/
110150
bool upsideDown = false;
151+
152+
/**
153+
* TRUE if label is a diagram.
154+
*/
111155
bool isDiagram = false;
156+
157+
/**
158+
* TRUE if label position has been pinned.
159+
*/
112160
bool isPinned = false;
113-
//! \since QGIS 2.14
161+
162+
/**
163+
* ID of the associated label provider.
164+
* \since QGIS 2.14
165+
*/
114166
QString providerID;
115167
};
116168

‎tests/src/core/testqgslabelingengine.cpp

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,18 +1484,19 @@ void TestQgsLabelingEngine::labelingResults()
14841484
QCOMPARE( labels.at( 0 ).labelText, QStringLiteral( "1" ) );
14851485
QGSCOMPARENEAR( labels.at( 0 ).width, 167961, 500 ); // tolerance will probably need tweaking, to account for cross-platform font diffs
14861486
QGSCOMPARENEAR( labels.at( 0 ).height, 295119, 500 );
1487-
#if 0 // TODO
1488-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMinimum(), -779822, 500 );
1489-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMaximum(), -611861, 500 );
1490-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMinimum(), 6897647, 500 );
1491-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMaximum(), 7192767, 500 );
1492-
#endif
1487+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMinimum(), -865622, 500 );
1488+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMaximum(), -526060, 500 );
1489+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMinimum(), 6898697, 500 );
1490+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMaximum(), 7191716, 500 );
14931491
QCOMPARE( labels.at( 0 ).rotation, 60 );
14941492

1493+
// should fall outside of rotated bounding box!
14951494
labels = results->labelsAtPosition( QgsPointXY( -769822, 6927647 ) );
1496-
QCOMPARE( labels.count(), 1 );
1497-
QCOMPARE( labels.at( 0 ).featureId, 1 );
1495+
QCOMPARE( labels.count(), 0 );
14981496
labels = results->labelsAtPosition( QgsPointXY( -615861, 7132767 ) );
1497+
QCOMPARE( labels.count(), 0 );
1498+
// just on corner, should only work if rotation of label's bounding box is handled correctly
1499+
labels = results->labelsAtPosition( QgsPointXY( -610000, 6898800 ) );
14991500
QCOMPARE( labels.count(), 1 );
15001501
QCOMPARE( labels.at( 0 ).featureId, 1 );
15011502

@@ -1505,25 +1506,21 @@ void TestQgsLabelingEngine::labelingResults()
15051506
QCOMPARE( labels.at( 0 ).labelText, QStringLiteral( "8888" ) );
15061507
QGSCOMPARENEAR( labels.at( 0 ).width, 671844, 500 ); // tolerance will probably need tweaking, to account for cross-platform font diffs
15071508
QGSCOMPARENEAR( labels.at( 0 ).height, 295119, 500 );
1508-
#if 0 // TODO
1509-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMinimum(), -2779386, 500 );
1510-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMaximum(), -2107542, 500 );
1511-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMinimum(), 9240403, 500 );
1512-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMaximum(), 9535523, 500 );
1513-
#endif
1509+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMinimum(), -2739216, 500 );
1510+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMaximum(), -2147712, 500 );
1511+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMinimum(), 9023266, 500 );
1512+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMaximum(), 9752660, 500 );
15141513
QCOMPARE( labels.at( 0 ).rotation, 60 );
15151514
labels = results->labelsAtPosition( QgsPointXY( -1383, 6708478 ) );
15161515
QCOMPARE( labels.count(), 1 );
15171516
QCOMPARE( labels.at( 0 ).featureId, 3 );
15181517
QCOMPARE( labels.at( 0 ).labelText, QStringLiteral( "33333" ) );
15191518
QGSCOMPARENEAR( labels.at( 0 ).width, 839805, 500 ); // tolerance will probably need tweaking, to account for cross-platform font diffs
15201519
QGSCOMPARENEAR( labels.at( 0 ).height, 295119, 500 );
1521-
#if 0 // TODO
1522-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMinimum(), -433112, 500 );
1523-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMaximum(), 406692, 500 );
1524-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMinimum(), 6563006, 500 );
1525-
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMaximum(), 6858125, 500 );
1526-
#endif
1520+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMinimum(), -350952, 500 );
1521+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.xMaximum(), 324531, 500 );
1522+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMinimum(), 6273139, 500 );
1523+
QGSCOMPARENEAR( labels.at( 0 ).labelRect.yMaximum(), 7147992, 500 );
15271524
QCOMPARE( labels.at( 0 ).rotation, 60 );
15281525
labels = results->labelsAtPosition( QgsPointXY( -2463392, 6708478 ) );
15291526
QCOMPARE( labels.count(), 0 );

0 commit comments

Comments
 (0)
Please sign in to comment.