1 /***************************************************************************
2      testqgsmaptoolreshape.cpp
3      --------------------------------
4     Date                 : 2017-12-1
5     Copyright            : (C) 2017 by Paul Blottiere
6     Email                : paul.blottiere@oslandia.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 "qgsadvanceddigitizingdockwidget.h"
20 #include "qgsgeometry.h"
21 #include "qgsmapcanvas.h"
22 #include "qgsmapcanvassnappingutils.h"
23 #include "qgssnappingconfig.h"
24 #include "qgssnappingutils.h"
25 #include "qgsmaptoolreshape.h"
26 #include "qgsproject.h"
27 #include "qgssettingsregistrycore.h"
28 #include "qgsvectorlayer.h"
29 #include "qgsmapmouseevent.h"
30 #include "testqgsmaptoolutils.h"
31 
32 
33 /**
34  * \ingroup UnitTests
35  * This is a unit test for the vertex tool
36  */
37 class TestQgsMapToolReshape: public QObject
38 {
39     Q_OBJECT
40   public:
41     TestQgsMapToolReshape();
42 
43   private slots:
44     void initTestCase();// will be called before the first testfunction is executed.
45     void cleanupTestCase();// will be called after the last testfunction was executed.
46 
47     void testReshapeZ();
48     void testTopologicalEditing();
49     void reshapeWithBindingLine();
50 
51   private:
52     QgisApp *mQgisApp = nullptr;
53     QgsMapCanvas *mCanvas = nullptr;
54     QgsMapToolReshape *mCaptureTool = nullptr;
55     QgsVectorLayer *mLayerLineZ = nullptr;
56     QgsVectorLayer *mLayerPointZ = nullptr;
57     QgsVectorLayer *mLayerPolygonZ = nullptr;
58     QgsVectorLayer *mLayerTopo = nullptr;
59 };
60 
61 TestQgsMapToolReshape::TestQgsMapToolReshape() = default;
62 
63 
64 //runs before all tests
initTestCase()65 void TestQgsMapToolReshape::initTestCase()
66 {
67   qDebug() << "TestMapToolCapture::initTestCase()";
68   // init QGIS's paths - true means that all path will be inited from prefix
69   QgsApplication::init();
70   QgsApplication::initQgis();
71 
72   // Set up the QSettings environment
73   QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
74   QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
75   QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
76 
77   mQgisApp = new QgisApp();
78 
79   mCanvas = new QgsMapCanvas();
80 
81   mCanvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) );
82 
83   mCanvas->setFrameStyle( QFrame::NoFrame );
84   mCanvas->resize( 512, 512 );
85   mCanvas->setExtent( QgsRectangle( 0, 0, 8, 8 ) );
86   mCanvas->show(); // to make the canvas resize
87   mCanvas->hide();
88 
89   // make testing layers
90   mLayerLineZ = new QgsVectorLayer( QStringLiteral( "LineStringZ?crs=EPSG:3946" ), QStringLiteral( "layer line Z" ), QStringLiteral( "memory" ) );
91   QVERIFY( mLayerLineZ->isValid() );
92   QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << mLayerLineZ );
93 
94   mLayerPointZ = new QgsVectorLayer( QStringLiteral( "PointZ?crs=EPSG:3946" ), QStringLiteral( "point Z" ), QStringLiteral( "memory" ) );
95   QVERIFY( mLayerPointZ->isValid() );
96   QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << mLayerPointZ );
97 
98   mLayerPolygonZ = new QgsVectorLayer( QStringLiteral( "PolygonZ?crs=EPSG:3946" ), QStringLiteral( "polygon Z" ), QStringLiteral( "memory" ) );
99   QVERIFY( mLayerPolygonZ->isValid() );
100   QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << mLayerPolygonZ );
101 
102   mLayerTopo = new QgsVectorLayer( QStringLiteral( "Polygon?crs=EPSG:3946" ), QStringLiteral( "topo" ), QStringLiteral( "memory" ) );
103   QVERIFY( mLayerTopo->isValid() );
104 
105   mLayerLineZ->startEditing();
106   const QString wkt1 = "LineStringZ (0 0 0, 1 1 0, 1 2 0)";
107   const QString wkt2 = "LineStringZ (2 1 5, 3 3 5)";
108   QgsFeature f1;
109   f1.setGeometry( QgsGeometry::fromWkt( wkt1 ) );
110   QgsFeature f2;
111   f2.setGeometry( QgsGeometry::fromWkt( wkt2 ) );
112 
113   QgsFeatureList flist;
114   flist << f1 << f2;
115   mLayerLineZ->dataProvider()->addFeatures( flist );
116   QCOMPARE( mLayerLineZ->featureCount(), ( long )2 );
117   QCOMPARE( mLayerLineZ->getFeature( 1 ).geometry().asWkt(), wkt1 );
118   QCOMPARE( mLayerLineZ->getFeature( 2 ).geometry().asWkt(), wkt2 );
119 
120   mLayerPointZ->startEditing();
121   const QString wkt3 = "PointZ (5 5 5)";
122   QgsFeature f3;
123   f3.setGeometry( QgsGeometry::fromWkt( wkt3 ) );
124   const QString wkt4 = "PointZ (6 6 6)";
125   QgsFeature f4;
126   f4.setGeometry( QgsGeometry::fromWkt( wkt4 ) );
127 
128   QgsFeatureList flistPoint;
129   flistPoint << f3 << f4;
130   mLayerPointZ->dataProvider()->addFeatures( flistPoint );
131   QCOMPARE( mLayerPointZ->featureCount(), ( long )2 );
132   QCOMPARE( mLayerPointZ->getFeature( 1 ).geometry().asWkt(), wkt3 );
133   QCOMPARE( mLayerPointZ->getFeature( 2 ).geometry().asWkt(), wkt4 );
134 
135   mLayerPolygonZ->startEditing();
136   const QString wkt5 = "PolygonZ ((7 5 4, 3 2 1, 0 1 2, 7 5 4))";
137   QgsFeature f5;
138   f5.setGeometry( QgsGeometry::fromWkt( wkt5 ) );
139   QgsFeatureList flistPolygon;
140   flistPolygon << f5;
141   mLayerPolygonZ->dataProvider()->addFeatures( flistPolygon );
142   QCOMPARE( mLayerPolygonZ->featureCount(), ( long )1 );
143   QCOMPARE( mLayerPolygonZ->getFeature( 1 ).geometry().asWkt(), wkt5 );
144 
145   mLayerTopo->startEditing();
146   const QString wkt6 = "Polygon ((0 0, 4 0, 4 4, 0 4))";
147   QgsFeature f6;
148   f6.setGeometry( QgsGeometry::fromWkt( wkt6 ) );
149   const QString wkt7 = "Polygon ((7 0, 8 0, 8 4, 7 4))";
150   QgsFeature f7;
151   f7.setGeometry( QgsGeometry::fromWkt( wkt7 ) );
152   QgsFeatureList flistTopo;
153   flistTopo << f6 << f7;
154   mLayerTopo->dataProvider()->addFeatures( flistTopo );
155   QCOMPARE( mLayerTopo->featureCount(), ( long )2 );
156   QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), wkt6 );
157   QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), wkt7 );
158 
159   QgsSnappingConfig cfg = mCanvas->snappingUtils()->config();
160   cfg.setMode( QgsSnappingConfig::AllLayers );
161   cfg.setTolerance( 100 );
162   cfg.setTypeFlag( static_cast<QgsSnappingConfig::SnappingTypeFlag>( QgsSnappingConfig::VertexFlag | QgsSnappingConfig::SegmentFlag ) );
163   cfg.setEnabled( true );
164   mCanvas->snappingUtils()->setConfig( cfg );
165   mCanvas->setLayers( QList<QgsMapLayer *>() << mLayerLineZ << mLayerPointZ << mLayerPolygonZ );
166   mCanvas->setCurrentLayer( mLayerLineZ );
167 
168   mCanvas->snappingUtils()->locatorForLayer( mLayerLineZ )->init();
169   mCanvas->snappingUtils()->locatorForLayer( mLayerPointZ )->init();
170   mCanvas->snappingUtils()->locatorForLayer( mLayerPolygonZ )->init();
171   mCanvas->snappingUtils()->locatorForLayer( mLayerTopo )->init();
172 
173   // create the tool
174   mCaptureTool = new QgsMapToolReshape( mCanvas );
175   mCanvas->setMapTool( mCaptureTool );
176 
177   QCOMPARE( mCanvas->mapSettings().outputSize(), QSize( 512, 512 ) );
178   QCOMPARE( mCanvas->mapSettings().visibleExtent(), QgsRectangle( 0, 0, 8, 8 ) );
179 }
180 
181 //runs after all tests
cleanupTestCase()182 void TestQgsMapToolReshape::cleanupTestCase()
183 {
184   delete mCaptureTool;
185   delete mCanvas;
186   QgsApplication::exitQgis();
187 }
188 
testReshapeZ()189 void TestQgsMapToolReshape::testReshapeZ()
190 {
191   TestQgsMapToolAdvancedDigitizingUtils utils( mCaptureTool );
192 
193   // test with default Z value = 333
194   QgsSettingsRegistryCore::settingsDigitizingDefaultZValue.setValue( 333 );
195 
196   // snap on a linestringz layer
197   utils.mouseClick( 1, 2, Qt::LeftButton, Qt::KeyboardModifiers(), true );
198   utils.mouseClick( 2, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
199   utils.mouseClick( 2, 1, Qt::RightButton );
200 
201   const QString wkt = "LineStringZ (0 0 0, 1 1 0, 1 2 0, 2 1 5)";
202   QCOMPARE( mLayerLineZ->getFeature( 1 ).geometry().asWkt(), wkt );
203 
204   // snap on a pointz layer
205   utils.mouseClick( 2, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
206   utils.mouseClick( 5, 5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
207   utils.mouseClick( 6, 6, Qt::LeftButton, Qt::KeyboardModifiers(), true );
208   utils.mouseClick( 6, 6, Qt::RightButton );
209 
210   const QString wkt2 = "LineStringZ (0 0 0, 1 1 0, 1 2 0, 2 1 5, 5 5 5, 6 6 6)";
211   QCOMPARE( mLayerLineZ->getFeature( 1 ).geometry().asWkt(), wkt2 );
212 
213   // snap on a polygonz layer
214   utils.mouseClick( 6, 6, Qt::LeftButton, Qt::KeyboardModifiers(), true );
215   utils.mouseClick( 7, 5, Qt::LeftButton, Qt::KeyboardModifiers(), true );
216   utils.mouseClick( 3, 2, Qt::LeftButton, Qt::KeyboardModifiers(), true );
217   utils.mouseClick( 3, 2, Qt::RightButton );
218 
219   const QString wkt3 = "LineStringZ (0 0 0, 1 1 0, 1 2 0, 2 1 5, 5 5 5, 6 6 6, 7 5 4, 3 2 1)";
220   QCOMPARE( mLayerLineZ->getFeature( 1 ).geometry().asWkt(), wkt3 );
221   mLayerLineZ->undoStack()->undo();
222 
223 }
224 
testTopologicalEditing()225 void TestQgsMapToolReshape::testTopologicalEditing()
226 {
227   QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << mLayerTopo );
228   mCanvas->setLayers( QList<QgsMapLayer *>() << mLayerTopo );
229 
230   const bool topologicalEditing = QgsProject::instance()->topologicalEditing();
231   QgsProject::instance()->setTopologicalEditing( true );
232   mCanvas->setCurrentLayer( mLayerTopo );
233   TestQgsMapToolAdvancedDigitizingUtils utils( mCaptureTool );
234 
235   // test with default Z value = 333
236   QgsSettingsRegistryCore::settingsDigitizingDefaultZValue.setValue( 333 );
237 
238   utils.mouseClick( 4, 4, Qt::LeftButton, Qt::KeyboardModifiers(), true );
239   utils.mouseClick( 7, 2, Qt::LeftButton, Qt::KeyboardModifiers(), true );
240   utils.mouseClick( 4, 0, Qt::LeftButton, Qt::KeyboardModifiers(), true );
241   utils.mouseClick( 4, 0, Qt::RightButton );
242 
243   const QString wkt = "Polygon ((4 0, 8 2, 4 4, 0 4, 0 0, 4 0))";
244   const QString wkt2 = "Polygon ((7 0, 8 0, 8 2, 8 4, 7 4))";
245 
246   QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), wkt );
247   QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), wkt2 );
248 
249   QgsProject::instance()->setTopologicalEditing( topologicalEditing );
250 
251   mLayerTopo->undoStack()->undo();
252 }
253 
reshapeWithBindingLine()254 void TestQgsMapToolReshape::reshapeWithBindingLine()
255 {
256   // prepare vector layer
257   std::unique_ptr<QgsVectorLayer> vl;
258   vl.reset( new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326&field=name:string(20)" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
259 
260   const QgsGeometry g0 = QgsGeometry::fromWkt( "LineString (0 0, 1 1, 1 2)" );
261   QgsFeature f0;
262   f0.setGeometry( g0 );
263   f0.setAttribute( 0, "polyline0" );
264 
265   const QgsGeometry g1 = QgsGeometry::fromWkt( "LineString (2 1, 3 2, 3 3, 2 2)" );
266   QgsFeature f1;
267   f1.setGeometry( g1 );
268   f1.setAttribute( 0, "polyline1" );
269 
270   vl->dataProvider()->addFeatures( QgsFeatureList() << f0 << f1 );
271 
272   // prepare canvas
273   QList<QgsMapLayer *> layers;
274   layers.append( vl.get() );
275 
276   const QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:4326" ) );
277   mQgisApp->mapCanvas()->setDestinationCrs( srs );
278   mQgisApp->mapCanvas()->setLayers( layers );
279   mQgisApp->mapCanvas()->setCurrentLayer( vl.get() );
280 
281   // reshape to add line to polyline0
282   QgsLineString cl0;
283   cl0.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 1 ) );
284 
285   const QgsCompoundCurve curve0( *cl0.toCurveType() );
286 
287   QgsMapToolReshape tool0( mQgisApp->mapCanvas() );
288   tool0.addCurve( curve0.clone() );
289 
290   vl->startEditing();
291   tool0.reshape( vl.get() );
292 
293   f0 = vl->getFeature( 1 );
294   QCOMPARE( f0.geometry().asWkt(), QStringLiteral( "LineString (0 0, 1 1, 1 2, 2 1)" ) );
295 
296   f1 = vl->getFeature( 2 );
297   QCOMPARE( f1.geometry().asWkt(), QStringLiteral( "LineString (2 1, 3 2, 3 3, 2 2)" ) );
298 
299   vl->rollBack();
300 
301   // reshape to add line to polyline1
302   QgsLineString cl1;
303   cl1.setPoints( QgsPointSequence() << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) );
304 
305   const QgsCompoundCurve curve1( *cl1.toCurveType() );
306 
307   QgsMapToolReshape tool1( mQgisApp->mapCanvas() );
308   tool1.addCurve( curve1.clone() );
309 
310   vl->startEditing();
311   tool1.reshape( vl.get() );
312 
313   f0 = vl->getFeature( 1 );
314   QCOMPARE( f0.geometry().asWkt(), QStringLiteral( "LineString (0 0, 1 1, 1 2)" ) );
315 
316   f1 = vl->getFeature( 2 );
317   QCOMPARE( f1.geometry().asWkt(), QStringLiteral( "LineString (1 2, 2 1, 3 2, 3 3, 2 2)" ) );
318 
319   vl->rollBack();
320 }
321 
322 
323 QGSTEST_MAIN( TestQgsMapToolReshape )
324 #include "testqgsmaptoolreshape.moc"
325