1 /***************************************************************************
2      testqgsmeasuretool.cpp
3      ----------------------
4     Date                 : 2016-02-14
5     Copyright            : (C) 2016 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 "qgstest.h"
16 #include "qgisapp.h"
17 #include "qgsapplication.h"
18 #include "qgsvectorlayer.h"
19 #include "qgsfeature.h"
20 #include "qgsgeometry.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsmeasuretool.h"
23 #include "qgsmeasuredialog.h"
24 #include "qgsproject.h"
25 #include "qgsmapcanvas.h"
26 #include "qgsunittypes.h"
27 
28 /**
29  * \ingroup UnitTests
30  * This is a unit test for the measure tool
31  */
32 class TestQgsMeasureTool : public QObject
33 {
34     Q_OBJECT
35   public:
36     TestQgsMeasureTool();
37 
38   private slots:
39     void initTestCase();// will be called before the first testfunction is executed.
40     void cleanupTestCase();// will be called after the last testfunction was executed.
init()41     void init() {} // will be called before each testfunction is executed.
cleanup()42     void cleanup() {} // will be called after every testfunction.
43     void testLengthCalculationCartesian();
44     void testLengthCalculationProjected();
45     void testLengthCalculationNoCrs();
46     void testAreaCalculationCartesian();
47     void testAreaCalculationProjected();
48     void degreeDecimalPlaces();
49 
50   private:
51     QgisApp *mQgisApp = nullptr;
52     QgsMapCanvas *mCanvas = nullptr;
53 };
54 
55 TestQgsMeasureTool::TestQgsMeasureTool() = default;
56 
57 //runs before all tests
initTestCase()58 void TestQgsMeasureTool::initTestCase()
59 {
60   qDebug() << "TestQgisAppClipboard::initTestCase()";
61   // init QGIS's paths - true means that all path will be inited from prefix
62   QgsApplication::init();
63   QgsApplication::initQgis();
64 
65   // Set up the QgsSettings environment
66   QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
67   QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
68   QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
69 
70   mQgisApp = new QgisApp();
71   mCanvas = new QgsMapCanvas();
72 
73   // enforce C locale because the tests expect it
74   // (decimal separators / thousand separators)
75   QLocale::setDefault( QLocale::c() );
76 }
77 
78 //runs after all tests
cleanupTestCase()79 void TestQgsMeasureTool::cleanupTestCase()
80 {
81   delete mCanvas;
82   QgsApplication::exitQgis();
83 }
84 
testLengthCalculationCartesian()85 void TestQgsMeasureTool::testLengthCalculationCartesian()
86 {
87   //test length measurement
88   QgsSettings s;
89   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
90 
91   // set project CRS and ellipsoid
92   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
93   mCanvas->setDestinationCrs( srs );
94   QgsProject::instance()->setCrs( srs );
95   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
96   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceMeters );
97 
98   // run length calculation
99   std::unique_ptr< QgsMeasureTool > tool( new QgsMeasureTool( mCanvas, false ) );
100   std::unique_ptr< QgsMeasureDialog > dlg( new QgsMeasureDialog( tool.get() ) );
101 
102   dlg->mCartesian->setChecked( true );
103 
104   tool->restart();
105   tool->addPoint( QgsPointXY( 2484588, 2425722 ) );
106   tool->addPoint( QgsPointXY( 2482767, 2398853 ) );
107   //force dialog recalculation
108   dlg->addPoint();
109 
110   // check result
111   QString measureString = dlg->editTotal->text();
112   double measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
113   double expected = 26930.637;
114   QGSCOMPARENEAR( measured, expected, 0.001 );
115 
116   // change project length unit, check calculation respects unit
117   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceFeet );
118   std::unique_ptr< QgsMeasureTool > tool2( new QgsMeasureTool( mCanvas, false ) );
119   std::unique_ptr< QgsMeasureDialog > dlg2( new QgsMeasureDialog( tool2.get() ) );
120   dlg2->mCartesian->setChecked( true );
121 
122   tool2->restart();
123   tool2->addPoint( QgsPointXY( 2484588, 2425722 ) );
124   tool2->addPoint( QgsPointXY( 2482767, 2398853 ) );
125   //force dialog recalculation
126   dlg2->addPoint();
127 
128   // check result
129   measureString = dlg2->editTotal->text();
130   measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
131   expected = 88355.108;
132   QGSCOMPARENEAR( measured, expected, 0.001 );
133 
134   // check new CoordinateReferenceSystem, points must be reprojected to paint them successfully (issue #15182)
135   QgsCoordinateReferenceSystem srs2( QStringLiteral( "EPSG:4326" ) );
136 
137   QgsCoordinateTransform ct( srs, srs2, QgsProject::instance() );
138 
139   QgsPointXY p0 = ct.transform( tool2->points()[0] );
140   QgsPointXY p1 = ct.transform( tool2->points()[1] );
141 
142   mCanvas->setDestinationCrs( srs2 );
143 
144   QgsPointXY n0 = tool2->points()[0];
145   QgsPointXY n1 = tool2->points()[1];
146 
147   QGSCOMPARENEAR( p0.x(), n0.x(), 0.001 );
148   QGSCOMPARENEAR( p0.y(), n0.y(), 0.001 );
149   QGSCOMPARENEAR( p1.x(), n1.x(), 0.001 );
150   QGSCOMPARENEAR( p1.y(), n1.y(), 0.001 );
151 
152 }
testLengthCalculationProjected()153 void TestQgsMeasureTool::testLengthCalculationProjected()
154 {
155   //test length measurement
156   QgsSettings s;
157   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
158 
159   // set project CRS and ellipsoid
160   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
161   mCanvas->setDestinationCrs( srs );
162   QgsProject::instance()->setCrs( srs );
163   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
164   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceMeters );
165 
166   // run length calculation
167   std::unique_ptr< QgsMeasureTool > tool( new QgsMeasureTool( mCanvas, false ) );
168   std::unique_ptr< QgsMeasureDialog > dlg( new QgsMeasureDialog( tool.get() ) );
169   dlg->mEllipsoidal->setChecked( true );
170 
171   tool->restart();
172   tool->addPoint( QgsPointXY( 2484588, 2425722 ) );
173   tool->addPoint( QgsPointXY( 2482767, 2398853 ) );
174   //force dialog recalculation
175   dlg->addPoint();
176 
177   // check result
178   QString measureString = dlg->editTotal->text();
179   double measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
180   double expected = 26932.156;
181   QGSCOMPARENEAR( measured, expected, 0.001 );
182 
183   // change project length unit, check calculation respects unit
184   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceFeet );
185   std::unique_ptr< QgsMeasureTool > tool2( new QgsMeasureTool( mCanvas, false ) );
186   std::unique_ptr< QgsMeasureDialog > dlg2( new QgsMeasureDialog( tool2.get() ) );
187   dlg2->mEllipsoidal->setChecked( true );
188 
189   tool2->restart();
190   tool2->addPoint( QgsPointXY( 2484588, 2425722 ) );
191   tool2->addPoint( QgsPointXY( 2482767, 2398853 ) );
192   //force dialog recalculation
193   dlg2->addPoint();
194 
195   // check result
196   measureString = dlg2->editTotal->text();
197   measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
198   expected = 88360.0918635;
199   QGSCOMPARENEAR( measured, expected, 0.001 );
200 
201   // check new CoordinateReferenceSystem, points must be reprojected to paint them successfully (issue #15182)
202   QgsCoordinateReferenceSystem srs2( QStringLiteral( "EPSG:4326" ) );
203 
204   QgsCoordinateTransform ct( srs, srs2, QgsProject::instance() );
205 
206   QgsPointXY p0 = ct.transform( tool2->points()[0] );
207   QgsPointXY p1 = ct.transform( tool2->points()[1] );
208 
209   mCanvas->setDestinationCrs( srs2 );
210 
211   QgsPointXY n0 = tool2->points()[0];
212   QgsPointXY n1 = tool2->points()[1];
213 
214   QGSCOMPARENEAR( p0.x(), n0.x(), 0.001 );
215   QGSCOMPARENEAR( p0.y(), n0.y(), 0.001 );
216   QGSCOMPARENEAR( p1.x(), n1.x(), 0.001 );
217   QGSCOMPARENEAR( p1.y(), n1.y(), 0.001 );
218 }
219 
220 
testLengthCalculationNoCrs()221 void TestQgsMeasureTool::testLengthCalculationNoCrs()
222 {
223   // test length measurement when no projection is set
224   QSettings s;
225   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
226 
227   // set project CRS and ellipsoid
228   mCanvas->setDestinationCrs( QgsCoordinateReferenceSystem() );
229   QgsProject::instance()->setCrs( QgsCoordinateReferenceSystem() );
230 
231   // run length calculation
232   std::unique_ptr< QgsMeasureTool > tool( new QgsMeasureTool( mCanvas, false ) );
233   std::unique_ptr< QgsMeasureDialog > dlg( new QgsMeasureDialog( tool.get() ) );
234 
235   tool->restart();
236   tool->addPoint( QgsPointXY( 2484588, 2425722 ) );
237   tool->addPoint( QgsPointXY( 2482767, 2398853 ) );
238   //force dialog recalculation
239   dlg->addPoint();
240 
241   // check result
242   QString measureString = dlg->editTotal->text();
243   double measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
244   double expected = 26930.63686584482;
245   QGSCOMPARENEAR( measured, expected, 0.001 );
246 }
247 
testAreaCalculationCartesian()248 void TestQgsMeasureTool::testAreaCalculationCartesian()
249 {
250   //test area measurement
251   QgsSettings s;
252   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
253 
254   // set project CRS and ellipsoid
255   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
256   mCanvas->setDestinationCrs( srs );
257   QgsProject::instance()->setCrs( srs );
258   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
259   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMeters );
260 
261   // run length calculation
262   std::unique_ptr< QgsMeasureTool > tool( new QgsMeasureTool( mCanvas, true ) );
263   std::unique_ptr< QgsMeasureDialog > dlg( new QgsMeasureDialog( tool.get() ) );
264 
265   dlg->mCartesian->setChecked( true );
266 
267   tool->restart();
268   tool->addPoint( QgsPointXY( 2484588, 2425722 ) );
269   tool->addPoint( QgsPointXY( 2482767, 2398853 ) );
270   tool->addPoint( QgsPointXY( 2520109, 2397715 ) );
271   tool->addPoint( QgsPointXY( 2520792, 2425494 ) );
272   //force dialog recalculation
273   dlg->addPoint();
274 
275   // check result
276   QString measureString = dlg->editTotal->text();
277   double measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
278   double expected = 1005640568.0;
279   QGSCOMPARENEAR( measured, expected, 1.0 );
280 
281   // change project area unit, check calculation respects unit
282   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMiles );
283   std::unique_ptr< QgsMeasureTool > tool2( new QgsMeasureTool( mCanvas, true ) );
284   std::unique_ptr< QgsMeasureDialog > dlg2( new QgsMeasureDialog( tool2.get() ) );
285   dlg2->mCartesian->setChecked( true );
286 
287   tool2->restart();
288   tool2->addPoint( QgsPointXY( 2484588, 2425722 ) );
289   tool2->addPoint( QgsPointXY( 2482767, 2398853 ) );
290   tool2->addPoint( QgsPointXY( 2520109, 2397715 ) );
291   tool2->addPoint( QgsPointXY( 2520792, 2425494 ) );
292   //force dialog recalculation
293   dlg2->addPoint();
294 
295   // check result
296   measureString = dlg2->editTotal->text();
297   measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
298   expected = 388.280;
299   QGSCOMPARENEAR( measured, expected, 0.001 );
300 }
301 
testAreaCalculationProjected()302 void TestQgsMeasureTool::testAreaCalculationProjected()
303 {
304   //test area measurement
305   QgsSettings s;
306   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
307 
308   // set project CRS and ellipsoid
309   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
310   mCanvas->setDestinationCrs( srs );
311   QgsProject::instance()->setCrs( srs );
312   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
313   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMeters );
314 
315   // run length calculation
316   std::unique_ptr< QgsMeasureTool > tool( new QgsMeasureTool( mCanvas, true ) );
317   std::unique_ptr< QgsMeasureDialog > dlg( new QgsMeasureDialog( tool.get() ) );
318 
319   dlg->mEllipsoidal->setChecked( true );
320 
321   tool->restart();
322   tool->addPoint( QgsPointXY( 2484588, 2425722 ) );
323   tool->addPoint( QgsPointXY( 2482767, 2398853 ) );
324   tool->addPoint( QgsPointXY( 2520109, 2397715 ) );
325   tool->addPoint( QgsPointXY( 2520792, 2425494 ) );
326   //force dialog recalculation
327   dlg->addPoint();
328 
329   // check result
330   QString measureString = dlg->editTotal->text();
331   double measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
332   double expected = 1005721496.780000;
333   QGSCOMPARENEAR( measured, expected, 1.0 );
334 
335   // change project area unit, check calculation respects unit
336   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMiles );
337   std::unique_ptr< QgsMeasureTool > tool2( new QgsMeasureTool( mCanvas, true ) );
338   std::unique_ptr< QgsMeasureDialog > dlg2( new QgsMeasureDialog( tool2.get() ) );
339 
340   dlg2->mEllipsoidal->setChecked( true );
341 
342   tool2->restart();
343   tool2->addPoint( QgsPointXY( 2484588, 2425722 ) );
344   tool2->addPoint( QgsPointXY( 2482767, 2398853 ) );
345   tool2->addPoint( QgsPointXY( 2520109, 2397715 ) );
346   tool2->addPoint( QgsPointXY( 2520792, 2425494 ) );
347   //force dialog recalculation
348   dlg2->addPoint();
349 
350   // check result
351   measureString = dlg2->editTotal->text();
352   measured = measureString.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
353   expected = 388.311000;
354   QGSCOMPARENEAR( measured, expected, 0.001 );
355 }
356 
degreeDecimalPlaces()357 void TestQgsMeasureTool::degreeDecimalPlaces()
358 {
359   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceDegrees );
360 
361   QgsSettings s;
362   s.setValue( QStringLiteral( "qgis/measure/decimalplaces" ), 3 );
363 
364   std::unique_ptr< QgsMeasureTool > tool( new QgsMeasureTool( mCanvas, true ) );
365   std::unique_ptr< QgsMeasureDialog > dlg( new QgsMeasureDialog( tool.get() ) );
366 
367   QCOMPARE( dlg->formatDistance( 11, false ), QString( "11.000 deg" ) );
368   QCOMPARE( dlg->formatDistance( 0.005, false ), QString( "0.005 deg" ) );
369   QCOMPARE( dlg->formatDistance( 0.002, false ), QString( "0.0020 deg" ) );
370   QCOMPARE( dlg->formatDistance( 0.001, false ), QString( "0.0010 deg" ) );
371   QCOMPARE( dlg->formatDistance( 0.0001, false ), QString( "0.00010 deg" ) );
372   QCOMPARE( dlg->formatDistance( 0.00001, false ), QString( "0.000010 deg" ) );
373 
374 }
375 
376 QGSTEST_MAIN( TestQgsMeasureTool )
377 #include "testqgsmeasuretool.moc"
378