1 /***************************************************************************
2     testqgsmaptooleditannotation.cpp
3      --------------------------------------
4     Date                 : September 2021
5     Copyright            : (C) 2021 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 #include <QCoreApplication>
16 
17 #include "qgstest.h"
18 #include "qgsguiutils.h"
19 #include "qgsmaptooledit.h"
20 #include "qgsapplication.h"
21 #include "qgsmapcanvas.h"
22 #include "qgslogger.h"
23 #include "qgsannotationlayer.h"
24 #include "qgsannotationpolygonitem.h"
25 #include "qgsproject.h"
26 #include "testqgsmaptoolutils.h"
27 #include "qgsmapmouseevent.h"
28 #include "qgspolygon.h"
29 #include "qgslinestring.h"
30 #include "qgsmaptoolmodifyannotation.h"
31 #include "qgsadvanceddigitizingdockwidget.h"
32 #include "qgsrendereditemresults.h"
33 
34 #include <QSignalSpy>
35 
36 class TestQgsMapToolEditAnnotation : public QObject
37 {
38     Q_OBJECT
39   public:
40     TestQgsMapToolEditAnnotation() = default;
41 
42   private slots:
43     void initTestCase(); // will be called before the first testfunction is executed.
44     void cleanupTestCase(); // will be called after the last testfunction was executed.
45     void init(); // will be called before each testfunction is executed.
46     void cleanup(); // will be called after every testfunction.
47 
48     void testSelectItem();
49     void testDeleteItem();
50     void testMoveItem();
51     void testMoveNode();
52     void testDeleteNode();
53     void testAddNode();
54 
55 };
56 
initTestCase()57 void TestQgsMapToolEditAnnotation::initTestCase()
58 {
59   QgsApplication::init();
60   QgsApplication::initQgis();
61   QgsApplication::showSettings();
62 }
63 
cleanupTestCase()64 void TestQgsMapToolEditAnnotation::cleanupTestCase()
65 {
66   QgsApplication::exitQgis();
67 }
68 
init()69 void TestQgsMapToolEditAnnotation::init()
70 {
71 }
72 
cleanup()73 void TestQgsMapToolEditAnnotation::cleanup()
74 {
75 }
76 
testSelectItem()77 void TestQgsMapToolEditAnnotation::testSelectItem()
78 {
79   QgsProject::instance()->clear();
80   QgsMapCanvas canvas;
81   canvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
82   canvas.setFrameStyle( QFrame::NoFrame );
83   canvas.resize( 600, 600 );
84   canvas.setExtent( QgsRectangle( 0, 0, 10, 10 ) );
85   canvas.show(); // to make the canvas resize
86 
87   QgsAnnotationLayer *layer = new QgsAnnotationLayer( QStringLiteral( "test" ), QgsAnnotationLayer::LayerOptions( QgsProject::instance()->transformContext() ) );
88   QVERIFY( layer->isValid() );
89   QgsAnnotationLayer *layer2 = new QgsAnnotationLayer( QStringLiteral( "test" ), QgsAnnotationLayer::LayerOptions( QgsProject::instance()->transformContext() ) );
90   QVERIFY( layer2->isValid() );
91   QgsProject::instance()->addMapLayers( { layer, layer2 } );
92 
93   QgsAnnotationPolygonItem *item1 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 1 ), QgsPoint( 5, 1 ), QgsPoint( 5, 5 ), QgsPoint( 1, 5 ), QgsPoint( 1, 1 ) } ) ) );
94   item1->setZIndex( 1 );
95   const QString i1id = layer->addItem( item1 );
96 
97   QgsAnnotationPolygonItem *item2 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 4 ), QgsPoint( 5, 4 ), QgsPoint( 5, 9 ), QgsPoint( 1, 9 ), QgsPoint( 1, 4 ) } ) ) );
98   item2->setZIndex( 2 );
99   const QString i2id = layer->addItem( item2 );
100 
101   QgsAnnotationPolygonItem *item3 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 7, 1 ), QgsPoint( 8, 1 ), QgsPoint( 8, 2 ), QgsPoint( 7, 2 ), QgsPoint( 7, 1 ) } ) ) );
102   item3->setZIndex( 3 );
103   const QString i3id = layer2->addItem( item3 );
104 
105   layer->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
106   layer2->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
107 
108   canvas.setLayers( { layer, layer2 } );
109   while ( !canvas.isDrawing() )
110   {
111     QgsApplication::processEvents();
112   }
113   canvas.waitWhileRendering();
114   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 3 );
115 
116   QgsAdvancedDigitizingDockWidget cadDock( &canvas );
117   QgsMapToolModifyAnnotation tool( &canvas, &cadDock );
118   canvas.setMapTool( &tool );
119 
120   QSignalSpy spy( &tool, &QgsMapToolModifyAnnotation::itemSelected );
121   TestQgsMapToolUtils utils( &tool );
122 
123   // click outside of items
124   utils.mouseMove( 9, 9 );
125   utils.mouseClick( 9, 9, Qt::LeftButton, Qt::KeyboardModifiers(), true );
126   QCOMPARE( spy.count(), 0 );
127 
128   // click on items
129   utils.mouseMove( 1.5, 1.5 );
130   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
131   QCOMPARE( spy.count(), 1 );
132   QCOMPARE( spy.at( 0 ).at( 1 ).toString(), i1id );
133 
134   // overlapping items, highest z order should be selected
135   utils.mouseMove( 1.5, 4.5 );
136   utils.mouseClick( 1.5, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
137   QCOMPARE( spy.count(), 2 );
138   QCOMPARE( spy.at( 1 ).at( 1 ).toString(), i2id );
139 
140   utils.mouseMove( 7.5, 1.5 );
141   utils.mouseClick( 7.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
142   QCOMPARE( spy.count(), 3 );
143   QCOMPARE( spy.at( 2 ).at( 1 ).toString(), i3id );
144 
145   // click on no item - should clear selection
146   QSignalSpy selectionClearedSpy( &tool, &QgsMapToolModifyAnnotation::selectionCleared );
147   utils.mouseMove( 9.5, 9.5 );
148   utils.mouseClick( 9.5, 9.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
149   QCOMPARE( spy.count(), 3 );
150   QCOMPARE( selectionClearedSpy.count(), 1 );
151 }
152 
testDeleteItem()153 void TestQgsMapToolEditAnnotation::testDeleteItem()
154 {
155   QgsProject::instance()->clear();
156   QgsMapCanvas canvas;
157   canvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
158   canvas.setFrameStyle( QFrame::NoFrame );
159   canvas.resize( 600, 600 );
160   canvas.setExtent( QgsRectangle( 0, 0, 10, 10 ) );
161   canvas.show(); // to make the canvas resize
162 
163   QgsAnnotationLayer *layer = new QgsAnnotationLayer( QStringLiteral( "test" ), QgsAnnotationLayer::LayerOptions( QgsProject::instance()->transformContext() ) );
164   QVERIFY( layer->isValid() );
165   QgsProject::instance()->addMapLayers( { layer } );
166 
167   QgsAnnotationPolygonItem *item1 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 1 ), QgsPoint( 5, 1 ), QgsPoint( 5, 5 ), QgsPoint( 1, 5 ), QgsPoint( 1, 1 ) } ) ) );
168   item1->setZIndex( 1 );
169   const QString i1id = layer->addItem( item1 );
170 
171   QgsAnnotationPolygonItem *item2 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 4 ), QgsPoint( 5, 4 ), QgsPoint( 5, 9 ), QgsPoint( 1, 9 ), QgsPoint( 1, 4 ) } ) ) );
172   item2->setZIndex( 2 );
173   const QString i2id = layer->addItem( item2 );
174 
175   QgsAnnotationPolygonItem *item3 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 7, 1 ), QgsPoint( 8, 1 ), QgsPoint( 8, 2 ), QgsPoint( 7, 2 ), QgsPoint( 7, 1 ) } ) ) );
176   item3->setZIndex( 3 );
177   const QString i3id = layer->addItem( item3 );
178 
179   layer->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
180 
181   canvas.setLayers( { layer } );
182   while ( !canvas.isDrawing() )
183   {
184     QgsApplication::processEvents();
185   }
186   canvas.waitWhileRendering();
187   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 3 );
188 
189   QgsAdvancedDigitizingDockWidget cadDock( &canvas );
190   QgsMapToolModifyAnnotation tool( &canvas, &cadDock );
191   canvas.setMapTool( &tool );
192 
193   TestQgsMapToolUtils utils( &tool );
194 
195   // no selected item
196   utils.mouseMove( 9, 9 );
197   utils.mouseClick( 9, 9, Qt::LeftButton, Qt::KeyboardModifiers(), true );
198   utils.keyClick( Qt::Key_Delete );
199   QCOMPARE( qgis::listToSet( layer->items().keys() ), QSet< QString >( { i1id, i2id, i3id } ) );
200 
201   // with selected item
202   utils.mouseMove( 1.5, 1.5 );
203   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
204   utils.keyClick( Qt::Key_Delete );
205   QCOMPARE( qgis::listToSet( layer->items().keys() ), QSet< QString >( { i2id, i3id } ) );
206   while ( !canvas.isDrawing() )
207   {
208     QgsApplication::processEvents();
209   }
210   canvas.waitWhileRendering();
211 
212   utils.mouseMove( 1.5, 4.5 );
213   utils.mouseClick( 1.5, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
214   utils.keyClick( Qt::Key_Delete );
215   QCOMPARE( qgis::listToSet( layer->items().keys() ), QSet< QString >( { i3id } ) );
216 }
217 
testMoveItem()218 void TestQgsMapToolEditAnnotation::testMoveItem()
219 {
220   QgsProject::instance()->clear();
221   QgsMapCanvas canvas;
222   canvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
223   canvas.setFrameStyle( QFrame::NoFrame );
224   canvas.resize( 600, 600 );
225   canvas.setExtent( QgsRectangle( 0, 0, 10, 10 ) );
226   canvas.show(); // to make the canvas resize
227 
228   QgsAnnotationLayer *layer = new QgsAnnotationLayer( QStringLiteral( "test" ), QgsAnnotationLayer::LayerOptions( QgsProject::instance()->transformContext() ) );
229   QVERIFY( layer->isValid() );
230   QgsProject::instance()->addMapLayers( { layer } );
231 
232   QgsAnnotationPolygonItem *item1 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 1 ), QgsPoint( 5, 1 ), QgsPoint( 5, 5 ), QgsPoint( 1, 5 ), QgsPoint( 1, 1 ) } ) ) );
233   item1->setZIndex( 1 );
234   const QString i1id = layer->addItem( item1 );
235   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((1 1, 5 1, 5 5, 1 5, 1 1))" ) );
236 
237   layer->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
238 
239   canvas.setLayers( { layer } );
240   while ( !canvas.isDrawing() )
241   {
242     QgsApplication::processEvents();
243   }
244   canvas.waitWhileRendering();
245   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
246 
247   QgsAdvancedDigitizingDockWidget cadDock( &canvas );
248   QgsMapToolModifyAnnotation tool( &canvas, &cadDock );
249   canvas.setMapTool( &tool );
250 
251   QSignalSpy spy( &tool, &QgsMapToolModifyAnnotation::itemSelected );
252   TestQgsMapToolUtils utils( &tool );
253 
254   // click on item
255   utils.mouseMove( 1.5, 1.5 );
256   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
257   QCOMPARE( spy.count(), 1 );
258   QCOMPARE( spy.at( 0 ).at( 1 ).toString(), i1id );
259 
260   // a second left click on the item will start moving the item
261   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
262   // second click isn't selecting an item, so no new signals should be emitted
263   QCOMPARE( spy.count(), 1 );
264 
265   // move mouse and click to end item move
266   utils.mouseMove( 4.5, 4.5 );
267   utils.mouseClick( 4.5, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
268   while ( !canvas.isDrawing() )
269   {
270     QgsApplication::processEvents();
271   }
272   canvas.waitWhileRendering();
273   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
274 
275   // check that item was moved
276   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((4 4, 8 4, 8 8, 4 8, 4 4))" ) );
277 
278   // start a new move
279   // click on item -- it should already be selected, so this will start a new move, not emit the itemSelected signal
280   utils.mouseMove( 4.6, 4.5 );
281   utils.mouseClick( 4.6, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
282   QCOMPARE( spy.count(), 1 );
283 
284   utils.mouseMove( 1.5, 1.5 );
285   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
286   while ( !canvas.isDrawing() )
287   {
288     QgsApplication::processEvents();
289   }
290   canvas.waitWhileRendering();
291   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
292 
293   // check that item was moved
294   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt( 1 ), QStringLiteral( "Polygon ((0.9 1, 4.9 1, 4.9 5, 0.9 5, 0.9 1))" ) );
295 
296   // start a move then cancel it via right click
297   utils.mouseMove( 1.6, 1.6 );
298   utils.mouseClick( 1.6, 1.6, Qt::LeftButton, Qt::KeyboardModifiers(), true );
299   QCOMPARE( spy.count(), 1 );
300 
301   utils.mouseMove( 4.5, 4.5 );
302   utils.mouseClick( 4.5, 4.5, Qt::RightButton, Qt::KeyboardModifiers(), true );
303   // check that item was NOT moved
304   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt( 1 ), QStringLiteral( "Polygon ((0.9 1, 4.9 1, 4.9 5, 0.9 5, 0.9 1))" ) );
305 
306   // cancel a move via escape key
307   utils.mouseMove( 1.6, 1.6 );
308   utils.mouseClick( 1.6, 1.6, Qt::LeftButton, Qt::KeyboardModifiers(), true );
309   QCOMPARE( spy.count(), 1 );
310 
311   utils.mouseMove( 4.5, 4.5 );
312   // escape should cancel
313   utils.keyClick( Qt::Key_Escape );
314   //... so next click is not "finish move", but "clear selection"
315   utils.mouseClick( 4.5, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
316   // check that item was NOT moved
317   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt( 1 ), QStringLiteral( "Polygon ((0.9 1, 4.9 1, 4.9 5, 0.9 5, 0.9 1))" ) );
318 }
319 
testMoveNode()320 void TestQgsMapToolEditAnnotation::testMoveNode()
321 {
322   QgsProject::instance()->clear();
323   QgsMapCanvas canvas;
324   canvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
325   canvas.setFrameStyle( QFrame::NoFrame );
326   canvas.resize( 600, 600 );
327   canvas.setExtent( QgsRectangle( 0, 0, 10, 10 ) );
328   canvas.show(); // to make the canvas resize
329 
330   QgsAnnotationLayer *layer = new QgsAnnotationLayer( QStringLiteral( "test" ), QgsAnnotationLayer::LayerOptions( QgsProject::instance()->transformContext() ) );
331   QVERIFY( layer->isValid() );
332   QgsProject::instance()->addMapLayers( { layer } );
333 
334   QgsAnnotationPolygonItem *item1 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 1 ), QgsPoint( 5, 1 ), QgsPoint( 5, 5 ), QgsPoint( 1, 5 ), QgsPoint( 1, 1 ) } ) ) );
335   item1->setZIndex( 1 );
336   const QString i1id = layer->addItem( item1 );
337   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((1 1, 5 1, 5 5, 1 5, 1 1))" ) );
338 
339   layer->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
340 
341   canvas.setLayers( { layer } );
342   while ( !canvas.isDrawing() )
343   {
344     QgsApplication::processEvents();
345   }
346   canvas.waitWhileRendering();
347   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
348 
349   QgsAdvancedDigitizingDockWidget cadDock( &canvas );
350   QgsMapToolModifyAnnotation tool( &canvas, &cadDock );
351   canvas.setMapTool( &tool );
352 
353   QSignalSpy spy( &tool, &QgsMapToolModifyAnnotation::itemSelected );
354   TestQgsMapToolUtils utils( &tool );
355 
356   // click on item
357   utils.mouseMove( 1.5, 1.5 );
358   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
359   QCOMPARE( spy.count(), 1 );
360   QCOMPARE( spy.at( 0 ).at( 1 ).toString(), i1id );
361 
362   // click on a node
363   utils.mouseMove( 5, 5 );
364   utils.mouseClick( 5, 5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
365   // second click isn't selecting an item, so no new signals should be emitted
366   QCOMPARE( spy.count(), 1 );
367 
368   // move mouse and click to end node move
369   utils.mouseMove( 4.5, 4.5 );
370   utils.mouseClick( 4.5, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
371   while ( !canvas.isDrawing() )
372   {
373     QgsApplication::processEvents();
374   }
375   canvas.waitWhileRendering();
376   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
377 
378   // check that item was moved
379   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((1 1, 5 1, 4.5 4.5, 1 5, 1 1))" ) );
380 
381   // start a new move node
382   // click on item -- it should already be selected, so this will start a new move, not emit the itemSelected signal
383   utils.mouseMove( 5, 1 );
384   utils.mouseClick( 5, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
385   QCOMPARE( spy.count(), 1 );
386 
387   utils.mouseMove( 5.5, 1.5 );
388   utils.mouseClick( 5.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
389   while ( !canvas.isDrawing() )
390   {
391     QgsApplication::processEvents();
392   }
393   canvas.waitWhileRendering();
394   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
395 
396   // check that item was moved
397   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt( 1 ), QStringLiteral( "Polygon ((1 1, 5.5 1.5, 4.5 4.5, 1 5, 1 1))" ) );
398 
399   // start a move then cancel it via right click
400   utils.mouseMove( 4.5, 4.5 );
401   utils.mouseClick( 4.5, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
402   QCOMPARE( spy.count(), 1 );
403 
404   utils.mouseMove( 4.9, 4.9 );
405   utils.mouseClick( 4.9, 4.9, Qt::RightButton, Qt::KeyboardModifiers(), true );
406   // check that node was NOT moved
407   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt( 1 ), QStringLiteral( "Polygon ((1 1, 5.5 1.5, 4.5 4.5, 1 5, 1 1))" ) );
408 
409   // cancel a move via escape key
410   utils.mouseMove( 4.5, 4.5 );
411   utils.mouseClick( 4.5, 4.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
412   QCOMPARE( spy.count(), 1 );
413 
414   utils.mouseMove( 4.9, 4.9 );
415   // escape should cancel
416   utils.keyClick( Qt::Key_Escape );
417   //... so next click is not "finish move", but "clear selection"
418   utils.mouseClick( 6.5, 6.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
419   // check that node was NOT moved
420   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt( 1 ), QStringLiteral( "Polygon ((1 1, 5.5 1.5, 4.5 4.5, 1 5, 1 1))" ) );
421 }
422 
testDeleteNode()423 void TestQgsMapToolEditAnnotation::testDeleteNode()
424 {
425   QgsProject::instance()->clear();
426   QgsMapCanvas canvas;
427   canvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
428   canvas.setFrameStyle( QFrame::NoFrame );
429   canvas.resize( 600, 600 );
430   canvas.setExtent( QgsRectangle( 0, 0, 10, 10 ) );
431   canvas.show(); // to make the canvas resize
432 
433   QgsAnnotationLayer *layer = new QgsAnnotationLayer( QStringLiteral( "test" ), QgsAnnotationLayer::LayerOptions( QgsProject::instance()->transformContext() ) );
434   QVERIFY( layer->isValid() );
435   QgsProject::instance()->addMapLayers( { layer } );
436 
437   QgsAnnotationPolygonItem *item1 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 1 ), QgsPoint( 5, 1 ), QgsPoint( 5, 5 ), QgsPoint( 1, 5 ), QgsPoint( 1, 1 ) } ) ) );
438   item1->setZIndex( 1 );
439   const QString i1id = layer->addItem( item1 );
440   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((1 1, 5 1, 5 5, 1 5, 1 1))" ) );
441 
442   layer->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
443 
444   canvas.setLayers( { layer } );
445   while ( !canvas.isDrawing() )
446   {
447     QgsApplication::processEvents();
448   }
449   canvas.waitWhileRendering();
450   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
451 
452   QgsAdvancedDigitizingDockWidget cadDock( &canvas );
453   QgsMapToolModifyAnnotation tool( &canvas, &cadDock );
454   canvas.setMapTool( &tool );
455 
456   QSignalSpy spy( &tool, &QgsMapToolModifyAnnotation::itemSelected );
457   TestQgsMapToolUtils utils( &tool );
458 
459   // click on item
460   utils.mouseMove( 1.5, 1.5 );
461   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
462   QCOMPARE( spy.count(), 1 );
463   QCOMPARE( spy.at( 0 ).at( 1 ).toString(), i1id );
464 
465   // click on a node
466   utils.mouseMove( 5, 5 );
467   utils.mouseClick( 5, 5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
468   // second click isn't selecting an item, so no new signals should be emitted
469   QCOMPARE( spy.count(), 1 );
470 
471   // delete node
472   utils.keyClick( Qt::Key_Delete );
473   while ( !canvas.isDrawing() )
474   {
475     QgsApplication::processEvents();
476   }
477   canvas.waitWhileRendering();
478   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
479 
480   // check that node was deleted
481   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((1 1, 5 1, 1 5, 1 1))" ) );
482 
483   // start a new delete node
484   // click on item -- it should already be selected, so this will start a new move, not emit the itemSelected signal
485   utils.mouseMove( 5, 1 );
486   utils.mouseClick( 5, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
487   QCOMPARE( spy.count(), 1 );
488 
489   utils.keyClick( Qt::Key_Delete );
490   while ( !canvas.isDrawing() )
491   {
492     QgsApplication::processEvents();
493   }
494   canvas.waitWhileRendering();
495 
496   // check that node was deleted -- this should delete the whole item, as its geometry was cleared
497   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 0 );
498   QVERIFY( !layer->item( i1id ) );
499 }
500 
testAddNode()501 void TestQgsMapToolEditAnnotation::testAddNode()
502 {
503   QgsProject::instance()->clear();
504   QgsMapCanvas canvas;
505   canvas.setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
506   canvas.setFrameStyle( QFrame::NoFrame );
507   canvas.resize( 600, 600 );
508   canvas.setExtent( QgsRectangle( 0, 0, 10, 10 ) );
509   canvas.show(); // to make the canvas resize
510 
511   QgsAnnotationLayer *layer = new QgsAnnotationLayer( QStringLiteral( "test" ), QgsAnnotationLayer::LayerOptions( QgsProject::instance()->transformContext() ) );
512   QVERIFY( layer->isValid() );
513   QgsProject::instance()->addMapLayers( { layer } );
514 
515   QgsAnnotationPolygonItem *item1 = new QgsAnnotationPolygonItem( new QgsPolygon( new QgsLineString( QVector<QgsPoint> { QgsPoint( 1, 1 ), QgsPoint( 5, 1 ), QgsPoint( 5, 5 ), QgsPoint( 1, 5 ), QgsPoint( 1, 1 ) } ) ) );
516   item1->setZIndex( 1 );
517   const QString i1id = layer->addItem( item1 );
518   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((1 1, 5 1, 5 5, 1 5, 1 1))" ) );
519 
520   layer->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
521 
522   canvas.setLayers( { layer } );
523   while ( !canvas.isDrawing() )
524   {
525     QgsApplication::processEvents();
526   }
527   canvas.waitWhileRendering();
528   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
529 
530   QgsAdvancedDigitizingDockWidget cadDock( &canvas );
531   QgsMapToolModifyAnnotation tool( &canvas, &cadDock );
532   canvas.setMapTool( &tool );
533 
534   QSignalSpy spy( &tool, &QgsMapToolModifyAnnotation::itemSelected );
535   TestQgsMapToolUtils utils( &tool );
536 
537   // click on item
538   utils.mouseMove( 1.5, 1.5 );
539   utils.mouseClick( 1.5, 1.5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
540   QCOMPARE( spy.count(), 1 );
541   QCOMPARE( spy.at( 0 ).at( 1 ).toString(), i1id );
542 
543   // double-click a segment
544   utils.mouseMove( 5, 3 );
545   utils.mouseDoubleClick( 5, 3, Qt::LeftButton, Qt::KeyboardModifiers(), true );
546   // second click isn't selecting an item, so no new signals should be emitted
547   QCOMPARE( spy.count(), 1 );
548   while ( !canvas.isDrawing() )
549   {
550     QgsApplication::processEvents();
551   }
552   canvas.waitWhileRendering();
553   QCOMPARE( canvas.renderedItemResults()->renderedItems().size(), 1 );
554 
555   // check that node was added
556   QCOMPARE( qgis::down_cast< QgsAnnotationPolygonItem * >( layer->item( i1id ) )->geometry()->asWkt(), QStringLiteral( "Polygon ((1 1, 5 1, 5 3, 5 5, 1 5, 1 1))" ) );
557 }
558 
559 
560 QGSTEST_MAIN( TestQgsMapToolEditAnnotation )
561 #include "testqgsmaptooleditannotation.moc"
562