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