1 /***************************************************************************
2      testqgsmaptoolidentifyaction.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 
16 #include "qgstest.h"
17 
18 #include "qgsapplication.h"
19 #include "qgsvectorlayer.h"
20 #include "qgsrasterlayer.h"
21 #include "qgsfeature.h"
22 #include "qgsgeometry.h"
23 #include "qgsvectordataprovider.h"
24 #include "qgsvectortilelayer.h"
25 #include "qgsproject.h"
26 #include "qgsmapcanvas.h"
27 #include "qgsmeshlayer.h"
28 #include "qgsunittypes.h"
29 #include "qgsmaptoolidentifyaction.h"
30 #include "qgssettings.h"
31 #include "qgsidentifymenu.h"
32 #include "qgisapp.h"
33 #include "qgsaction.h"
34 #include "qgsactionmanager.h"
35 #include "qgsactionmenu.h"
36 #include "qgsidentifyresultsdialog.h"
37 #include "qgsmapmouseevent.h"
38 #include "qgsmaplayertemporalproperties.h"
39 #include "qgsmeshlayertemporalproperties.h"
40 
41 #include <QTimer>
42 
43 #include "cpl_conv.h"
44 
45 class TestQgsMapToolIdentifyAction : public QObject
46 {
47     Q_OBJECT
48   public:
49     TestQgsMapToolIdentifyAction() = default;
50 
51   private slots:
52     void initTestCase(); // will be called before the first testfunction is executed.
53     void cleanupTestCase(); // will be called after the last testfunction was executed.
54     void init(); // will be called before each testfunction is executed.
55     void cleanup(); // will be called after every testfunction.
56     void lengthCalculation(); //test calculation of derived length attributes
57     void perimeterCalculation(); //test calculation of derived perimeter attribute
58     void areaCalculation(); //test calculation of derived area attribute
59     void identifyRasterFloat32(); // test pixel identification and decimal precision
60     void identifyRasterFloat64(); // test pixel identification and decimal precision
61     void identifyMesh(); // test identification for mesh layer
62     void identifyVectorTile();  // test identification for vector tile layer
63     void identifyInvalidPolygons(); // test selecting invalid polygons
64     void clickxy(); // test if click_x and click_y variables are propagated
65     void closestPoint();
66 
67   private:
68     void doAction();
69 
70     QgsMapCanvas *canvas = nullptr;
71     QgsMapToolIdentifyAction *mIdentifyAction = nullptr;
72     QgisApp *mQgisApp = nullptr;
73 
74     QString testIdentifyRaster( QgsRasterLayer *layer, double xGeoref, double yGeoref );
75     QList<QgsMapToolIdentify::IdentifyResult> testIdentifyVector( QgsVectorLayer *layer, double xGeoref, double yGeoref );
76     QList<QgsMapToolIdentify::IdentifyResult> testIdentifyMesh( QgsMeshLayer *layer, double xGeoref, double yGeoref );
77     QList<QgsMapToolIdentify::IdentifyResult> testIdentifyVectorTile( QgsVectorTileLayer *layer, double xGeoref, double yGeoref );
78 
79     // Release return with delete []
80     unsigned char *
hex2bytes(const char * hex,int * size)81     hex2bytes( const char *hex, int *size )
82     {
83       QByteArray ba = QByteArray::fromHex( hex );
84       unsigned char *out = new unsigned char[ba.size()];
85       memcpy( out, ba.data(), ba.size() );
86       *size = ba.size();
87       return out;
88     }
89 
90     // TODO: make this a QgsGeometry member...
geomFromHexWKB(const char * hexwkb)91     QgsGeometry geomFromHexWKB( const char *hexwkb )
92     {
93       int wkbsize;
94       unsigned char *wkb = hex2bytes( hexwkb, &wkbsize );
95       QgsGeometry geom;
96       // NOTE: QgsGeometry takes ownership of wkb
97       geom.fromWkb( wkb, wkbsize );
98       return geom;
99     }
100 };
101 
initTestCase()102 void TestQgsMapToolIdentifyAction::initTestCase()
103 {
104   QgsApplication::init();
105   QgsApplication::initQgis();
106   // Set up the QgsSettings environment
107   QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
108   QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
109   QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
110 
111   QgsApplication::showSettings();
112 
113   // enforce C locale because the tests expect it
114   // (decimal separators / thousand separators)
115   QLocale::setDefault( QLocale::c() );
116 
117   mQgisApp = new QgisApp();
118 }
119 
cleanupTestCase()120 void TestQgsMapToolIdentifyAction::cleanupTestCase()
121 {
122   QgsApplication::exitQgis();
123 }
124 
init()125 void TestQgsMapToolIdentifyAction::init()
126 {
127   canvas = new QgsMapCanvas();
128 }
129 
cleanup()130 void TestQgsMapToolIdentifyAction::cleanup()
131 {
132   delete canvas;
133 }
134 
doAction()135 void TestQgsMapToolIdentifyAction::doAction()
136 {
137   bool ok = false;
138   int clickxOk = 2484588;
139   int clickyOk = 2425722;
140 
141   // test QActionMenu
142   QList<QAction *> actions = mIdentifyAction->identifyMenu()->actions();
143   bool testDone = false;
144 
145   for ( int i = 0; i < actions.count(); i++ )
146   {
147     if ( actions[i]->text().compare( "MyAction" ) == 0 )
148     {
149       QgsActionMenu::ActionData data = actions[i]->data().value<QgsActionMenu::ActionData>();
150       QgsAction act = data.actionData.value<QgsAction>();
151 
152       int clickx = act.expressionContextScope().variable( "click_x" ).toString().toInt( &ok, 10 );
153       QCOMPARE( clickx, clickxOk );
154 
155       int clicky = act.expressionContextScope().variable( "click_y" ).toString().toInt( &ok, 10 );
156       QCOMPARE( clicky, clickyOk );
157 
158       testDone = true;
159     }
160   }
161 
162   QCOMPARE( testDone, true );
163 
164   // test QgsIdentifyResultsDialog expression context scope
165   QgsIdentifyResultsDialog *dlg = mIdentifyAction->resultsDialog();
166   int clickx = dlg->expressionContextScope().variable( "click_x" ).toString().toInt( &ok, 10 );
167   QCOMPARE( clickx, clickxOk );
168 
169   int clicky = dlg->expressionContextScope().variable( "click_y" ).toString().toInt( &ok, 10 );
170   QCOMPARE( clicky, clickyOk );
171 
172   // close
173   mIdentifyAction->identifyMenu()->close();
174 }
175 
clickxy()176 void TestQgsMapToolIdentifyAction::clickxy()
177 {
178   // create temp layer
179   std::unique_ptr< QgsVectorLayer> tempLayer( new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:3111" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
180   QVERIFY( tempLayer->isValid() );
181 
182   // add feature
183   QgsFeature f1( tempLayer->dataProvider()->fields(), 1 );
184   QgsPointXY wordPoint( 2484588, 2425722 );
185   QgsGeometry geom = QgsGeometry::fromPointXY( wordPoint ) ;
186   f1.setGeometry( geom );
187   tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
188 
189   // prepare canvas
190   QList<QgsMapLayer *> layers;
191   layers.append( tempLayer.get() );
192 
193   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
194   canvas->setDestinationCrs( srs );
195   canvas->setLayers( layers );
196   canvas->setCurrentLayer( tempLayer.get() );
197 
198   // create/add action
199   QgsAction act( QgsAction::GenericPython, "MyAction", "", true );
200 
201   QSet<QString> scopes;
202   scopes << "Feature";
203   act.setActionScopes( scopes );
204   tempLayer->actions()->addAction( act );
205 
206   // init map tool identify action
207   mIdentifyAction = new QgsMapToolIdentifyAction( canvas );
208 
209   // simulate a click on the canvas
210   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( 2484588, 2425722 );
211   QPoint point = QPoint( mapPoint.x(), mapPoint.y() );
212   QMouseEvent releases( QEvent::MouseButtonRelease, point,
213                         Qt::RightButton, Qt::LeftButton, Qt::NoModifier );
214   QgsMapMouseEvent mapReleases( nullptr, &releases );
215 
216   // simulate a click on the corresponding action
217   QTimer::singleShot( 2000, this, &TestQgsMapToolIdentifyAction::doAction );
218   mIdentifyAction->canvasReleaseEvent( &mapReleases );
219 }
220 
closestPoint()221 void TestQgsMapToolIdentifyAction::closestPoint()
222 {
223   QgsSettings s;
224   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
225 
226   //create a temporary layer
227   std::unique_ptr< QgsVectorLayer> tempLayer( new QgsVectorLayer( QStringLiteral( "LineStringZM?crs=epsg:3111&field=pk:int&field=col1:double" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
228   QVERIFY( tempLayer->isValid() );
229   QgsFeature f1( tempLayer->dataProvider()->fields(), 1 );
230   f1.setAttribute( QStringLiteral( "pk" ), 1 );
231   f1.setAttribute( QStringLiteral( "col1" ), 0.0 );
232   QgsPolylineXY line3111;
233   line3111 << QgsPointXY( 2484588, 2425722 ) << QgsPointXY( 2482767, 2398853 );
234   QgsGeometry line3111G = QgsGeometry::fromWkt( QStringLiteral( "LineStringZM( 2484588 2425722 11 31, 2484588 2398853 15 37)" ) ) ;
235   f1.setGeometry( line3111G );
236   tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
237 
238   // set project CRS and ellipsoid
239   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
240   canvas->setDestinationCrs( srs );
241   canvas->setExtent( f1.geometry().boundingBox() );
242   QgsProject::instance()->setCrs( srs );
243   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
244   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceMeters );
245 
246   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( 2484587, 2399800 );
247 
248   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
249 
250   //check that closest point attributes are present
251   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
252   QCOMPARE( result.length(), 1 );
253   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Closest X" )], QStringLiteral( "2484588" ) );
254   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Closest Y" )], QStringLiteral( "2399800" ) );
255   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Interpolated M" )].left( 4 ), QStringLiteral( "36.7" ) );
256   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Interpolated Z" )].left( 4 ), QStringLiteral( "14.8" ) );
257 
258   // polygons
259   //create a temporary layer
260   std::unique_ptr< QgsVectorLayer> tempLayer2( new QgsVectorLayer( QStringLiteral( "PolygonZM?crs=epsg:3111&field=pk:int&field=col1:double" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
261   QVERIFY( tempLayer2->isValid() );
262   f1 = QgsFeature( tempLayer2->dataProvider()->fields(), 1 );
263   f1.setAttribute( QStringLiteral( "pk" ), 1 );
264   f1.setAttribute( QStringLiteral( "col1" ), 0.0 );
265 
266   f1.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "PolygonZM((2484588 2425722 1 11, 2484588 2398853 2 12, 2520109 2397715 3 13, 2520792 2425494 4 14, 2484588 2425722 1 11))" ) ) );
267   QVERIFY( f1.hasGeometry() );
268   tempLayer2->dataProvider()->addFeatures( QgsFeatureList() << f1 );
269 
270   mapPoint = canvas->getCoordinateTransform()->transform( 2484589, 2399800 );
271   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer2.get() );
272   QCOMPARE( result.length(), 1 );
273   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Closest X" )], QStringLiteral( "2484588" ) );
274   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Closest Y" )], QStringLiteral( "2399800" ) );
275   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Interpolated M" )].left( 4 ), QStringLiteral( "11.9" ) );
276   QCOMPARE( result.at( 0 ).mDerivedAttributes[tr( "Interpolated Z" )].left( 4 ), QStringLiteral( "1.96" ) );
277 }
278 
lengthCalculation()279 void TestQgsMapToolIdentifyAction::lengthCalculation()
280 {
281   QgsSettings s;
282   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
283 
284   //create a temporary layer
285   std::unique_ptr< QgsVectorLayer> tempLayer( new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:3111&field=pk:int&field=col1:double" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
286   QVERIFY( tempLayer->isValid() );
287   QgsFeature f1( tempLayer->dataProvider()->fields(), 1 );
288   f1.setAttribute( QStringLiteral( "pk" ), 1 );
289   f1.setAttribute( QStringLiteral( "col1" ), 0.0 );
290   QgsPolylineXY line3111;
291   line3111 << QgsPointXY( 2484588, 2425722 ) << QgsPointXY( 2482767, 2398853 );
292   QgsGeometry line3111G = QgsGeometry::fromPolylineXY( line3111 ) ;
293   f1.setGeometry( line3111G );
294   tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
295 
296   // set project CRS and ellipsoid
297   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
298   canvas->setDestinationCrs( srs );
299   canvas->setExtent( f1.geometry().boundingBox() );
300   QgsProject::instance()->setCrs( srs );
301   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
302   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceMeters );
303 
304   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( 2484588, 2425722 );
305 
306   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
307   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
308   QCOMPARE( result.length(), 1 );
309   QString derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Ellipsoidal — WGS84)" )];
310   double length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
311   QGSCOMPARENEAR( length, 26932.2, 0.1 );
312   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Cartesian)" )];
313   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
314   QGSCOMPARENEAR( length, 26930.6, 0.1 );
315 
316   //check that project units are respected
317   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceFeet );
318   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
319   QCOMPARE( result.length(), 1 );
320   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Ellipsoidal — WGS84)" )];
321   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
322   QGSCOMPARENEAR( length, 88360.1, 0.1 );
323   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Cartesian)" )];
324   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
325   QGSCOMPARENEAR( length, 88355.1, 0.1 );
326 
327   //test unchecked "keep base units" setting
328   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), false );
329   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
330   QCOMPARE( result.length(), 1 );
331   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Ellipsoidal — WGS84)" )];
332   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
333   QGSCOMPARENEAR( length, 16.735, 0.001 );
334   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Cartesian)" )];
335   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
336   QGSCOMPARENEAR( length, 16.734000, 0.001 );
337 
338   // no conversion of Cartesian lengths between unit types
339   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
340   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceDegrees );
341   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
342   QCOMPARE( result.length(), 1 );
343   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Ellipsoidal — WGS84)" )];
344   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
345   QGSCOMPARENEAR( length, 0.242000, 0.001 );
346   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Cartesian)" )];
347   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
348   QGSCOMPARENEAR( length, 26930.6, 0.1 );
349 
350   // LineString with Z
351   tempLayer = qgis::make_unique< QgsVectorLayer>( QStringLiteral( "LineStringZ?crs=epsg:3111&field=pk:int&field=col1:double" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
352   QVERIFY( tempLayer->isValid() );
353   f1.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineStringZ(2484588 2425722 10, 2482767 2398853 1000)" ) ) );
354   tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
355   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
356   QCOMPARE( result.length(), 1 );
357   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Ellipsoidal — WGS84)" )];
358   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
359   QGSCOMPARENEAR( length, 0.242000, 0.001 );
360   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Cartesian — 2D)" )];
361   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
362   QGSCOMPARENEAR( length, 26930.6, 0.1 );
363   derivedLength = result.at( 0 ).mDerivedAttributes[tr( "Length (Cartesian — 3D)" )];
364   length = derivedLength.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
365   QGSCOMPARENEAR( length, 26948.827000, 0.1 );
366 }
367 
perimeterCalculation()368 void TestQgsMapToolIdentifyAction::perimeterCalculation()
369 {
370   QgsSettings s;
371   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
372 
373   //create a temporary layer
374   std::unique_ptr< QgsVectorLayer> tempLayer( new QgsVectorLayer( QStringLiteral( "Polygon?crs=epsg:3111&field=pk:int&field=col1:double" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
375   QVERIFY( tempLayer->isValid() );
376   QgsFeature f1( tempLayer->dataProvider()->fields(), 1 );
377   f1.setAttribute( QStringLiteral( "pk" ), 1 );
378   f1.setAttribute( QStringLiteral( "col1" ), 0.0 );
379   QgsPolylineXY polygonRing3111;
380   polygonRing3111 << QgsPointXY( 2484588, 2425722 ) << QgsPointXY( 2482767, 2398853 ) << QgsPointXY( 2520109, 2397715 ) << QgsPointXY( 2520792, 2425494 ) << QgsPointXY( 2484588, 2425722 );
381   QgsPolygonXY polygon3111;
382   polygon3111 << polygonRing3111;
383   QgsGeometry polygon3111G = QgsGeometry::fromPolygonXY( polygon3111 ) ;
384   f1.setGeometry( polygon3111G );
385   tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
386 
387   // set project CRS and ellipsoid
388   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
389   canvas->setDestinationCrs( srs );
390   canvas->setExtent( f1.geometry().boundingBox() );
391   QgsProject::instance()->setCrs( srs );
392   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
393   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceMeters );
394 
395   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( 2484588, 2425722 );
396 
397   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
398   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
399   QCOMPARE( result.length(), 1 );
400   QString derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Ellipsoidal — WGS84)" )];
401   double perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
402   QCOMPARE( perimeter, 128289.074 );
403   derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Cartesian)" )];
404   perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
405   QCOMPARE( perimeter, 128282.086 );
406 
407   //check that project units are respected
408   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceFeet );
409   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
410   QCOMPARE( result.length(), 1 );
411   derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Ellipsoidal — WGS84)" )];
412   perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
413   QGSCOMPARENEAR( perimeter, 420896.0, 0.1 );
414   derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Cartesian)" )];
415   perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
416   QGSCOMPARENEAR( perimeter, 420873.0, 0.1 );
417 
418   //test unchecked "keep base units" setting
419   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), false );
420   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
421   QCOMPARE( result.length(), 1 );
422   derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Ellipsoidal — WGS84)" )];
423   perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
424   QGSCOMPARENEAR( perimeter, 79.715, 0.001 );
425   derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Cartesian)" )];
426   perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
427   QCOMPARE( perimeter, 79.711 );
428 
429   // no conversion of Cartesian lengths between unit types
430   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
431   QgsProject::instance()->setDistanceUnits( QgsUnitTypes::DistanceDegrees );
432   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
433   QCOMPARE( result.length(), 1 );
434   derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Ellipsoidal — WGS84)" )];
435   perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
436   QGSCOMPARENEAR( perimeter, 1.152000, 0.001 );
437   derivedPerimeter = result.at( 0 ).mDerivedAttributes[tr( "Perimeter (Cartesian)" )];
438   perimeter = derivedPerimeter.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
439   QGSCOMPARENEAR( perimeter, 128282, 0.1 );
440 }
441 
areaCalculation()442 void TestQgsMapToolIdentifyAction::areaCalculation()
443 {
444   QgsSettings s;
445   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
446 
447   //create a temporary layer
448   std::unique_ptr< QgsVectorLayer> tempLayer( new QgsVectorLayer( QStringLiteral( "Polygon?crs=epsg:3111&field=pk:int&field=col1:double" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
449   QVERIFY( tempLayer->isValid() );
450   QgsFeature f1( tempLayer->dataProvider()->fields(), 1 );
451   f1.setAttribute( QStringLiteral( "pk" ), 1 );
452   f1.setAttribute( QStringLiteral( "col1" ), 0.0 );
453 
454   QgsPolylineXY polygonRing3111;
455   polygonRing3111 << QgsPointXY( 2484588, 2425722 ) << QgsPointXY( 2482767, 2398853 ) << QgsPointXY( 2520109, 2397715 ) << QgsPointXY( 2520792, 2425494 ) << QgsPointXY( 2484588, 2425722 );
456   QgsPolygonXY polygon3111;
457   polygon3111 << polygonRing3111;
458   QgsGeometry polygon3111G = QgsGeometry::fromPolygonXY( polygon3111 ) ;
459   f1.setGeometry( polygon3111G );
460   tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
461 
462   // set project CRS and ellipsoid
463   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3111" ) );
464   canvas->setDestinationCrs( srs );
465   canvas->setExtent( f1.geometry().boundingBox() );
466   QgsProject::instance()->setCrs( srs );
467   QgsProject::instance()->setEllipsoid( QStringLiteral( "WGS84" ) );
468   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMeters );
469 
470   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( 2484588, 2425722 );
471 
472   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
473   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
474   QCOMPARE( result.length(), 1 );
475   QString derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Ellipsoidal — WGS84)" )];
476   double area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
477   QGSCOMPARENEAR( area, 1005721496.780000, 1.0 );
478   derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Cartesian)" )];
479   area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
480   QGSCOMPARENEAR( area, 1005640568.0, 1.0 );
481 
482   //check that project units are respected
483   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareMiles );
484   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
485   QCOMPARE( result.length(), 1 );
486   derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Ellipsoidal — WGS84)" )];
487   area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
488   QGSCOMPARENEAR( area, 388.311000, 0.001 );
489   derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Cartesian)" )];
490   area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
491   QGSCOMPARENEAR( area, 388.280000, 0.001 );
492 
493   //test unchecked "keep base units" setting
494   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), false );
495   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareFeet );
496   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
497   QCOMPARE( result.length(), 1 );
498   derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Ellipsoidal — WGS84)" )];
499   area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
500   QGSCOMPARENEAR( area, 388.311000, 0.001 );
501   derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Cartesian)" )];
502   area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
503   QGSCOMPARENEAR( area, 388.280000, 0.001 );
504 
505   // no conversion of Cartesian lengths between unit types
506   s.setValue( QStringLiteral( "/qgis/measure/keepbaseunit" ), true );
507   QgsProject::instance()->setAreaUnits( QgsUnitTypes::AreaSquareDegrees );
508   result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << tempLayer.get() );
509   QCOMPARE( result.length(), 1 );
510   derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Ellipsoidal — WGS84)" )];
511   area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
512   QGSCOMPARENEAR( area, 0.081000, 0.001 );
513   derivedArea = result.at( 0 ).mDerivedAttributes[tr( "Area (Cartesian)" )];
514   area = derivedArea.remove( ',' ).split( ' ' ).at( 0 ).toDouble();
515   QGSCOMPARENEAR( area, 1005640568.6, 1 );
516 }
517 
518 // private
testIdentifyRaster(QgsRasterLayer * layer,double xGeoref,double yGeoref)519 QString TestQgsMapToolIdentifyAction::testIdentifyRaster( QgsRasterLayer *layer, double xGeoref, double yGeoref )
520 {
521   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
522   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( xGeoref, yGeoref );
523   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << layer );
524   if ( result.length() != 1 )
525     return QString();
526   return result[0].mAttributes[QStringLiteral( "Band 1" )];
527 }
528 
529 // private
testIdentifyMesh(QgsMeshLayer * layer,double xGeoref,double yGeoref)530 QList<QgsMapToolIdentify::IdentifyResult> TestQgsMapToolIdentifyAction::testIdentifyMesh( QgsMeshLayer *layer, double xGeoref, double yGeoref )
531 {
532   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
533   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( xGeoref, yGeoref );
534   //check that closest point attributes are present
535   QgsIdentifyContext identifyContext;
536   if ( canvas->mapSettings().isTemporal() )
537     identifyContext.setTemporalRange( canvas->temporalRange() );
538   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << layer, QgsMapToolIdentify::DefaultQgsSetting, identifyContext );
539   return result;
540 }
541 
542 // private
543 QList<QgsMapToolIdentify::IdentifyResult>
testIdentifyVector(QgsVectorLayer * layer,double xGeoref,double yGeoref)544 TestQgsMapToolIdentifyAction::testIdentifyVector( QgsVectorLayer *layer, double xGeoref, double yGeoref )
545 {
546   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
547   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( xGeoref, yGeoref );
548   QgsIdentifyContext identifyContext;
549   if ( canvas->mapSettings().isTemporal() )
550     identifyContext.setTemporalRange( canvas->temporalRange() );
551   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << layer, QgsMapToolIdentify::DefaultQgsSetting, identifyContext );
552   return result;
553 }
554 
555 // private
556 QList<QgsMapToolIdentify::IdentifyResult>
testIdentifyVectorTile(QgsVectorTileLayer * layer,double xGeoref,double yGeoref)557 TestQgsMapToolIdentifyAction::testIdentifyVectorTile( QgsVectorTileLayer *layer, double xGeoref, double yGeoref )
558 {
559   std::unique_ptr< QgsMapToolIdentifyAction > action( new QgsMapToolIdentifyAction( canvas ) );
560   QgsPointXY mapPoint = canvas->getCoordinateTransform()->transform( xGeoref, yGeoref );
561   QgsIdentifyContext identifyContext;
562   if ( canvas->mapSettings().isTemporal() )
563     identifyContext.setTemporalRange( canvas->temporalRange() );
564   QList<QgsMapToolIdentify::IdentifyResult> result = action->identify( mapPoint.x(), mapPoint.y(), QList<QgsMapLayer *>() << layer, QgsMapToolIdentify::DefaultQgsSetting, identifyContext );
565   return result;
566 }
567 
identifyRasterFloat32()568 void TestQgsMapToolIdentifyAction::identifyRasterFloat32()
569 {
570   //create a temporary layer
571   QString raster = QStringLiteral( TEST_DATA_DIR ) + "/raster/test.asc";
572 
573   // By default the QgsRasterLayer forces AAIGRID_DATATYPE=Float64
574   CPLSetConfigOption( "AAIGRID_DATATYPE", "Float32" );
575   std::unique_ptr< QgsRasterLayer> tempLayer( new QgsRasterLayer( raster ) );
576   CPLSetConfigOption( "AAIGRID_DATATYPE", nullptr );
577 
578   QVERIFY( tempLayer->isValid() );
579 
580   canvas->setExtent( QgsRectangle( 0, 0, 7, 1 ) );
581 
582   QCOMPARE( testIdentifyRaster( tempLayer.get(), 0.5, 0.5 ), QString( "-999.9" ) );
583 
584   QCOMPARE( testIdentifyRaster( tempLayer.get(), 1.5, 0.5 ), QString( "-999.987" ) );
585 
586   // More than 6 significant digits for corresponding value in .asc:
587   // precision loss in Float32
588   QCOMPARE( testIdentifyRaster( tempLayer.get(), 2.5, 0.5 ), QString( "1.234568" ) ); // in .asc file : 1.2345678
589 
590   QCOMPARE( testIdentifyRaster( tempLayer.get(), 3.5, 0.5 ), QString( "123456" ) );
591 
592   // More than 6 significant digits: no precision loss here for that particular value
593   QCOMPARE( testIdentifyRaster( tempLayer.get(), 4.5, 0.5 ), QString( "1234567" ) );
594 
595   // More than 6 significant digits: no precision loss here for that particular value
596   QCOMPARE( testIdentifyRaster( tempLayer.get(), 5.5, 0.5 ), QString( "-999.9876" ) );
597 
598   // More than 6 significant digits for corresponding value in .asc:
599   // precision loss in Float32
600   QCOMPARE( testIdentifyRaster( tempLayer.get(), 6.5, 0.5 ), QString( "1.234568" ) ); // in .asc file : 1.2345678901234
601 }
602 
identifyRasterFloat64()603 void TestQgsMapToolIdentifyAction::identifyRasterFloat64()
604 {
605   //create a temporary layer
606   QString raster = QStringLiteral( TEST_DATA_DIR ) + "/raster/test.asc";
607   std::unique_ptr< QgsRasterLayer> tempLayer( new QgsRasterLayer( raster ) );
608   QVERIFY( tempLayer->isValid() );
609 
610   canvas->setExtent( QgsRectangle( 0, 0, 7, 1 ) );
611 
612   QCOMPARE( testIdentifyRaster( tempLayer.get(), 0.5, 0.5 ), QString( "-999.9" ) );
613 
614   QCOMPARE( testIdentifyRaster( tempLayer.get(), 1.5, 0.5 ), QString( "-999.987" ) );
615 
616   QCOMPARE( testIdentifyRaster( tempLayer.get(), 2.5, 0.5 ), QString( "1.2345678" ) );
617 
618   QCOMPARE( testIdentifyRaster( tempLayer.get(), 3.5, 0.5 ), QString( "123456" ) );
619 
620   QCOMPARE( testIdentifyRaster( tempLayer.get(), 4.5, 0.5 ), QString( "1234567" ) );
621 
622   QCOMPARE( testIdentifyRaster( tempLayer.get(), 5.5, 0.5 ), QString( "-999.9876" ) );
623 
624   QCOMPARE( testIdentifyRaster( tempLayer.get(), 6.5, 0.5 ), QString( "1.2345678901234" ) );
625 }
626 
identifyMesh()627 void TestQgsMapToolIdentifyAction::identifyMesh()
628 {
629   //create a temporary layer
630   const QString mesh = QStringLiteral( TEST_DATA_DIR ) + "/mesh/quad_and_triangle.2dm";
631   QgsMeshLayer *tempLayer = new QgsMeshLayer( mesh, "testlayer", "mdal" );
632   QVERIFY( tempLayer->isValid() );
633   const QString vectorDs = QStringLiteral( TEST_DATA_DIR ) + "/mesh/quad_and_triangle_vertex_vector.dat";
634   tempLayer->dataProvider()->addDataset( vectorDs );
635   static_cast<QgsMeshLayerTemporalProperties *>(
636     tempLayer->temporalProperties() )->setReferenceTime(
637       QDateTime( QDate( 1950, 01, 01 ), QTime( 0, 0, 0 ), Qt::UTC ), tempLayer->dataProvider()->temporalCapabilities() );
638 
639   // we need to setup renderer otherwise triangular mesh
640   // will not be populated and identify will not work
641   QgsMapSettings mapSettings;
642   mapSettings.setExtent( tempLayer->extent() );
643   mapSettings.setDestinationCrs( tempLayer->crs() );
644   mapSettings.setOutputDpi( 96 );
645 
646   // here we check that datasets automatically get our default color ramp applied ("Plasma")
647   QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
648   tempLayer->createMapRenderer( context );
649 
650   // only scalar dataset
651   tempLayer->temporalProperties()->setIsActive( false );
652   tempLayer->setStaticScalarDatasetIndex( QgsMeshDatasetIndex( 0, 0 ) );
653   QList<QgsMapToolIdentify::IdentifyResult> results;
654 
655   results = testIdentifyMesh( tempLayer, 500, 500 );
656   QCOMPARE( results.size(), 2 );
657   QCOMPARE( results[0].mAttributes[ QStringLiteral( "Scalar Value" )], QStringLiteral( "no data" ) );
658   QCOMPARE( results[0].mDerivedAttributes[QStringLiteral( "Source" )], mesh );
659   QCOMPARE( results[1].mLabel, QStringLiteral( "Geometry" ) );
660   results = testIdentifyMesh( tempLayer, 2400, 2400 );
661   QCOMPARE( results.size(), 2 );
662   QCOMPARE( results[0].mAttributes[ QStringLiteral( "Scalar Value" )], QStringLiteral( "42" ) );
663   QCOMPARE( results[0].mDerivedAttributes[QStringLiteral( "Source" )], mesh );
664   QCOMPARE( results[1].mLabel, QStringLiteral( "Geometry" ) );
665   QCOMPARE( results[1].mDerivedAttributes[QStringLiteral( "Face Centroid X" )], QStringLiteral( "2333.33" ) );
666   QCOMPARE( results[1].mDerivedAttributes[QStringLiteral( "Face Centroid Y" )], QStringLiteral( "2333.33" ) );
667   results = testIdentifyMesh( tempLayer, 1999, 2999 );
668   QCOMPARE( results[1].mDerivedAttributes[QStringLiteral( "Snapped Vertex Position X" )], QStringLiteral( "2000" ) );
669   QCOMPARE( results[1].mDerivedAttributes[QStringLiteral( "Snapped Vertex Position Y" )], QStringLiteral( "3000" ) );
670 
671   canvas->setTemporalRange( QgsDateTimeRange( QDateTime( QDate( 1950, 01, 01 ), QTime( 0, 0, 0 ), Qt::UTC ),
672                             QDateTime( QDate( 1950, 01, 01 ), QTime( 1, 0, 0 ), Qt::UTC ) ) );
673 
674   tempLayer->temporalProperties()->setIsActive( true );
675   results = testIdentifyMesh( tempLayer, 2400, 2400 );
676   QCOMPARE( results.size(), 3 );
677   QCOMPARE( results[0].mLabel, QStringLiteral( "Bed Elevation (active)" ) );
678   QCOMPARE( results[0].mAttributes[ QStringLiteral( "Scalar Value" )], QStringLiteral( "42" ) );
679   QCOMPARE( results[0].mDerivedAttributes[QStringLiteral( "Source" )], mesh );
680 
681   QCOMPARE( results[1].mDerivedAttributes[ QStringLiteral( "Time Step" )], QStringLiteral( "01.01.1950 00:00:00" ) );
682 
683   QCOMPARE( results[1].mLabel, QStringLiteral( "VertexVectorDataset" ) );
684   QCOMPARE( results[1].mDerivedAttributes[QStringLiteral( "Source" )], vectorDs );
685   QCOMPARE( results[1].mAttributes[ QStringLiteral( "Vector Magnitude" )], QStringLiteral( "3" ) );
686   QCOMPARE( results[1].mDerivedAttributes[ QStringLiteral( "Vector x-component" )], QStringLiteral( "1.8" ) );
687   QCOMPARE( results[1].mDerivedAttributes[ QStringLiteral( "Vector y-component" )], QStringLiteral( "2.4" ) );
688 
689   QCOMPARE( results[2].mLabel, QStringLiteral( "Geometry" ) );
690   QCOMPARE( results[2].mDerivedAttributes[QStringLiteral( "Face Centroid X" )], QStringLiteral( "2333.33" ) );
691   QCOMPARE( results[2].mDerivedAttributes[QStringLiteral( "Face Centroid Y" )], QStringLiteral( "2333.33" ) );
692   results = testIdentifyMesh( tempLayer, 1999, 2999 );
693   QCOMPARE( results[2].mDerivedAttributes[QStringLiteral( "Snapped Vertex Position X" )], QStringLiteral( "2000" ) );
694   QCOMPARE( results[2].mDerivedAttributes[QStringLiteral( "Snapped Vertex Position Y" )], QStringLiteral( "3000" ) );
695 
696   tempLayer->temporalProperties()->setIsActive( false );
697 
698   // scalar + vector same
699   tempLayer->setStaticScalarDatasetIndex( QgsMeshDatasetIndex( 1, 0 ) );
700   tempLayer->setStaticVectorDatasetIndex( QgsMeshDatasetIndex( 1, 0 ) );
701   results = testIdentifyMesh( tempLayer, 500, 500 );
702   QCOMPARE( results.size(), 2 );
703   QCOMPARE( results[0].mAttributes[ QStringLiteral( "Vector Value" )], QStringLiteral( "no data" ) );
704   results = testIdentifyMesh( tempLayer, 2400, 2400 );
705   QCOMPARE( results.size(), 2 );
706   QCOMPARE( results[0].mAttributes[ QStringLiteral( "Vector Magnitude" )], QStringLiteral( "3" ) );
707   QCOMPARE( results[0].mDerivedAttributes[ QStringLiteral( "Vector x-component" )], QStringLiteral( "1.8" ) );
708   QCOMPARE( results[0].mDerivedAttributes[ QStringLiteral( "Vector y-component" )], QStringLiteral( "2.4" ) );
709 
710   // scalar + vector different
711   tempLayer->setStaticScalarDatasetIndex( QgsMeshDatasetIndex( 0, 0 ) );
712   tempLayer->setStaticVectorDatasetIndex( QgsMeshDatasetIndex( 1, 0 ) );
713   results = testIdentifyMesh( tempLayer, 2400, 2400 );
714   QCOMPARE( results.size(), 3 );
715   QCOMPARE( results[0].mAttributes[ QStringLiteral( "Scalar Value" )], QStringLiteral( "42" ) );
716   QCOMPARE( results[1].mAttributes[ QStringLiteral( "Vector Magnitude" )], QStringLiteral( "3" ) );
717   QCOMPARE( results[1].mDerivedAttributes[ QStringLiteral( "Vector x-component" )], QStringLiteral( "1.8" ) );
718   QCOMPARE( results[1].mDerivedAttributes[ QStringLiteral( "Vector y-component" )], QStringLiteral( "2.4" ) );
719 
720   // only vector
721   tempLayer->setStaticScalarDatasetIndex( QgsMeshDatasetIndex() );
722   tempLayer->setStaticVectorDatasetIndex( QgsMeshDatasetIndex( 1, 0 ) );
723   results = testIdentifyMesh( tempLayer, 2400, 2400 );
724   QCOMPARE( results.size(), 2 );
725   QCOMPARE( results[0].mAttributes[ QStringLiteral( "Vector Magnitude" )], QStringLiteral( "3" ) );
726   QCOMPARE( results[0].mDerivedAttributes[ QStringLiteral( "Vector x-component" )], QStringLiteral( "1.8" ) );
727   QCOMPARE( results[0].mDerivedAttributes[ QStringLiteral( "Vector y-component" )], QStringLiteral( "2.4" ) );
728 }
729 
identifyVectorTile()730 void TestQgsMapToolIdentifyAction::identifyVectorTile()
731 {
732   //create a temporary layer
733   QString vtPath = QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/vector_tile/{z}-{x}-{y}.pbf" );
734   QgsDataSourceUri dsUri;
735   dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
736   dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( vtPath ).toString() );
737   QgsVectorTileLayer *tempLayer = new QgsVectorTileLayer( dsUri.encodedUri(), QStringLiteral( "testlayer" ) );
738   QVERIFY( tempLayer->isValid() );
739 
740   QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3857" ) );
741   canvas->setDestinationCrs( srs );
742   canvas->setExtent( tempLayer->extent() );
743   canvas->resize( 512, 512 );
744   canvas->setLayers( QList<QgsMapLayer *>() << tempLayer );
745   canvas->setCurrentLayer( tempLayer );
746 
747   QList<QgsMapToolIdentify::IdentifyResult> results;
748   results = testIdentifyVectorTile( tempLayer, 15186127, -2974969 );
749   QCOMPARE( results.size(), 1 );
750   QCOMPARE( results[0].mLayer, tempLayer );
751   QCOMPARE( results[0].mLabel, QStringLiteral( "place" ) );
752   QCOMPARE( results[0].mFeature.geometry().wkbType(), QgsWkbTypes::Point );
753   QCOMPARE( results[0].mFeature.attribute( QStringLiteral( "class" ) ).toString(), QStringLiteral( "country" ) );
754   QCOMPARE( results[0].mFeature.attribute( QStringLiteral( "name" ) ).toString(), QStringLiteral( "Australia" ) );
755 
756   delete tempLayer;
757 }
758 
identifyInvalidPolygons()759 void TestQgsMapToolIdentifyAction::identifyInvalidPolygons()
760 {
761   //create a temporary layer
762   std::unique_ptr< QgsVectorLayer > memoryLayer( new QgsVectorLayer( QStringLiteral( "Polygon?field=pk:int" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
763   QVERIFY( memoryLayer->isValid() );
764   QgsFeature f1( memoryLayer->dataProvider()->fields(), 1 );
765   f1.setAttribute( QStringLiteral( "pk" ), 1 );
766   // This geometry is an invalid polygon (3 distinct vertices).
767   // GEOS reported invalidity: Points of LinearRing do not form a closed linestring
768   f1.setGeometry( geomFromHexWKB(
769                     "010300000001000000030000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000"
770                   ) );
771   // TODO: check why we need the ->dataProvider() part, since
772   //       there's a QgsVectorLayer::addFeatures method too
773   //memoryLayer->addFeatures( QgsFeatureList() << f1 );
774   memoryLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 );
775 
776   canvas->setExtent( QgsRectangle( 0, 0, 10, 10 ) );
777   QList<QgsMapToolIdentify::IdentifyResult> identified;
778   identified = testIdentifyVector( memoryLayer.get(), 4, 6 );
779   QCOMPARE( identified.length(), 0 );
780   identified = testIdentifyVector( memoryLayer.get(), 6, 4 );
781   QCOMPARE( identified.length(), 1 );
782   QCOMPARE( identified[0].mFeature.attribute( "pk" ), QVariant( 1 ) );
783 
784 }
785 
786 
787 QGSTEST_MAIN( TestQgsMapToolIdentifyAction )
788 #include "testqgsmaptoolidentifyaction.moc"
789 
790 
791 
792 
793