1 /***************************************************************************
2      testqgsmaptoolsplitfeatures.cpp
3      ------------------------
4     Date                 : August 2020
5     Copyright            : (C) 2020 by Stefanos Natsis
6     Email                : uclaros@gmail.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 
18 #include "qgisapp.h"
19 #include "qgsgeometry.h"
20 #include "qgsmapcanvas.h"
21 #include "qgssettings.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsmaptoolsplitfeatures.h"
24 #include "qgsgeometryutils.h"
25 
26 #include "testqgsmaptoolutils.h"
27 
28 class TestQgsMapToolSplitFeatures : public QObject
29 {
30     Q_OBJECT
31 
32   public:
33     TestQgsMapToolSplitFeatures();
34 
35   private slots:
36     void initTestCase();
37     void cleanupTestCase();
38 
39     void testNoFeaturesSplit();
40     void testSplitPolygon();
41     void testSplitPolygonTopologicalEditing();
42 
43   private:
44     QPoint mapToPoint( double x, double y );
45     QgisApp *mQgisApp = nullptr;
46     QgsMapCanvas *mCanvas = nullptr;
47     QgsVectorLayer *mMultiLineStringLayer = nullptr;
48     QgsVectorLayer *mPolygonLayer = nullptr;
49     QgsVectorLayer *mMultiPolygonLayer = nullptr;
50     QgsFeature lineF1, lineF2, polygonF1, polygonF2, multipolygonF1;
51 };
52 
53 TestQgsMapToolSplitFeatures::TestQgsMapToolSplitFeatures() = default;
54 
55 
56 //runs before all tests
initTestCase()57 void TestQgsMapToolSplitFeatures::initTestCase()
58 {
59   QgsApplication::init();
60   QgsApplication::initQgis();
61 
62   mQgisApp = new QgisApp();
63 
64   mCanvas = new QgsMapCanvas();
65   mCanvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) );
66 
67   // make testing layers
68   mMultiLineStringLayer = new QgsVectorLayer( QStringLiteral( "MultiLineString?crs=EPSG:3946" ), QStringLiteral( "layer multiline" ), QStringLiteral( "memory" ) );
69   QVERIFY( mMultiLineStringLayer->isValid() );
70   mMultiLineStringLayer->startEditing();
71   lineF1.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "MultiLineString ((0 0, 10 0))" ) ) );
72   lineF2.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "MultiLineString ((0 5, 10 5),(10 5, 15 5))" ) ) );
73   mMultiLineStringLayer->addFeature( lineF1 );
74   mMultiLineStringLayer->addFeature( lineF2 );
75 
76   mPolygonLayer = new QgsVectorLayer( QStringLiteral( "Polygon?crs=EPSG:3946" ), QStringLiteral( "layer polygon" ), QStringLiteral( "memory" ) );
77   QVERIFY( mPolygonLayer->isValid() );
78   mPolygonLayer->startEditing();
79   polygonF1.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((0 5, 0 10, 10 10, 10 5, 0 5))" ) ) );
80   polygonF2.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((0 0, 0 5, 10 5, 10 0, 0 0))" ) ) );
81   mPolygonLayer->addFeature( polygonF1 );
82   mPolygonLayer->addFeature( polygonF2 );
83 
84   mMultiPolygonLayer = new QgsVectorLayer( QStringLiteral( "MultiPolygon?crs=EPSG:3946" ), QStringLiteral( "layer multipolygon" ), QStringLiteral( "memory" ) );
85   QVERIFY( mMultiPolygonLayer->isValid() );
86   mMultiPolygonLayer->startEditing();
87   multipolygonF1.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "MultiPolygon (((0 5, 0 10, 10 10, 10 5, 0 5)),((0 0, 0 4, 10 4, 10 0, 0 0)))" ) ) );
88   mMultiPolygonLayer->addFeature( multipolygonF1 );
89 
90   mCanvas->setFrameStyle( QFrame::NoFrame );
91   mCanvas->resize( 50, 50 );
92   mCanvas->setExtent( QgsRectangle( 0, 0, 10, 10 ) );
93   mCanvas->show(); // to make the canvas resize
94   mCanvas->hide();
95   // Disable flaky tests on windows...
96   // QCOMPARE( mCanvas->mapSettings().outputSize(), QSize( 50, 50 ) );
97   // QCOMPARE( mCanvas->mapSettings().visibleExtent(), QgsRectangle( 0, 0, 10, 10 ) );
98 
99   QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << mMultiLineStringLayer
100                                         << mPolygonLayer
101                                         << mMultiPolygonLayer );
102 
103   // set layers in canvas
104   mCanvas->setLayers( QList<QgsMapLayer *>() << mMultiLineStringLayer
105                       << mPolygonLayer
106                       << mMultiPolygonLayer );
107 
108 }
109 
cleanupTestCase()110 void TestQgsMapToolSplitFeatures::cleanupTestCase()
111 {
112   QgsApplication::exitQgis();
113 }
114 
mapToPoint(double x,double y)115 QPoint TestQgsMapToolSplitFeatures::mapToPoint( double x, double y )
116 {
117 
118   QgsPointXY mapPoint = mCanvas->mapSettings().mapToPixel().transform( x, y );
119 
120   return QPoint( std::round( mapPoint.x() ), std::round( mapPoint.y() ) );
121 }
122 
testNoFeaturesSplit()123 void TestQgsMapToolSplitFeatures::testNoFeaturesSplit()
124 {
125   mCanvas->setCurrentLayer( mMultiLineStringLayer );
126   QgsMapToolSplitFeatures *mapTool = new QgsMapToolSplitFeatures( mCanvas ) ;
127   mCanvas->setMapTool( mapTool );
128 
129   std::unique_ptr< QgsMapMouseEvent > event( new QgsMapMouseEvent(
130         mCanvas,
131         QEvent::MouseButtonRelease,
132         mapToPoint( 4, 7 ),
133         Qt::LeftButton
134       ) );
135   mapTool->cadCanvasReleaseEvent( event.get() );
136   event.reset( new QgsMapMouseEvent(
137                  mCanvas,
138                  QEvent::MouseButtonRelease,
139                  mapToPoint( 4, 8 ),
140                  Qt::LeftButton
141                ) );
142   mapTool->cadCanvasReleaseEvent( event.get() );
143 
144   event.reset( new QgsMapMouseEvent(
145                  mCanvas,
146                  QEvent::MouseButtonRelease,
147                  mapToPoint( 4, 8 ),
148                  Qt::RightButton
149                ) );
150   mapTool->cadCanvasReleaseEvent( event.get() );
151 
152 
153   QVERIFY( mMultiLineStringLayer->featureCount() == 2 );
154   QVERIFY( mMultiLineStringLayer->undoStack()->index() == 2 );
155 }
156 
testSplitPolygon()157 void TestQgsMapToolSplitFeatures::testSplitPolygon()
158 {
159   QgsProject::instance()->setTopologicalEditing( false );
160   mCanvas->setCurrentLayer( mPolygonLayer );
161   QgsMapToolSplitFeatures *mapTool = new QgsMapToolSplitFeatures( mCanvas ) ;
162   mCanvas->setMapTool( mapTool );
163 
164   std::unique_ptr< QgsMapMouseEvent > event( new QgsMapMouseEvent(
165         mCanvas,
166         QEvent::MouseButtonRelease,
167         mapToPoint( 4, 11 ),
168         Qt::LeftButton
169       ) );
170   mapTool->cadCanvasReleaseEvent( event.get() );
171   event.reset( new QgsMapMouseEvent(
172                  mCanvas,
173                  QEvent::MouseButtonRelease,
174                  mapToPoint( 4, 3 ),
175                  Qt::LeftButton
176                ) );
177   mapTool->cadCanvasReleaseEvent( event.get() );
178 
179   event.reset( new QgsMapMouseEvent(
180                  mCanvas,
181                  QEvent::MouseButtonRelease,
182                  mapToPoint( 4, 3 ),
183                  Qt::RightButton
184                ) );
185   mapTool->cadCanvasReleaseEvent( event.get() );
186 
187   QVERIFY( mPolygonLayer->undoStack()->index() == 3 );
188   QVERIFY( mPolygonLayer->featureCount() == 3 );
189   QCOMPARE( mPolygonLayer->getFeature( polygonF1.id() ).geometry().asWkt(), QStringLiteral( "Polygon ((4 10, 4 5, 0 5, 0 10, 4 10))" ) );
190   QCOMPARE( mPolygonLayer->getFeature( polygonF2.id() ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 0 5, 10 5, 10 0, 0 0))" ) );
191 
192   // no change to other layers
193   QVERIFY( mMultiLineStringLayer->undoStack()->index() == 2 );
194   QVERIFY( mMultiPolygonLayer->undoStack()->index() == 1 );
195 
196   // undo changes
197   mPolygonLayer->undoStack()->undo();
198   QVERIFY( mPolygonLayer->undoStack()->index() == 2 );
199 }
200 
testSplitPolygonTopologicalEditing()201 void TestQgsMapToolSplitFeatures::testSplitPolygonTopologicalEditing()
202 {
203   QgsProject::instance()->setTopologicalEditing( true );
204   mCanvas->setCurrentLayer( mPolygonLayer );
205   QgsMapToolSplitFeatures *mapTool = new QgsMapToolSplitFeatures( mCanvas ) ;
206   mCanvas->setMapTool( mapTool );
207 
208   std::unique_ptr< QgsMapMouseEvent > event( new QgsMapMouseEvent(
209         mCanvas,
210         QEvent::MouseButtonRelease,
211         mapToPoint( 4, 11 ),
212         Qt::LeftButton
213       ) );
214   mapTool->cadCanvasReleaseEvent( event.get() );
215   event.reset( new QgsMapMouseEvent(
216                  mCanvas,
217                  QEvent::MouseButtonRelease,
218                  mapToPoint( 4, 3 ),
219                  Qt::LeftButton
220                ) );
221   mapTool->cadCanvasReleaseEvent( event.get() );
222 
223   event.reset( new QgsMapMouseEvent(
224                  mCanvas,
225                  QEvent::MouseButtonRelease,
226                  mapToPoint( 4, 3 ),
227                  Qt::RightButton
228                ) );
229   mapTool->cadCanvasReleaseEvent( event.get() );
230 
231   QVERIFY( mPolygonLayer->undoStack()->index() == 3 );
232   QVERIFY( mPolygonLayer->featureCount() == 3 );
233   QCOMPARE( mPolygonLayer->getFeature( polygonF1.id() ).geometry().asWkt(), QStringLiteral( "Polygon ((4 10, 4 5, 0 5, 0 10, 4 10))" ) );
234   QCOMPARE( mPolygonLayer->getFeature( polygonF2.id() ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 0 5, 4 5, 10 5, 10 0, 0 0))" ) );
235 
236   QVERIFY( mMultiLineStringLayer->undoStack()->index() == 3 );
237   QCOMPARE( mMultiLineStringLayer->getFeature( lineF2.id() ).geometry().asWkt(), QStringLiteral( "MultiLineString ((0 5, 4 5, 10 5),(10 5, 15 5))" ) );
238   QVERIFY( mMultiPolygonLayer->undoStack()->index() == 2 );
239   QCOMPARE( mMultiPolygonLayer->getFeature( multipolygonF1.id() ).geometry().asWkt(), QStringLiteral( "MultiPolygon (((0 5, 0 10, 4 10, 10 10, 10 5, 4 5, 0 5)),((0 0, 0 4, 10 4, 10 0, 0 0)))" ) );
240 
241   // undo changes
242   mPolygonLayer->undoStack()->undo();
243   QVERIFY( mPolygonLayer->undoStack()->index() == 2 );
244   mMultiLineStringLayer->undoStack()->undo();
245   QVERIFY( mMultiLineStringLayer->undoStack()->index() == 2 );
246   mMultiPolygonLayer->undoStack()->undo();
247   QVERIFY( mMultiPolygonLayer->undoStack()->index() == 1 );
248 }
249 
250 QGSTEST_MAIN( TestQgsMapToolSplitFeatures )
251 #include "testqgsmaptoolsplitfeatures.moc"
252