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