1 /***************************************************************************
2      testqgsmaptoollabel.cpp
3      -----------------------
4     Date                 : July 2019
5     Copyright            : (C) 2019 by Nyall Dawson
6     Email                : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include "qgstest.h"
17 #include <QObject>
18 #include <QString>
19 #include <QMouseEvent>
20 
21 #include "qgsapplication.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsvectordataprovider.h"
24 #include "qgsgeometry.h"
25 #include "qgsmapcanvas.h"
26 #include "qgsmaptoollabel.h"
27 #include "qgsfontutils.h"
28 #include "qgsvectorlayerlabelprovider.h"
29 #include "qgsvectorlayerlabeling.h"
30 #include "qgsadvanceddigitizingdockwidget.h"
31 #include "qgsexpressioncontextutils.h"
32 
33 class TestQgsMapToolLabel : public QObject
34 {
35     Q_OBJECT
36 
37   public:
38     TestQgsMapToolLabel() = default;
39 
40   private:
41 
42   private slots:
43 
initTestCase()44     void initTestCase()
45     {
46       QgsApplication::init();
47       QgsApplication::initQgis();
48 
49     }
50 
cleanupTestCase()51     void cleanupTestCase()
52     {
53       QgsApplication::exitQgis();
54     }
55 
testSelectLabel()56     void testSelectLabel()
57     {
58       std::unique_ptr< QgsVectorLayer > vl1 = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?crs=epsg:3946&field=text:string" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) );
59       QVERIFY( vl1->isValid() );
60       QgsFeature f1;
61       f1.setAttributes( QgsAttributes() << QStringLiteral( "label" ) );
62       f1.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( 1, 1 ) ) );
63       QVERIFY( vl1->dataProvider()->addFeature( f1 ) );
64       f1.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( 3, 3 ) ) );
65       f1.setAttributes( QgsAttributes() << QStringLiteral( "l" ) );
66       QVERIFY( vl1->dataProvider()->addFeature( f1 ) );
67 
68       std::unique_ptr< QgsVectorLayer > vl2 = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point?crs=epsg:3946&field=text:string" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) );
69       QVERIFY( vl2->isValid() );
70       f1.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( 1, 1 ) ) );
71       f1.setAttributes( QgsAttributes() << QStringLiteral( "label" ) );
72       QVERIFY( vl2->dataProvider()->addFeature( f1 ) );
73       f1.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( 3, 3 ) ) );
74       f1.setAttributes( QgsAttributes() << QStringLiteral( "label2" ) );
75       QVERIFY( vl2->dataProvider()->addFeature( f1 ) );
76       f1.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( 3, 1 ) ) );
77       f1.setAttributes( QgsAttributes() << QStringLiteral( "label3" ) );
78       QVERIFY( vl2->dataProvider()->addFeature( f1 ) );
79 
80       std::unique_ptr< QgsMapCanvas > canvas = std::make_unique< QgsMapCanvas >();
81       canvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) );
82       canvas->setLayers( QList<QgsMapLayer *>() << vl1.get() << vl2.get() );
83       const std::unique_ptr< QgsAdvancedDigitizingDockWidget > advancedDigitizingDockWidget = std::make_unique< QgsAdvancedDigitizingDockWidget >( canvas.get() );
84 
85       QgsMapSettings mapSettings;
86       mapSettings.setOutputSize( QSize( 500, 500 ) );
87       mapSettings.setExtent( QgsRectangle( -1, -1, 4, 4 ) );
88       QVERIFY( mapSettings.hasValidSettings() );
89 
90       mapSettings.setLayers( QList<QgsMapLayer *>() << vl1.get() << vl2.get() );
91 
92       canvas->setFrameStyle( QFrame::NoFrame );
93       canvas->resize( 500, 500 );
94       canvas->setExtent( QgsRectangle( -1, -1, 4, 4 ) );
95       canvas->show(); // to make the canvas resize
96       canvas->hide();
97       QCOMPARE( canvas->mapSettings().outputSize(), QSize( 500, 500 ) );
98       QCOMPARE( canvas->mapSettings().visibleExtent(), QgsRectangle( -1, -1, 4, 4 ) );
99 
100       std::unique_ptr< QgsMapToolLabel > tool( new QgsMapToolLabel( canvas.get(), advancedDigitizingDockWidget.get() ) );
101 
102       // no labels yet
103       QgsPointXY pt;
104       pt = tool->canvas()->mapSettings().mapToPixel().transform( 1, 1 );
105       std::unique_ptr< QMouseEvent > event( new QMouseEvent(
106                                               QEvent::MouseButtonPress,
107                                               QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
108                                             ) );
109       QgsLabelPosition pos;
110       QVERIFY( !tool->labelAtPosition( event.get(), pos ) );
111 
112       // add some labels
113       QgsPalLayerSettings pls1;
114       pls1.fieldName = QStringLiteral( "text" );
115       pls1.placement = QgsPalLayerSettings::OverPoint;
116       pls1.quadOffset = QgsPalLayerSettings::QuadrantOver;
117       pls1.displayAll = true;
118       QgsTextFormat format = pls1.format();
119       format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ) );
120       format.setSize( 12 );
121       pls1.setFormat( format );
122 
123       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
124       vl1->setLabelsEnabled( true );
125 
126       QEventLoop loop;
127       connect( canvas.get(), &QgsMapCanvas::mapCanvasRefreshed, &loop, &QEventLoop::quit );
128       canvas->refreshAllLayers();
129       canvas->show();
130       loop.exec();
131 
132       QVERIFY( canvas->labelingResults() );
133       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
134       QCOMPARE( pos.layerID, vl1->id() );
135       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
136 
137       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 3 );
138       event = std::make_unique< QMouseEvent >(
139                 QEvent::MouseButtonPress,
140                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
141               );
142       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
143       QCOMPARE( pos.layerID, vl1->id() );
144       QCOMPARE( pos.labelText, QStringLiteral( "l" ) );
145 
146       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 1 );
147       event = std::make_unique< QMouseEvent >(
148                 QEvent::MouseButtonPress,
149                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
150               );
151       QVERIFY( !tool->labelAtPosition( event.get(), pos ) );
152 
153       // label second layer
154       vl2->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
155       vl2->setLabelsEnabled( true );
156 
157       canvas->refreshAllLayers();
158       canvas->show();
159       loop.exec();
160 
161       // should prioritize current layer
162       canvas->setCurrentLayer( vl1.get() );
163       pt = tool->canvas()->mapSettings().mapToPixel().transform( 1, 1 );
164       event = std::make_unique< QMouseEvent >(
165                 QEvent::MouseButtonPress,
166                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
167               );
168       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
169       QCOMPARE( pos.layerID, vl1->id() );
170       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
171 
172       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 3 );
173       event = std::make_unique< QMouseEvent >(
174                 QEvent::MouseButtonPress,
175                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
176               );
177       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
178       QCOMPARE( pos.layerID, vl1->id() );
179       QCOMPARE( pos.labelText, QStringLiteral( "l" ) );
180 
181       //... but fallback to any labels if nothing in current layer
182       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 1 );
183       event = std::make_unique< QMouseEvent >(
184                 QEvent::MouseButtonPress,
185                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
186               );
187       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
188       QCOMPARE( pos.layerID, vl2->id() );
189       QCOMPARE( pos.labelText, QStringLiteral( "label3" ) );
190 
191       canvas->setCurrentLayer( vl2.get() );
192       pt = tool->canvas()->mapSettings().mapToPixel().transform( 1, 1 );
193       event = std::make_unique< QMouseEvent >(
194                 QEvent::MouseButtonPress,
195                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
196               );
197       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
198       QCOMPARE( pos.layerID, vl2->id() );
199       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
200 
201       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 3 );
202       event = std::make_unique< QMouseEvent >(
203                 QEvent::MouseButtonPress,
204                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
205               );
206       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
207       QCOMPARE( pos.layerID, vl2->id() );
208       QCOMPARE( pos.labelText, QStringLiteral( "label2" ) );
209       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 1 );
210       event = std::make_unique< QMouseEvent >(
211                 QEvent::MouseButtonPress,
212                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
213               );
214       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
215       QCOMPARE( pos.layerID, vl2->id() );
216       QCOMPARE( pos.labelText, QStringLiteral( "label3" ) );
217 
218       canvas->setCurrentLayer( nullptr );
219 
220       // when multiple candidates exist, pick the smallest
221       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 3 );
222       event = std::make_unique< QMouseEvent >(
223                 QEvent::MouseButtonPress,
224                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
225               );
226       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
227       QCOMPARE( pos.layerID, vl1->id() );
228       QCOMPARE( pos.labelText, QStringLiteral( "l" ) );
229     }
230 
testAlignment()231     void testAlignment()
232     {
233       QgsVectorLayer *vl1 = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:3946&field=halig:string&field=valig:string" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) );
234       QVERIFY( vl1->isValid() );
235       QgsProject::instance()->addMapLayer( vl1 );
236       QgsFeature f1;
237       f1.setAttributes( QgsAttributes() << QStringLiteral( "right" ) << QStringLiteral( "top" ) );
238       f1.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( 1, 1 ) ) );
239       QVERIFY( vl1->dataProvider()->addFeature( f1 ) );
240       f1.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( 3, 3 ) ) );
241       f1.setAttributes( QgsAttributes() << QStringLiteral( "center" ) << QStringLiteral( "base" ) );
242       QVERIFY( vl1->dataProvider()->addFeature( f1 ) );
243 
244       std::unique_ptr< QgsMapCanvas > canvas = std::make_unique< QgsMapCanvas >();
245       canvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) );
246       canvas->setLayers( QList<QgsMapLayer *>() << vl1 );
247       const std::unique_ptr< QgsAdvancedDigitizingDockWidget > advancedDigitizingDockWidget = std::make_unique< QgsAdvancedDigitizingDockWidget >( canvas.get() );
248 
249       QgsMapSettings mapSettings;
250       mapSettings.setOutputSize( QSize( 500, 500 ) );
251       mapSettings.setExtent( QgsRectangle( -1, -1, 4, 4 ) );
252       QVERIFY( mapSettings.hasValidSettings() );
253 
254       mapSettings.setLayers( QList<QgsMapLayer *>() << vl1 );
255 
256       canvas->setFrameStyle( QFrame::NoFrame );
257       canvas->resize( 500, 500 );
258       canvas->setExtent( QgsRectangle( -1, -1, 4, 4 ) );
259       canvas->show(); // to make the canvas resize
260       canvas->hide();
261       QCOMPARE( canvas->mapSettings().outputSize(), QSize( 500, 500 ) );
262       QCOMPARE( canvas->mapSettings().visibleExtent(), QgsRectangle( -1, -1, 4, 4 ) );
263 
264       std::unique_ptr< QgsMapToolLabel > tool( new QgsMapToolLabel( canvas.get(), advancedDigitizingDockWidget.get() ) );
265 
266       // add some labels
267       QgsPalLayerSettings pls1;
268       pls1.fieldName = QStringLiteral( "'label'" );
269       pls1.isExpression = true;
270       pls1.placement = QgsPalLayerSettings::OverPoint;
271       pls1.quadOffset = QgsPalLayerSettings::QuadrantOver;
272       pls1.displayAll = true;
273       QgsTextFormat format = pls1.format();
274       format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ) );
275       format.setSize( 12 );
276       pls1.setFormat( format );
277 
278       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
279       vl1->setLabelsEnabled( true );
280 
281       QEventLoop loop;
282       connect( canvas.get(), &QgsMapCanvas::mapCanvasRefreshed, &loop, &QEventLoop::quit );
283       canvas->refreshAllLayers();
284       canvas->show();
285       loop.exec();
286 
287       QVERIFY( canvas->labelingResults() );
288       QgsPointXY pt;
289       pt = tool->canvas()->mapSettings().mapToPixel().transform( 1, 1 );
290       std::unique_ptr< QMouseEvent > event( new QMouseEvent(
291                                               QEvent::MouseButtonPress,
292                                               QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
293                                             ) );
294       QgsLabelPosition pos;
295       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
296       QCOMPARE( pos.layerID, vl1->id() );
297       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
298       tool->mCurrentLabel = QgsMapToolLabel::LabelDetails( pos, canvas.get() );
299 
300       // defaults to bottom left
301       QString hali, vali;
302       tool->currentAlignment( hali, vali );
303       QCOMPARE( hali, QStringLiteral( "Left" ) );
304       QCOMPARE( vali, QStringLiteral( "Bottom" ) );
305 
306       // using field bound alignment
307       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::Hali, QgsProperty::fromField( QStringLiteral( "halig" ) ) );
308       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::Vali, QgsProperty::fromField( QStringLiteral( "valig" ) ) );
309       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
310 
311       canvas->refreshAllLayers();
312       canvas->show();
313       loop.exec();
314 
315       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
316       QCOMPARE( pos.layerID, vl1->id() );
317       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
318       tool->mCurrentLabel = QgsMapToolLabel::LabelDetails( pos, canvas.get() );
319 
320       tool->currentAlignment( hali, vali );
321       QCOMPARE( hali, QStringLiteral( "right" ) );
322       QCOMPARE( vali, QStringLiteral( "top" ) );
323 
324       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 3 );
325       event = std::make_unique< QMouseEvent >(
326                 QEvent::MouseButtonPress,
327                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
328               );
329 
330       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
331       QCOMPARE( pos.layerID, vl1->id() );
332       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
333       tool->mCurrentLabel = QgsMapToolLabel::LabelDetails( pos, canvas.get() );
334 
335       tool->currentAlignment( hali, vali );
336       QCOMPARE( hali, QStringLiteral( "center" ) );
337       QCOMPARE( vali, QStringLiteral( "base" ) );
338 
339       // now try with expression based alignment
340       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::Hali, QgsProperty::fromExpression( QStringLiteral( "case when $id % 2 = 0 then 'right' else 'left' end" ) ) );
341       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::Vali, QgsProperty::fromExpression( QStringLiteral( "case when $id % 2 = 0 then 'half' else 'cap' end" ) ) );
342       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
343 
344       canvas->refreshAllLayers();
345       canvas->show();
346       loop.exec();
347 
348       pt = tool->canvas()->mapSettings().mapToPixel().transform( 1, 1 );
349       event = std::make_unique< QMouseEvent >(
350                 QEvent::MouseButtonPress,
351                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
352               );
353 
354       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
355       QCOMPARE( pos.layerID, vl1->id() );
356       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
357       tool->mCurrentLabel = QgsMapToolLabel::LabelDetails( pos, canvas.get() );
358 
359       tool->currentAlignment( hali, vali );
360       QCOMPARE( hali, QStringLiteral( "left" ) );
361       QCOMPARE( vali, QStringLiteral( "cap" ) );
362 
363       pt = tool->canvas()->mapSettings().mapToPixel().transform( 3, 3 );
364       event = std::make_unique< QMouseEvent >(
365                 QEvent::MouseButtonPress,
366                 QPoint( std::round( pt.x() ), std::round( pt.y() ) ), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier
367               );
368 
369       QVERIFY( tool->labelAtPosition( event.get(), pos ) );
370       QCOMPARE( pos.layerID, vl1->id() );
371       QCOMPARE( pos.labelText, QStringLiteral( "label" ) );
372       tool->mCurrentLabel = QgsMapToolLabel::LabelDetails( pos, canvas.get() );
373 
374       tool->currentAlignment( hali, vali );
375       QCOMPARE( hali, QStringLiteral( "right" ) );
376       QCOMPARE( vali, QStringLiteral( "half" ) );
377     }
378 
dataDefinedColumnName()379     void dataDefinedColumnName()
380     {
381       QgsVectorLayer *vl1 = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:3946&field=label_x_1:string&field=label_y_1:string&field=label_x_2:string&field=label_y_2:string&field=override_x_field:string" ), QStringLiteral( "vl1" ), QStringLiteral( "memory" ) );
382       QVERIFY( vl1->isValid() );
383       QgsProject::instance()->addMapLayer( vl1 );
384 
385       std::unique_ptr< QgsMapCanvas > canvas = std::make_unique< QgsMapCanvas >();
386       canvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) );
387       canvas->setLayers( QList<QgsMapLayer *>() << vl1 );
388       const std::unique_ptr< QgsAdvancedDigitizingDockWidget > advancedDigitizingDockWidget = std::make_unique< QgsAdvancedDigitizingDockWidget >( canvas.get() );
389 
390       std::unique_ptr< QgsMapToolLabel > tool( new QgsMapToolLabel( canvas.get(), advancedDigitizingDockWidget.get() ) );
391 
392       QgsExpressionContextUtils::setProjectVariable( QgsProject::instance(), QStringLiteral( "var_1" ), QStringLiteral( "1" ) );
393 
394       // add some labels
395       QgsPalLayerSettings pls1;
396       pls1.fieldName = QStringLiteral( "'label'" );
397 
398       // not using a column
399       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionX, QgsProperty::fromValue( 5 ) );
400       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionY, QgsProperty::fromValue( 6 ) );
401 
402       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
403       vl1->setLabelsEnabled( true );
404 
405       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::AlwaysShow, pls1, vl1 ), QString() );
406       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionX, pls1, vl1 ), QString() );
407       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionY, pls1, vl1 ), QString() );
408 
409       // using direct field references
410       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionX, QgsProperty::fromField( QStringLiteral( "label_x_2" ) ) );
411       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionY, QgsProperty::fromField( QStringLiteral( "label_y_2" ) ) );
412 
413       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
414       vl1->setLabelsEnabled( true );
415 
416       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::AlwaysShow, pls1, vl1 ), QString() );
417       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionX, pls1, vl1 ), QStringLiteral( "label_x_2" ) );
418       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionY, pls1, vl1 ), QStringLiteral( "label_y_2" ) );
419 
420       // using expressions which are just field references, should still work
421       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionX, QgsProperty::fromExpression( QStringLiteral( "\"label_x_1\"" ) ) );
422       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionY, QgsProperty::fromExpression( QStringLiteral( "\"label_y_1\"" ) ) );
423 
424       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
425       vl1->setLabelsEnabled( true );
426 
427       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::AlwaysShow, pls1, vl1 ), QString() );
428       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionX, pls1, vl1 ), QStringLiteral( "label_x_1" ) );
429       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionY, pls1, vl1 ), QStringLiteral( "label_y_1" ) );
430 
431 
432       // using complex expressions which change field depending on a project level variable
433 
434       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionX, QgsProperty::fromExpression( QStringLiteral( "case when @var_1 = '1' then \"label_x_1\" else \"label_x_2\" end" ) ) );
435       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionY, QgsProperty::fromExpression( QStringLiteral( "case when @var_1 = '1' then \"label_y_1\" else \"label_y_2\" end" ) ) );
436       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
437       vl1->setLabelsEnabled( true );
438 
439       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::AlwaysShow, pls1, vl1 ), QString() );
440       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionX, pls1, vl1 ), QStringLiteral( "label_x_1" ) );
441       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionY, pls1, vl1 ), QStringLiteral( "label_y_1" ) );
442 
443       QgsExpressionContextUtils::setProjectVariable( QgsProject::instance(), QStringLiteral( "var_1" ), QStringLiteral( "2" ) );
444 
445       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::AlwaysShow, pls1, vl1 ), QString() );
446       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionX, pls1, vl1 ), QStringLiteral( "label_x_2" ) );
447       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionY, pls1, vl1 ), QStringLiteral( "label_y_2" ) );
448 
449       // another smart situation -- an expression which uses coalesce to store per-feature overrides in a field, otherwise falling back to some complex expression
450       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionX, QgsProperty::fromExpression( QStringLiteral( "coalesce(\"override_x_field\", $x + 10)" ) ) );
451       pls1.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionY, QgsProperty::fromExpression( QStringLiteral( "COALESCE(case when @var_1 = '1' then \"label_y_1\" else \"label_y_2\" end, $y-20)" ) ) );
452       vl1->setLabeling( new QgsVectorLayerSimpleLabeling( pls1 ) );
453       vl1->setLabelsEnabled( true );
454 
455       QgsExpressionContextUtils::setProjectVariable( QgsProject::instance(), QStringLiteral( "var_1" ), QStringLiteral( "1" ) );
456 
457       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::AlwaysShow, pls1, vl1 ), QString() );
458       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionX, pls1, vl1 ), QStringLiteral( "override_x_field" ) );
459       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionY, pls1, vl1 ), QStringLiteral( "label_y_1" ) );
460 
461       QgsExpressionContextUtils::setProjectVariable( QgsProject::instance(), QStringLiteral( "var_1" ), QStringLiteral( "2" ) );
462 
463       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::AlwaysShow, pls1, vl1 ), QString() );
464       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionX, pls1, vl1 ), QStringLiteral( "override_x_field" ) );
465       QCOMPARE( tool->dataDefinedColumnName( QgsPalLayerSettings::PositionY, pls1, vl1 ), QStringLiteral( "label_y_2" ) );
466     }
467 
468 
469 };
470 
471 QGSTEST_MAIN( TestQgsMapToolLabel )
472 #include "testqgsmaptoollabel.moc"
473