1 /***************************************************************************
2 testqgsprocessingalgs.cpp
3 ---------------------
4 begin : November 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17 #include "limits"
18
19 #include "qgstest.h"
20 #include "qgsprocessingregistry.h"
21 #include "qgsprocessingprovider.h"
22 #include "qgsprocessingutils.h"
23 #include "qgsprocessingalgorithm.h"
24 #include "qgsprocessingcontext.h"
25 #include "qgsprocessingmodelalgorithm.h"
26 #include "qgsnativealgorithms.h"
27 #include "qgsalgorithmfillnodata.h"
28 #include "qgsalgorithmlinedensity.h"
29 #include "qgsalgorithmimportphotos.h"
30 #include "qgsalgorithmtransform.h"
31 #include "qgsalgorithmkmeansclustering.h"
32 #include "qgsvectorlayer.h"
33 #include "qgscategorizedsymbolrenderer.h"
34 #include "qgssinglesymbolrenderer.h"
35 #include "qgsmultipolygon.h"
36 #include "qgsrasteranalysisutils.h"
37 #include "qgsrasteranalysisutils.cpp"
38 #include "qgsrasterfilewriter.h"
39 #include "qgsreclassifyutils.h"
40 #include "qgsalgorithmrasterlogicalop.h"
41 #include "qgsprintlayout.h"
42 #include "qgslayertree.h"
43 #include "qgslayoutmanager.h"
44 #include "qgslayoutitemmap.h"
45 #include "qgsmarkersymbollayer.h"
46 #include "qgsrulebasedrenderer.h"
47 #include "qgspallabeling.h"
48 #include "qgsrastershader.h"
49 #include "qgssinglebandpseudocolorrenderer.h"
50 #include "qgslayoutitemscalebar.h"
51 #include "annotations/qgstextannotation.h"
52 #include "qgsfontutils.h"
53 #include "annotations/qgsannotationmanager.h"
54 #include "qgsvectorlayerlabeling.h"
55 #include "qgsstyle.h"
56 #include "qgsbookmarkmanager.h"
57 #include "qgsexpressioncontextutils.h"
58 #include "qgsrenderchecker.h"
59 #include "qgsrelationmanager.h"
60 #include "qgsmeshlayer.h"
61 #include "qgsmarkersymbol.h"
62 #include "qgsfillsymbol.h"
63 #include "qgsalgorithmgpsbabeltools.h"
64 #include "qgsannotationlayer.h"
65 #include "qgsannotationmarkeritem.h"
66
67 class TestQgsProcessingAlgs: public QObject
68 {
69 Q_OBJECT
70
71 private:
72
73 /**
74 * Helper function to get a feature based algorithm.
75 */
76 std::unique_ptr<QgsProcessingFeatureBasedAlgorithm> featureBasedAlg( const QString &id );
77
78 QgsFeature runForFeature( const std::unique_ptr<QgsProcessingFeatureBasedAlgorithm> &alg, QgsFeature feature, const QString &layerType, QVariantMap parameters = QVariantMap() );
79
80 private slots:
81 void initTestCase();// will be called before the first testfunction is executed.
82 void cleanupTestCase(); // will be called after the last testfunction was executed.
init()83 void init() {} // will be called before each testfunction is executed.
cleanup()84 void cleanup() {} // will be called after every testfunction.
85 void saveFeaturesAlg();
86 void packageAlg();
87 void rasterLayerProperties();
88 void exportToSpreadsheetXlsx();
89 void exportToSpreadsheetOds();
90 void exportToSpreadsheetOptions();
91 void renameLayerAlg();
92 void loadLayerAlg();
93 void parseGeoTags();
94 void featureFilterAlg();
95 void transformAlg();
96 void kmeansCluster();
97 void categorizeByStyle();
98 void extractBinary();
99 void exportLayersInformationAlg();
100 void createDirectory();
101 void flattenRelations();
102
103 void polygonsToLines_data();
104 void polygonsToLines();
105
106 void createConstantRaster_data();
107 void createConstantRaster();
108
109 void densifyGeometries_data();
110 void densifyGeometries();
111
112 void fillNoData_data();
113 void fillNoData();
114 void lineDensity_data();
115 void lineDensity();
116
117 void rasterLogicOp_data();
118 void rasterLogicOp();
119 void cellStatistics_data();
120 void cellStatistics();
121 void percentileFunctions_data();
122 void percentileFunctions();
123 void percentileRaster_data();
124 void percentileRaster();
125 void percentrankFunctions_data();
126 void percentrankFunctions();
127 void percentrankByRaster_data();
128 void percentrankByRaster();
129 void percentrankByValue_data();
130 void percentrankByValue();
131 void rasterFrequencyByComparisonOperator_data();
132 void rasterFrequencyByComparisonOperator();
133 void rasterLocalPosition_data();
134 void rasterLocalPosition();
135 void roundRasterValues_data();
136 void roundRasterValues();
137
138 void layoutMapExtent();
139
140 void styleFromProject();
141 void combineStyles();
142
143 void bookmarksToLayer();
144 void layerToBookmarks();
145
146 void repairShapefile();
147 void renameField();
148
149 void compareDatasets();
150 void shapefileEncoding();
151 void setLayerEncoding();
152
153 void raiseException();
154 void raiseWarning();
155
156 void randomFloatingPointDistributionRaster_data();
157 void randomFloatingPointDistributionRaster();
158 void randomIntegerDistributionRaster_data();
159 void randomIntegerDistributionRaster();
160 void randomRaster_data();
161 void randomRaster();
162
163 void filterByLayerType();
164 void conditionalBranch();
165
166 void saveLog();
167 void setProjectVariable();
168
169 #ifndef QT_NO_PRINTER
170 void exportLayoutPdf();
171 void exportLayoutPng();
172 void exportAtlasLayoutPdf();
173 void exportAtlasLayoutPng();
174 #endif
175
176 void tinMeshCreation();
177 void exportMeshVertices();
178 void exportMeshFaces();
179 void exportMeshEdges();
180 void exportMeshOnGrid();
181 void rasterizeMesh();
182 void exportMeshContours();
183 void exportMeshCrossSection();
184 void exportMeshTimeSeries();
185
186 void fileDownloader();
187
188 void rasterize();
189
190 void convertGpxFeatureType();
191 void convertGpsData();
192 void downloadGpsData();
193 void uploadGpsData();
194 void transferMainAnnotationLayer();
195
196 private:
197
198 bool imageCheck( const QString &testName, const QString &renderedImage );
199
200 QString mPointLayerPath;
201 QgsVectorLayer *mPointsLayer = nullptr;
202 QgsVectorLayer *mPolygonLayer = nullptr;
203
204 void exportToSpreadsheet( const QString &outputPath );
205 };
206
featureBasedAlg(const QString & id)207 std::unique_ptr<QgsProcessingFeatureBasedAlgorithm> TestQgsProcessingAlgs::featureBasedAlg( const QString &id )
208 {
209 return std::unique_ptr<QgsProcessingFeatureBasedAlgorithm>( static_cast<QgsProcessingFeatureBasedAlgorithm *>( QgsApplication::processingRegistry()->createAlgorithmById( id ) ) );
210 }
211
runForFeature(const std::unique_ptr<QgsProcessingFeatureBasedAlgorithm> & alg,QgsFeature feature,const QString & layerType,QVariantMap parameters)212 QgsFeature TestQgsProcessingAlgs::runForFeature( const std::unique_ptr< QgsProcessingFeatureBasedAlgorithm > &alg, QgsFeature feature, const QString &layerType, QVariantMap parameters )
213 {
214 Q_ASSERT( alg.get() );
215 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
216 QgsProject p;
217 context->setProject( &p );
218
219 QgsProcessingFeedback feedback;
220 context->setFeedback( &feedback );
221
222 std::unique_ptr<QgsVectorLayer> inputLayer( std::make_unique<QgsVectorLayer>( layerType, QStringLiteral( "layer" ), QStringLiteral( "memory" ) ) );
223 inputLayer->dataProvider()->addFeature( feature );
224
225 parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue<QgsMapLayer *>( inputLayer.get() ) );
226
227 parameters.insert( QStringLiteral( "OUTPUT" ), QStringLiteral( "memory:" ) );
228
229 bool ok = false;
230 const auto res = alg->run( parameters, *context, &feedback, &ok );
231 QgsFeature result;
232
233 std::unique_ptr<QgsVectorLayer> outputLayer( qobject_cast< QgsVectorLayer * >( context->getMapLayer( res.value( QStringLiteral( "OUTPUT" ) ).toString() ) ) );
234 outputLayer->getFeatures().nextFeature( result );
235
236 return result;
237 }
238
initTestCase()239 void TestQgsProcessingAlgs::initTestCase()
240 {
241 QgsApplication::init();
242 QgsApplication::initQgis();
243
244 // Set up the QgsSettings environment
245 QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
246 QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
247 QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
248
249 QgsApplication::processingRegistry()->addProvider( new QgsNativeAlgorithms( QgsApplication::processingRegistry() ) );
250
251 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
252
253 const QString pointsFileName = dataDir + "/points.shp";
254 const QFileInfo pointFileInfo( pointsFileName );
255 mPointLayerPath = pointFileInfo.filePath();
256 mPointsLayer = new QgsVectorLayer( mPointLayerPath,
257 QStringLiteral( "points" ), QStringLiteral( "ogr" ) );
258 QVERIFY( mPointsLayer->isValid() );
259 // Register the layer with the registry
260 QgsProject::instance()->addMapLayers(
261 QList<QgsMapLayer *>() << mPointsLayer );
262
263 //
264 //create a poly layer that will be used in all tests...
265 //
266 const QString polysFileName = dataDir + "/polys.shp";
267 const QFileInfo polyFileInfo( polysFileName );
268 mPolygonLayer = new QgsVectorLayer( polyFileInfo.filePath(),
269 QStringLiteral( "polygons" ), QStringLiteral( "ogr" ) );
270 // Register the layer with the registry
271 QgsProject::instance()->addMapLayers(
272 QList<QgsMapLayer *>() << mPolygonLayer );
273 QVERIFY( mPolygonLayer->isValid() );
274
275 //add a mesh layer
276 const QString uri( dataDir + "/mesh/quad_and_triangle.2dm" );
277 const QString meshLayerName = QStringLiteral( "mesh layer" );
278 QgsMeshLayer *meshLayer = new QgsMeshLayer( uri, meshLayerName, QStringLiteral( "mdal" ) );
279 // Register the layer with the registry
280 QgsProject::instance()->addMapLayer( meshLayer );
281 QVERIFY( meshLayer->isValid() );
282 meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_vertex_scalar.dat" );
283 meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_vertex_vector.dat" );
284 meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_els_face_scalar.dat" );
285 meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_els_face_vector.dat" );
286 QCOMPARE( meshLayer->datasetGroupCount(), 5 );
287
288 //add a 1D mesh layer
289 const QString uri1d( dataDir + "/mesh/lines.2dm" );
290 const QString meshLayer1dName = QStringLiteral( "mesh layer 1D" );
291 QgsMeshLayer *meshLayer1d = new QgsMeshLayer( uri1d, meshLayer1dName, QStringLiteral( "mdal" ) );
292 // Register the layer with the registry
293 QgsProject::instance()->addMapLayer( meshLayer1d );
294 QVERIFY( meshLayer1d->isValid() );
295 meshLayer1d->addDatasets( dataDir + "/mesh/lines_els_scalar.dat" );
296 meshLayer1d->addDatasets( dataDir + "/mesh/lines_els_vector.dat" );
297 QCOMPARE( meshLayer1d->datasetGroupCount(), 3 );
298 }
299
cleanupTestCase()300 void TestQgsProcessingAlgs::cleanupTestCase()
301 {
302 QgsApplication::exitQgis();
303 }
304
pkgAlg(const QStringList & layers,const QString & outputGpkg,bool overwrite,bool selectedFeaturesOnly,bool saveMetadata,bool * ok)305 QVariantMap pkgAlg( const QStringList &layers, const QString &outputGpkg, bool overwrite, bool selectedFeaturesOnly, bool saveMetadata, bool *ok )
306 {
307 const QgsProcessingAlgorithm *package( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:package" ) ) );
308
309 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
310 context->setProject( QgsProject::instance() );
311
312 QgsProcessingFeedback feedback;
313
314 QVariantMap parameters;
315 parameters.insert( QStringLiteral( "LAYERS" ), layers );
316 parameters.insert( QStringLiteral( "OUTPUT" ), outputGpkg );
317 parameters.insert( QStringLiteral( "OVERWRITE" ), overwrite );
318 parameters.insert( QStringLiteral( "SELECTED_FEATURES_ONLY" ), selectedFeaturesOnly );
319 parameters.insert( QStringLiteral( "SAVE_METADATA" ), saveMetadata );
320 return package->run( parameters, *context, &feedback, ok );
321 }
322
saveFeaturesAlg()323 void TestQgsProcessingAlgs::saveFeaturesAlg()
324 {
325 const QString outputGeoJson = QDir::tempPath() + "/savefeatures_alg.geojson";
326 const QString layerName = QStringLiteral( "custom_layer" );
327
328 if ( QFile::exists( outputGeoJson ) )
329 QFile::remove( outputGeoJson );
330
331 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
332
333 QVariantMap parameters;
334 parameters.insert( QStringLiteral( "INPUT" ), QString( dataDir + "/points.shp" ) );
335 parameters.insert( QStringLiteral( "LAYER_NAME" ), layerName );
336 parameters.insert( QStringLiteral( "LAYER_OPTIONS" ), QStringLiteral( "COORDINATE_PRECISION=1" ) );
337 parameters.insert( QStringLiteral( "OUTPUT" ), outputGeoJson );
338
339 const QgsProcessingAlgorithm *saveFeatures( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:savefeatures" ) ) );
340 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
341 context->setProject( QgsProject::instance() );
342
343 QgsProcessingFeedback feedback;
344 bool ok = false;
345 const QVariantMap outputs = saveFeatures->run( parameters, *context, &feedback, &ok );
346 QCOMPARE( ok, true );
347 QCOMPARE( outputs.value( QStringLiteral( "OUTPUT" ) ).toString(), QStringLiteral( "%1|layername=%2" ).arg( outputGeoJson, layerName ) );
348 QCOMPARE( outputs.value( QStringLiteral( "FILE_PATH" ) ).toString(), outputGeoJson );
349 QCOMPARE( outputs.value( QStringLiteral( "LAYER_NAME" ) ).toString(), layerName );
350
351 std::unique_ptr< QgsVectorLayer > savedLayer = std::make_unique< QgsVectorLayer >( outputs.value( QStringLiteral( "OUTPUT" ) ).toString(), "points", "ogr" );
352 QVERIFY( savedLayer->isValid() );
353 QCOMPARE( savedLayer->getFeature( 1 ).geometry().asPoint().x(), -83.3 );
354 }
355
exportLayersInformationAlg()356 void TestQgsProcessingAlgs::exportLayersInformationAlg()
357 {
358 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
359 const QString gpkgFileName = dataDir + "/humanbeings.gpkg";
360 const QFileInfo gpkgFileInfo( gpkgFileName );
361 const std::unique_ptr< QgsVectorLayer > gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgFileInfo.filePath() + QStringLiteral( "|layername=person" ),
362 QStringLiteral( "person" ), QStringLiteral( "ogr" ) );
363
364 const QgsProcessingAlgorithm *exportLayersInformation( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:exportlayersinformation" ) ) );
365
366 QgsProcessingContext context;;
367 context.setProject( QgsProject::instance() );
368 QgsProcessingFeedback feedback;
369
370 QVariantMap parameters;
371 parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( gpkgLayer.get() ) );
372 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
373 bool ok = false;
374 QVariantMap results = exportLayersInformation->run( parameters, context, &feedback, &ok );
375 QVERIFY( ok );
376 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
377
378 QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( context.getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
379 QVERIFY( vlayer );
380 QCOMPARE( vlayer->featureCount(), 1L );
381 QCOMPARE( vlayer->crs().authid(), QStringLiteral( "EPSG:2056" ) );
382
383 parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( gpkgLayer.get() ) << QVariant::fromValue( mPolygonLayer ) );
384 ok = false;
385 results = exportLayersInformation->run( parameters, context, &feedback, &ok );
386 QVERIFY( ok );
387 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
388
389 vlayer = qobject_cast< QgsVectorLayer * >( context.getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
390 QVERIFY( vlayer );
391 QCOMPARE( vlayer->featureCount(), 2L );
392 // when layers have mixed CRSes, the algorithm uses WGS84
393 QCOMPARE( vlayer->crs().authid(), QStringLiteral( "EPSG:4326" ) );
394 }
395
packageAlg()396 void TestQgsProcessingAlgs::packageAlg()
397 {
398 const QString outputGpkg = QDir::tempPath() + "/package_alg.gpkg";
399
400 if ( QFile::exists( outputGpkg ) )
401 QFile::remove( outputGpkg );
402
403 const QVariantMap parameters;
404 const QStringList layers = QStringList() << mPointsLayer->id() << mPolygonLayer->id();
405 bool ok = false;
406 const QVariantMap results = pkgAlg( layers, outputGpkg, true, false, false, &ok );
407 QVERIFY( ok );
408
409 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
410 std::unique_ptr< QgsVectorLayer > pointLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=points", "points", "ogr" );
411 QVERIFY( pointLayer->isValid() );
412 QCOMPARE( pointLayer->wkbType(), mPointsLayer->wkbType() );
413 QCOMPARE( pointLayer->featureCount(), mPointsLayer->featureCount() );
414 pointLayer.reset();
415 std::unique_ptr< QgsVectorLayer > polygonLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=polygons", "polygons", "ogr" );
416 QVERIFY( polygonLayer->isValid() );
417 QCOMPARE( polygonLayer->wkbType(), mPolygonLayer->wkbType() );
418 QCOMPARE( polygonLayer->featureCount(), mPolygonLayer->featureCount() );
419 polygonLayer.reset();
420
421 std::unique_ptr<QgsVectorLayer> rectangles = std::make_unique<QgsVectorLayer>( QStringLiteral( TEST_DATA_DIR ) + "/rectangles.shp",
422 QStringLiteral( "rectangles" ), QStringLiteral( "ogr" ) );
423 QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << rectangles.get() );
424 QgsLayerMetadata metadata;
425 metadata.setFees( QStringLiteral( "lots of dogecoin" ) );
426 rectangles->setMetadata( metadata );
427
428 // Test adding an additional layer (overwrite disabled)
429 const QVariantMap results2 = pkgAlg( QStringList() << rectangles->id(), outputGpkg, false, false, false, &ok );
430 QVERIFY( ok );
431
432 QVERIFY( !results2.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
433 std::unique_ptr< QgsVectorLayer > rectanglesPackagedLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=rectangles", "points", "ogr" );
434 QVERIFY( rectanglesPackagedLayer->isValid() );
435 QCOMPARE( rectanglesPackagedLayer->wkbType(), rectanglesPackagedLayer->wkbType() );
436 QCOMPARE( rectanglesPackagedLayer->featureCount(), rectangles->featureCount() );
437 rectanglesPackagedLayer.reset();
438
439 pointLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=points", "points", "ogr" );
440 QVERIFY( pointLayer->isValid() );
441 pointLayer.reset();
442
443 // And finally, test with overwrite enabled
444 QVariantMap results3 = pkgAlg( QStringList() << rectangles->id(), outputGpkg, true, false, false, &ok );
445 QVERIFY( ok );
446
447 QVERIFY( !results2.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
448 rectanglesPackagedLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=rectangles", "points", "ogr" );
449 QVERIFY( rectanglesPackagedLayer->isValid() );
450 QCOMPARE( rectanglesPackagedLayer->wkbType(), rectanglesPackagedLayer->wkbType() );
451 QCOMPARE( rectanglesPackagedLayer->featureCount(), rectangles->featureCount() );
452
453 pointLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=points", "points", "ogr" );
454 QVERIFY( !pointLayer->isValid() ); // It's gone -- the gpkg was recreated with a single layer
455
456 QCOMPARE( rectanglesPackagedLayer->metadata().fees(), QString() );
457 rectanglesPackagedLayer.reset();
458
459 // save layer metadata
460 results3 = pkgAlg( QStringList() << rectangles->id(), outputGpkg, true, false, true, &ok );
461 QVERIFY( ok );
462 rectanglesPackagedLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=rectangles", "points", "ogr" );
463 QVERIFY( rectanglesPackagedLayer->isValid() );
464 QCOMPARE( rectanglesPackagedLayer->wkbType(), rectanglesPackagedLayer->wkbType() );
465 QCOMPARE( rectanglesPackagedLayer->featureCount(), rectangles->featureCount() );
466 QCOMPARE( rectanglesPackagedLayer->metadata().fees(), QStringLiteral( "lots of dogecoin" ) );
467
468 // Test saving of selected features only
469 mPolygonLayer->selectByIds( QgsFeatureIds() << 1 << 2 << 3 );
470 const QVariantMap results4 = pkgAlg( QStringList() << mPolygonLayer->id(), outputGpkg, false, true, false, &ok );
471 QVERIFY( ok );
472
473 QVERIFY( !results4.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
474 std::unique_ptr< QgsVectorLayer > selectedPolygonsPackagedLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=polygons", "polygons", "ogr" );
475 QVERIFY( selectedPolygonsPackagedLayer->isValid() );
476 QCOMPARE( selectedPolygonsPackagedLayer->wkbType(), mPolygonLayer->wkbType() );
477 QCOMPARE( selectedPolygonsPackagedLayer->featureCount(), 3 );
478 selectedPolygonsPackagedLayer.reset();
479
480 mPolygonLayer->removeSelection();
481 const QVariantMap results5 = pkgAlg( QStringList() << mPolygonLayer->id(), outputGpkg, false, true, false, &ok );
482 QVERIFY( ok );
483
484 QVERIFY( !results5.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
485 selectedPolygonsPackagedLayer = std::make_unique< QgsVectorLayer >( outputGpkg + "|layername=polygons", "polygons", "ogr" );
486 QVERIFY( selectedPolygonsPackagedLayer->isValid() );
487 QCOMPARE( selectedPolygonsPackagedLayer->wkbType(), mPolygonLayer->wkbType() );
488 QCOMPARE( selectedPolygonsPackagedLayer->featureCount(), 10 ); // With enabled SELECTED_FEATURES_ONLY all features should be saved when there is no selection
489 }
490
rasterLayerProperties()491 void TestQgsProcessingAlgs::rasterLayerProperties()
492 {
493 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:rasterlayerproperties" ) ) );
494
495 const QString myDataPath( TEST_DATA_DIR ); //defined in CMakeLists.txt
496
497 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
498
499 QVariantMap parameters;
500
501 parameters.insert( QStringLiteral( "INPUT" ), QVariant( myDataPath + "/landsat.tif" ) );
502
503 //run alg...
504 bool ok = false;
505 QgsProcessingFeedback feedback;
506 QVariantMap results;
507
508 results = alg->run( parameters, *context, &feedback, &ok );
509 QVERIFY( ok );
510
511 QCOMPARE( results.value( QStringLiteral( "X_MIN" ) ).toDouble(), 781662.375 );
512 QCOMPARE( results.value( QStringLiteral( "X_MAX" ) ).toDouble(), 793062.375 );
513 QCOMPARE( results.value( QStringLiteral( "Y_MIN" ) ).toDouble(), 3339523.125 );
514 QCOMPARE( results.value( QStringLiteral( "Y_MAX" ) ).toDouble(), 3350923.125 );
515 QCOMPARE( results.value( QStringLiteral( "EXTENT" ) ).toString(), QStringLiteral( "781662.3750000000000000,3339523.1250000000000000 : 793062.3750000000000000,3350923.1250000000000000" ) );
516 QCOMPARE( results.value( QStringLiteral( "PIXEL_WIDTH" ) ).toDouble(), 57.0 );
517 QCOMPARE( results.value( QStringLiteral( "PIXEL_HEIGHT" ) ).toDouble(), 57.0 );
518 QCOMPARE( results.value( QStringLiteral( "CRS_AUTHID" ) ).toString(), QStringLiteral( "EPSG:32633" ) );
519 QCOMPARE( results.value( QStringLiteral( "WIDTH_IN_PIXELS" ) ).toInt(), 200 );
520 QCOMPARE( results.value( QStringLiteral( "HEIGHT_IN_PIXELS" ) ).toInt(), 200 );
521 QCOMPARE( results.value( QStringLiteral( "BAND_COUNT" ) ).toInt(), 9 );
522
523 parameters.insert( QStringLiteral( "INPUT" ), QVariant( myDataPath + "/raster/valueRas3_float64.asc" ) );
524 parameters.insert( QStringLiteral( "BAND" ), 1 );
525 ok = false;
526 results = alg->run( parameters, *context, &feedback, &ok );
527 QVERIFY( ok );
528
529 QCOMPARE( results.value( QStringLiteral( "X_MIN" ) ).toDouble(), 0.0 );
530 QCOMPARE( results.value( QStringLiteral( "X_MAX" ) ).toDouble(), 4.0 );
531 QCOMPARE( results.value( QStringLiteral( "Y_MIN" ) ).toDouble(), 0.0 );
532 QCOMPARE( results.value( QStringLiteral( "Y_MAX" ) ).toDouble(), 4.0 );
533 QCOMPARE( results.value( QStringLiteral( "EXTENT" ) ).toString(), QStringLiteral( "0.0000000000000000,0.0000000000000000 : 4.0000000000000000,4.0000000000000000" ) );
534 QCOMPARE( results.value( QStringLiteral( "PIXEL_WIDTH" ) ).toDouble(), 1.0 );
535 QCOMPARE( results.value( QStringLiteral( "PIXEL_HEIGHT" ) ).toDouble(), 1.0 );
536 QCOMPARE( results.value( QStringLiteral( "CRS_AUTHID" ) ).toString(), QStringLiteral( "" ) );
537 QCOMPARE( results.value( QStringLiteral( "WIDTH_IN_PIXELS" ) ).toInt(), 4 );
538 QCOMPARE( results.value( QStringLiteral( "HEIGHT_IN_PIXELS" ) ).toInt(), 4 );
539 QCOMPARE( results.value( QStringLiteral( "BAND_COUNT" ) ).toInt(), 1 );
540 QCOMPARE( results.value( QStringLiteral( "HAS_NODATA_VALUE" ) ).toInt(), 1 );
541 QCOMPARE( results.value( QStringLiteral( "NODATA_VALUE" ) ).toInt(), -9999 );
542 }
543
exportToSpreadsheetXlsx()544 void TestQgsProcessingAlgs::exportToSpreadsheetXlsx()
545 {
546 if ( QgsTest::isCIRun() )
547 {
548 QSKIP( "XLSX driver not working on Travis" );
549 }
550
551 const QString outputPath = QDir::tempPath() + "/spreadsheet.xlsx";
552 exportToSpreadsheet( outputPath );
553 }
554
exportToSpreadsheetOds()555 void TestQgsProcessingAlgs::exportToSpreadsheetOds()
556 {
557 const QString outputPath = QDir::tempPath() + "/spreadsheet.ods";
558 exportToSpreadsheet( outputPath );
559 }
560
exportToSpreadsheetOptions()561 void TestQgsProcessingAlgs::exportToSpreadsheetOptions()
562 {
563 const QString outputPath = QDir::tempPath() + "/spreadsheet.ods";
564 if ( QFile::exists( outputPath ) )
565 QFile::remove( outputPath );
566
567 QVariantMap parameters;
568 const QStringList layers = QStringList() << mPointsLayer->id();
569 bool ok = false;
570
571 mPointsLayer->setFieldAlias( 1, QStringLiteral( "my heading" ) );
572
573 const QgsProcessingAlgorithm *alg( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:exporttospreadsheet" ) ) );
574
575 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
576 context->setProject( QgsProject::instance() );
577
578 QgsProcessingFeedback feedback;
579
580 parameters.insert( QStringLiteral( "LAYERS" ), layers );
581 parameters.insert( QStringLiteral( "OUTPUT" ), outputPath );
582 parameters.insert( QStringLiteral( "OVERWRITE" ), true );
583 parameters.insert( QStringLiteral( "USE_ALIAS" ), false );
584 QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
585 QVERIFY( ok );
586
587 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
588 std::unique_ptr< QgsVectorLayer > pointLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=points", "points", "ogr" );
589 QCOMPARE( pointLayer->fields().at( 0 ).name(), QStringLiteral( "Class" ) );
590 QCOMPARE( pointLayer->fields().at( 1 ).name(), QStringLiteral( "Heading" ) );
591 QCOMPARE( pointLayer->fields().at( 2 ).name(), QStringLiteral( "Importance" ) );
592 QCOMPARE( pointLayer->fields().at( 3 ).name(), QStringLiteral( "Pilots" ) );
593 QCOMPARE( pointLayer->fields().at( 4 ).name(), QStringLiteral( "Cabin Crew" ) );
594
595 pointLayer.reset();
596
597
598 mPointsLayer->setEditorWidgetSetup( 2, QgsEditorWidgetSetup( QStringLiteral( "ValueMap" ),
599 QVariantMap{{"map", QVariantMap{{"High", "1"},
600 {"Medium", "10"},
601 {"Low", "20"},
602 {"VLow", "3"},
603 {"VHigh", "4"}}
604 }} ) );
605
606 parameters.insert( QStringLiteral( "USE_ALIAS" ), true );
607 parameters.insert( QStringLiteral( "FORMATTED_VALUES" ), false );
608 results = alg->run( parameters, *context, &feedback, &ok );
609 QVERIFY( ok );
610
611 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
612 pointLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=points", "points", "ogr" );
613 QCOMPARE( pointLayer->fields().at( 0 ).name(), QStringLiteral( "Class" ) );
614 QCOMPARE( pointLayer->fields().at( 1 ).name(), QStringLiteral( "my heading" ) );
615 QCOMPARE( pointLayer->fields().at( 2 ).name(), QStringLiteral( "Importance" ) );
616 QCOMPARE( pointLayer->fields().at( 3 ).name(), QStringLiteral( "Pilots" ) );
617 QCOMPARE( pointLayer->fields().at( 4 ).name(), QStringLiteral( "Cabin Crew" ) );
618
619 QSet< QString > values;
620 QgsFeature f;
621 QgsFeatureIterator it = pointLayer->getFeatures();
622 while ( it.nextFeature( f ) )
623 values.insert( f.attribute( QStringLiteral( "Importance" ) ).toString() );
624
625 QCOMPARE( values.size(), 5 );
626 QVERIFY( values.contains( "1" ) );
627 QVERIFY( values.contains( "3" ) );
628 QVERIFY( values.contains( "4" ) );
629 QVERIFY( values.contains( "10" ) );
630 QVERIFY( values.contains( "20" ) );
631
632 parameters.insert( QStringLiteral( "FORMATTED_VALUES" ), true );
633 results = alg->run( parameters, *context, &feedback, &ok );
634 QVERIFY( ok );
635
636 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
637 pointLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=points", "points", "ogr" );
638 values.clear();
639 it = pointLayer->getFeatures();
640 while ( it.nextFeature( f ) )
641 values.insert( f.attribute( QStringLiteral( "Importance" ) ).toString() );
642
643 QCOMPARE( values.size(), 5 );
644 QVERIFY( values.contains( "High" ) );
645 QVERIFY( values.contains( "Medium" ) );
646 QVERIFY( values.contains( "Low" ) );
647 QVERIFY( values.contains( "VLow" ) );
648 QVERIFY( values.contains( "VHigh" ) );
649 }
650
exportToSpreadsheet(const QString & outputPath)651 void TestQgsProcessingAlgs::exportToSpreadsheet( const QString &outputPath )
652 {
653 if ( QFile::exists( outputPath ) )
654 QFile::remove( outputPath );
655
656 QVariantMap parameters;
657 const QStringList layers = QStringList() << mPointsLayer->id() << mPolygonLayer->id();
658 bool ok = false;
659
660 const QgsProcessingAlgorithm *alg( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:exporttospreadsheet" ) ) );
661
662 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
663 context->setProject( QgsProject::instance() );
664
665 QgsProcessingFeedback feedback;
666
667 parameters.insert( QStringLiteral( "LAYERS" ), layers );
668 parameters.insert( QStringLiteral( "OUTPUT" ), outputPath );
669 parameters.insert( QStringLiteral( "OVERWRITE" ), false );
670 const QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
671 QVERIFY( ok );
672
673 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
674 std::unique_ptr< QgsVectorLayer > pointLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=points", "points", "ogr" );
675 QVERIFY( pointLayer->isValid() );
676 QCOMPARE( pointLayer->featureCount(), mPointsLayer->featureCount() );
677 pointLayer.reset();
678 std::unique_ptr< QgsVectorLayer > polygonLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=polygons", "polygons", "ogr" );
679 QVERIFY( polygonLayer->isValid() );
680 QCOMPARE( polygonLayer->featureCount(), mPolygonLayer->featureCount() );
681 polygonLayer.reset();
682
683 std::unique_ptr<QgsVectorLayer> rectangles = std::make_unique<QgsVectorLayer>( QStringLiteral( TEST_DATA_DIR ) + "/rectangles.shp",
684 QStringLiteral( "rectangles" ), QStringLiteral( "ogr" ) );
685 QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << rectangles.get() );
686
687 // Test adding an additional layer (overwrite disabled)
688 parameters.insert( QStringLiteral( "LAYERS" ), QStringList() << rectangles->id() );
689 const QVariantMap results2 = alg->run( parameters, *context, &feedback, &ok );
690 QVERIFY( ok );
691
692 QVERIFY( !results2.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
693 std::unique_ptr< QgsVectorLayer > rectanglesPackagedLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=rectangles", "points", "ogr" );
694 QVERIFY( rectanglesPackagedLayer->isValid() );
695 QCOMPARE( rectanglesPackagedLayer->featureCount(), rectangles->featureCount() );
696 rectanglesPackagedLayer.reset();
697
698 pointLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=points", "points", "ogr" );
699 QVERIFY( pointLayer->isValid() );
700 pointLayer.reset();
701
702 // And finally, test with overwrite enabled
703 parameters.insert( QStringLiteral( "OVERWRITE" ), true );
704 const QVariantMap results3 = alg->run( parameters, *context, &feedback, &ok );
705 QVERIFY( ok );
706
707 QVERIFY( !results3.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
708 rectanglesPackagedLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=rectangles", "points", "ogr" );
709 QVERIFY( rectanglesPackagedLayer->isValid() );
710 QCOMPARE( rectanglesPackagedLayer->featureCount(), rectangles->featureCount() );
711
712 pointLayer = std::make_unique< QgsVectorLayer >( outputPath + "|layername=points", "points", "ogr" );
713 QVERIFY( !pointLayer->isValid() ); // It's gone -- the xlsx was recreated with a single layer
714 }
715
716
renameLayerAlg()717 void TestQgsProcessingAlgs::renameLayerAlg()
718 {
719 const QgsProcessingAlgorithm *package( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:renamelayer" ) ) );
720 QVERIFY( package );
721
722 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
723 context->setProject( QgsProject::instance() );
724
725 QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:real" ), QStringLiteral( "layer" ), QStringLiteral( "memory" ) );
726 QVERIFY( layer->isValid() );
727 QgsProject::instance()->addMapLayer( layer );
728
729 QgsProcessingFeedback feedback;
730
731 QVariantMap parameters;
732
733 // bad layer
734 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "bad layer" ) );
735 parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name" ) );
736 bool ok = false;
737 ( void )package->run( parameters, *context, &feedback, &ok );
738 QVERIFY( !ok );
739 QCOMPARE( layer->name(), QStringLiteral( "layer" ) );
740
741 //invalid name
742 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "layer" ) );
743 parameters.insert( QStringLiteral( "NAME" ), QString() );
744 ok = false;
745 ( void )package->run( parameters, *context, &feedback, &ok );
746 QVERIFY( !ok );
747 QCOMPARE( layer->name(), QStringLiteral( "layer" ) );
748
749 //good params
750 parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( layer ) );
751 parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name" ) );
752 ok = false;
753 QVariantMap results = package->run( parameters, *context, &feedback, &ok );
754 QVERIFY( ok );
755 QCOMPARE( layer->name(), QStringLiteral( "new name" ) );
756 QCOMPARE( results.value( "OUTPUT" ), QVariant::fromValue( layer ) );
757
758 // with input layer name as parameter
759 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "new name" ) );
760 parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name2" ) );
761 ok = false;
762 results = package->run( parameters, *context, &feedback, &ok );
763 QVERIFY( ok );
764 QCOMPARE( layer->name(), QStringLiteral( "new name2" ) );
765 // result should use new name as value
766 QCOMPARE( results.value( "OUTPUT" ).toString(), QStringLiteral( "new name2" ) );
767 }
768
loadLayerAlg()769 void TestQgsProcessingAlgs::loadLayerAlg()
770 {
771 const QgsProcessingAlgorithm *package( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:loadlayer" ) ) );
772 QVERIFY( package );
773
774 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
775 QgsProject p;
776 context->setProject( &p );
777
778 QgsProcessingFeedback feedback;
779
780 QVariantMap parameters;
781
782 // bad layer
783 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "bad layer" ) );
784 parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name" ) );
785 bool ok = false;
786 ( void )package->run( parameters, *context, &feedback, &ok );
787 QVERIFY( !ok );
788 QVERIFY( context->layersToLoadOnCompletion().empty() );
789
790 //invalid name
791 parameters.insert( QStringLiteral( "INPUT" ), mPointLayerPath );
792 parameters.insert( QStringLiteral( "NAME" ), QString() );
793 ok = false;
794 ( void )package->run( parameters, *context, &feedback, &ok );
795 QVERIFY( !ok );
796 QVERIFY( context->layersToLoadOnCompletion().empty() );
797
798 //good params
799 parameters.insert( QStringLiteral( "INPUT" ), mPointLayerPath );
800 parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "my layer" ) );
801 ok = false;
802 const QVariantMap results = package->run( parameters, *context, &feedback, &ok );
803 QVERIFY( ok );
804 QVERIFY( !context->layersToLoadOnCompletion().empty() );
805 const QString layerId = context->layersToLoadOnCompletion().keys().at( 0 );
806 QCOMPARE( results.value( QStringLiteral( "OUTPUT" ) ).toString(), layerId );
807 QVERIFY( !layerId.isEmpty() );
808 QVERIFY( context->temporaryLayerStore()->mapLayer( layerId ) );
809 QCOMPARE( context->layersToLoadOnCompletion().value( layerId, QgsProcessingContext::LayerDetails( QString(), nullptr, QString() ) ).name, QStringLiteral( "my layer" ) );
810 QCOMPARE( context->layersToLoadOnCompletion().value( layerId, QgsProcessingContext::LayerDetails( QString(), nullptr, QString() ) ).project, &p );
811 QCOMPARE( context->layersToLoadOnCompletion().value( layerId, QgsProcessingContext::LayerDetails( QString(), nullptr, QString() ) ).outputName, QStringLiteral( "my layer" ) );
812 }
813
parseGeoTags()814 void TestQgsProcessingAlgs::parseGeoTags()
815 {
816 // parseCoord
817 QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "" ).isValid() );
818 QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "x" ).isValid() );
819 QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "1 2 3" ).isValid() );
820 QGSCOMPARENEAR( QgsImportPhotosAlgorithm::parseCoord( "(36) (13) (15.21)" ).toDouble(), 36.220892, 0.000001 );
821 QGSCOMPARENEAR( QgsImportPhotosAlgorithm::parseCoord( "(3) (1) (5.21)" ).toDouble(), 3.018114, 0.000001 );
822 QGSCOMPARENEAR( QgsImportPhotosAlgorithm::parseCoord( "(149) (7) (54.76)" ).toDouble(), 149.131878, 0.000001 );
823 QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "(149) (7) (c)" ).isValid() );
824 QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "(149) (7) ()" ).isValid() );
825
826 // parseMetadataValue
827 QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "abc" ).toString(), QStringLiteral( "abc" ) );
828 QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "(abc)" ).toString(), QStringLiteral( "(abc)" ) );
829 QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "abc (123)" ).toString(), QStringLiteral( "abc (123)" ) );
830 QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "(123)" ).toDouble(), 123.0 );
831
832 // parseMetadataList
833 QVariantMap md = QgsImportPhotosAlgorithm::parseMetadataList( QStringList() << "EXIF_Contrast=(1)"
834 << "EXIF_ExposureTime=(0.008339)"
835 << "EXIF_Model=Pixel"
836 << "EXIF_GPSLatitude=(36) (13) (15.21)"
837 << "EXIF_GPSLongitude=(149) (7) (54.76)" );
838 QCOMPARE( md.count(), 5 );
839 QCOMPARE( md.value( "EXIF_Contrast" ).toInt(), 1 );
840 QCOMPARE( md.value( "EXIF_ExposureTime" ).toDouble(), 0.008339 );
841 QCOMPARE( md.value( "EXIF_Model" ).toString(), QStringLiteral( "Pixel" ) );
842 QGSCOMPARENEAR( md.value( "EXIF_GPSLatitude" ).toDouble(), 36.220892, 0.000001 );
843 QGSCOMPARENEAR( md.value( "EXIF_GPSLongitude" ).toDouble(), 149.131878, 0.000001 );
844
845 // test extractGeoTagFromMetadata
846 md = QVariantMap();
847 QgsPointXY point;
848 QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
849 md.insert( QStringLiteral( "EXIF_GPSLongitude" ), 142.0 );
850 QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
851 md.insert( QStringLiteral( "EXIF_GPSLatitude" ), 37.0 );
852 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
853 QCOMPARE( point.x(), 142.0 );
854 QCOMPARE( point.y(), 37.0 );
855 md.insert( QStringLiteral( "EXIF_GPSLongitude" ), QStringLiteral( "x" ) );
856 QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
857 md.insert( QStringLiteral( "EXIF_GPSLongitude" ), 142.0 );
858 md.insert( QStringLiteral( "EXIF_GPSLatitude" ), QStringLiteral( "x" ) );
859 QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
860 md.insert( QStringLiteral( "EXIF_GPSLatitude" ), 37.0 );
861 md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "E" ) );
862 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
863 QCOMPARE( point.x(), 142.0 );
864 QCOMPARE( point.y(), 37.0 );
865 md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "W" ) );
866 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
867 QCOMPARE( point.x(), -142.0 );
868 QCOMPARE( point.y(), 37.0 );
869 md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "w" ) );
870 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
871 QCOMPARE( point.x(), -142.0 );
872 QCOMPARE( point.y(), 37.0 );
873 md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "...W" ) ); // apparently any string ENDING in W is acceptable
874 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
875 QCOMPARE( point.x(), -142.0 );
876 QCOMPARE( point.y(), 37.0 );
877 md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QString() );
878 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
879 QCOMPARE( point.x(), 142.0 );
880 QCOMPARE( point.y(), 37.0 );
881 md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), -1 );
882 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
883 QCOMPARE( point.x(), -142.0 );
884 QCOMPARE( point.y(), 37.0 );
885 md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QString() );
886 md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "N" ) );
887 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
888 QCOMPARE( point.x(), 142.0 );
889 QCOMPARE( point.y(), 37.0 );
890 md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "S" ) );
891 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
892 QCOMPARE( point.x(), 142.0 );
893 QCOMPARE( point.y(), -37.0 );
894 md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "s" ) );
895 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
896 QCOMPARE( point.x(), 142.0 );
897 QCOMPARE( point.y(), -37.0 );
898 md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "...S" ) ); // any string ending in s is acceptable
899 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
900 QCOMPARE( point.x(), 142.0 );
901 QCOMPARE( point.y(), -37.0 );
902 md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QString() );
903 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
904 QCOMPARE( point.x(), 142.0 );
905 QCOMPARE( point.y(), 37.0 );
906 md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), -1 );
907 QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
908 QCOMPARE( point.x(), 142.0 );
909 QCOMPARE( point.y(), -37.0 );
910
911 // extractAltitudeFromMetadata
912 QVERIFY( !QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).isValid() );
913 md.insert( QStringLiteral( "EXIF_GPSAltitude" ), 10.5 );
914 QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
915 md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "0" ) );
916 QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
917 md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "00" ) );
918 QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
919 md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "1" ) );
920 QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), -10.5 );
921 md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "01" ) );
922 QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), -10.5 );
923 md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), 1 );
924 QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
925 md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), -1 );
926 QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), -10.5 );
927
928 // extractDirectionFromMetadata
929 QVERIFY( !QgsImportPhotosAlgorithm::extractDirectionFromMetadata( md ).isValid() );
930 md.insert( QStringLiteral( "EXIF_GPSImgDirection" ), 15 );
931 QCOMPARE( QgsImportPhotosAlgorithm::extractDirectionFromMetadata( md ).toDouble(), 15.0 );
932
933 // extractOrientationFromMetadata
934 QVERIFY( !QgsImportPhotosAlgorithm::extractOrientationFromMetadata( md ).isValid() );
935 md.insert( QStringLiteral( "EXIF_Orientation" ), 3 );
936 QCOMPARE( QgsImportPhotosAlgorithm::extractOrientationFromMetadata( md ).toInt(), 180 );
937
938 // extractTimestampFromMetadata
939 QVERIFY( !QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).isValid() );
940 md.insert( QStringLiteral( "EXIF_DateTimeOriginal" ), QStringLiteral( "xx" ) );
941 QVERIFY( !QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).isValid() );
942 md.insert( QStringLiteral( "EXIF_DateTimeOriginal" ), QStringLiteral( "2017:12:27 19:20:52" ) );
943 QCOMPARE( QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).toDateTime(), QDateTime( QDate( 2017, 12, 27 ), QTime( 19, 20, 52 ) ) );
944 md.remove( QStringLiteral( "EXIF_DateTimeOriginal" ) );
945 md.insert( QStringLiteral( "EXIF_DateTimeDigitized" ), QStringLiteral( "2017:12:27 19:20:52" ) );
946 QCOMPARE( QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).toDateTime(), QDateTime( QDate( 2017, 12, 27 ), QTime( 19, 20, 52 ) ) );
947 md.remove( QStringLiteral( "EXIF_DateTimeDigitized" ) );
948 md.insert( QStringLiteral( "EXIF_DateTime" ), QStringLiteral( "2017:12:27 19:20:52" ) );
949 QCOMPARE( QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).toDateTime(), QDateTime( QDate( 2017, 12, 27 ), QTime( 19, 20, 52 ) ) );
950
951 }
952
featureFilterAlg()953 void TestQgsProcessingAlgs::featureFilterAlg()
954 {
955 const QgsProcessingAlgorithm *filterAlgTemplate = QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:filter" ) );
956
957 Q_ASSERT( filterAlgTemplate->outputDefinitions().isEmpty() );
958
959 QVariantList outputs;
960 QVariantMap output1;
961 output1.insert( QStringLiteral( "name" ), QStringLiteral( "test" ) );
962 output1.insert( QStringLiteral( "expression" ), QStringLiteral( "TRUE" ) );
963 output1.insert( QStringLiteral( "isModelOutput" ), true );
964
965 outputs.append( output1 );
966
967 QVariantMap config1;
968 config1.insert( QStringLiteral( "outputs" ), outputs );
969
970 std::unique_ptr<QgsProcessingAlgorithm> filterAlg( filterAlgTemplate->create( config1 ) );
971
972 QCOMPARE( filterAlg->outputDefinitions().size(), 1 );
973
974 auto outputDef = filterAlg->outputDefinition( QStringLiteral( "OUTPUT_test" ) );
975 QCOMPARE( outputDef->type(), QStringLiteral( "outputVector" ) );
976
977 auto outputParamDef = filterAlg->parameterDefinition( "OUTPUT_test" );
978 Q_ASSERT( outputParamDef->flags() & QgsProcessingParameterDefinition::FlagIsModelOutput );
979 Q_ASSERT( outputParamDef->flags() & QgsProcessingParameterDefinition::FlagHidden );
980
981 QVariantMap output2;
982 output2.insert( QStringLiteral( "name" ), QStringLiteral( "nonmodeloutput" ) );
983 output2.insert( QStringLiteral( "expression" ), QStringLiteral( "TRUE" ) );
984 output2.insert( QStringLiteral( "isModelOutput" ), false );
985
986 outputs.append( output2 );
987
988 QVariantMap config2;
989 config2.insert( QStringLiteral( "outputs" ), outputs );
990
991 std::unique_ptr<QgsProcessingAlgorithm> filterAlg2( filterAlgTemplate->create( config2 ) );
992
993 QCOMPARE( filterAlg2->outputDefinitions().size(), 2 );
994
995 auto outputDef2 = filterAlg2->outputDefinition( QStringLiteral( "OUTPUT_nonmodeloutput" ) );
996 QCOMPARE( outputDef2->type(), QStringLiteral( "outputVector" ) );
997
998 auto outputParamDef2 = filterAlg2->parameterDefinition( "OUTPUT_nonmodeloutput" );
999 Q_ASSERT( !outputParamDef2->flags().testFlag( QgsProcessingParameterDefinition::FlagIsModelOutput ) );
1000 Q_ASSERT( outputParamDef2->flags() & QgsProcessingParameterDefinition::FlagHidden );
1001 }
1002
transformAlg()1003 void TestQgsProcessingAlgs::transformAlg()
1004 {
1005 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:reprojectlayer" ) ) );
1006 QVERIFY( alg != nullptr );
1007
1008 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
1009 QgsProject p;
1010 context->setProject( &p );
1011
1012 QgsProcessingFeedback feedback;
1013
1014 QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326field=col1:integer" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
1015 QVERIFY( layer->isValid() );
1016 QgsFeature f;
1017 // add a point with a bad geometry - this should result in a transform exception!
1018 f.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( -96215069, 41.673559 ) ) );
1019 QVERIFY( layer->dataProvider()->addFeature( f ) );
1020 p.addMapLayer( layer );
1021
1022 QVariantMap parameters;
1023 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "test" ) );
1024 parameters.insert( QStringLiteral( "OUTPUT" ), QStringLiteral( "memory:" ) );
1025 parameters.insert( QStringLiteral( "TARGET_CRS" ), QStringLiteral( "EPSG:2163" ) );
1026 bool ok = false;
1027 const QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
1028 Q_UNUSED( results )
1029 QVERIFY( ok );
1030 }
1031
kmeansCluster()1032 void TestQgsProcessingAlgs::kmeansCluster()
1033 {
1034 // make some features
1035 std::vector< QgsKMeansClusteringAlgorithm::Feature > features;
1036 std::vector< QgsPointXY > centers( 2 );
1037
1038 // no features, no crash
1039 int k = 2;
1040 QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
1041 QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
1042
1043 // features < clusters
1044 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
1045 QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
1046 QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
1047 QCOMPARE( features[ 0 ].cluster, 0 );
1048
1049 // features == clusters
1050 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 11, 5 ) ) );
1051 QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
1052 QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
1053 QCOMPARE( features[ 0 ].cluster, 1 );
1054 QCOMPARE( features[ 1 ].cluster, 0 );
1055
1056 // features > clusters
1057 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 13, 3 ) ) );
1058 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 13, 13 ) ) );
1059 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 23, 6 ) ) );
1060 k = 2;
1061 QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
1062 QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
1063 QCOMPARE( features[ 0 ].cluster, 1 );
1064 QCOMPARE( features[ 1 ].cluster, 1 );
1065 QCOMPARE( features[ 2 ].cluster, 0 );
1066 QCOMPARE( features[ 3 ].cluster, 0 );
1067 QCOMPARE( features[ 4 ].cluster, 0 );
1068
1069 // repeat above, with 3 clusters
1070 k = 3;
1071 centers.resize( 3 );
1072 QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
1073 QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
1074 QCOMPARE( features[ 0 ].cluster, 1 );
1075 QCOMPARE( features[ 1 ].cluster, 2 );
1076 QCOMPARE( features[ 2 ].cluster, 2 );
1077 QCOMPARE( features[ 3 ].cluster, 2 );
1078 QCOMPARE( features[ 4 ].cluster, 0 );
1079
1080 // with identical points
1081 features.clear();
1082 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
1083 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
1084 features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
1085 QCOMPARE( features[ 0 ].cluster, -1 );
1086 QCOMPARE( features[ 1 ].cluster, -1 );
1087 QCOMPARE( features[ 2 ].cluster, -1 );
1088 }
1089
categorizeByStyle()1090 void TestQgsProcessingAlgs::categorizeByStyle()
1091 {
1092 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:categorizeusingstyle" ) ) );
1093 QVERIFY( alg != nullptr );
1094
1095 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
1096 QgsProject p;
1097 context->setProject( &p );
1098
1099 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
1100 const QString styleFileName = dataDir + "/categorized.xml";
1101
1102
1103 QgsProcessingFeedback feedback;
1104
1105 QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326&field=col1:string" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
1106 QVERIFY( layer->isValid() );
1107 QgsFeature f, f2, f3;
1108 f.setAttributes( QgsAttributes() << "a" );
1109 QVERIFY( layer->dataProvider()->addFeature( f ) );
1110 f2.setAttributes( QgsAttributes() << "b" );
1111 QVERIFY( layer->dataProvider()->addFeature( f2 ) );
1112 f3.setAttributes( QgsAttributes() << "c " );
1113 QVERIFY( layer->dataProvider()->addFeature( f3 ) );
1114 p.addMapLayer( layer );
1115
1116 QVariantMap parameters;
1117 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "test" ) );
1118 parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "col1" ) );
1119 parameters.insert( QStringLiteral( "STYLE" ), styleFileName );
1120 parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), true );
1121 parameters.insert( QStringLiteral( "TOLERANT" ), false );
1122 parameters.insert( QStringLiteral( "NON_MATCHING_CATEGORIES" ), QStringLiteral( "memory:" ) );
1123 parameters.insert( QStringLiteral( "NON_MATCHING_SYMBOLS" ), QStringLiteral( "memory:" ) );
1124
1125 bool ok = false;
1126 QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
1127 QVERIFY( ok );
1128 context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
1129 QgsCategorizedSymbolRenderer *catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
1130 QVERIFY( catRenderer );
1131
1132 auto allValues = []( QgsVectorLayer * layer )->QStringList
1133 {
1134 QStringList all;
1135 QgsFeature f;
1136 QgsFeatureIterator it = layer->getFeatures();
1137 while ( it.nextFeature( f ) )
1138 {
1139 all.append( f.attribute( 0 ).toString() );
1140 }
1141 return all;
1142 };
1143 QgsVectorLayer *nonMatchingCats = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
1144 QCOMPARE( allValues( nonMatchingCats ), QStringList() << "b" << "c " );
1145 QgsVectorLayer *nonMatchingSymbols = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
1146 QCOMPARE( allValues( nonMatchingSymbols ), QStringList() << " ----c/- " << "B " );
1147
1148 QCOMPARE( catRenderer->categories().count(), 3 );
1149 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
1150 QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name() != QLatin1String( "#00ff00" ) );
1151 QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name() != QLatin1String( "#0000ff" ) );
1152 // reset renderer
1153 layer->setRenderer( new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) ) );
1154
1155 // case insensitive
1156 parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), false );
1157 ok = false;
1158 results = alg->run( parameters, *context, &feedback, &ok );
1159 QVERIFY( ok );
1160 context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
1161 catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
1162 QVERIFY( catRenderer );
1163
1164 nonMatchingCats = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
1165 QCOMPARE( allValues( nonMatchingCats ), QStringList() << "c " );
1166 nonMatchingSymbols = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
1167 QCOMPARE( allValues( nonMatchingSymbols ), QStringList() << " ----c/- " );
1168
1169 QCOMPARE( catRenderer->categories().count(), 3 );
1170 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
1171 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name(), QStringLiteral( "#00ff00" ) );
1172 QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name() != QLatin1String( "#0000ff" ) );
1173 // reset renderer
1174 layer->setRenderer( new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) ) );
1175
1176 // tolerant
1177 parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), true );
1178 parameters.insert( QStringLiteral( "TOLERANT" ), true );
1179
1180 ok = false;
1181 results = alg->run( parameters, *context, &feedback, &ok );
1182 QVERIFY( ok );
1183 context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
1184 catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
1185 QVERIFY( catRenderer );
1186
1187 nonMatchingCats = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
1188 QCOMPARE( allValues( nonMatchingCats ), QStringList() << "b" );
1189 nonMatchingSymbols = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
1190 QCOMPARE( allValues( nonMatchingSymbols ), QStringList() << "B " );
1191
1192 QCOMPARE( catRenderer->categories().count(), 3 );
1193 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
1194 QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name() != QLatin1String( "#00ff00" ) );
1195 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name(), QStringLiteral( "#0000ff" ) );
1196 // reset renderer
1197 layer->setRenderer( new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) ) );
1198
1199 // no optional sinks
1200 parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), false );
1201 parameters.remove( QStringLiteral( "NON_MATCHING_CATEGORIES" ) );
1202 parameters.remove( QStringLiteral( "NON_MATCHING_SYMBOLS" ) );
1203 ok = false;
1204 results = alg->run( parameters, *context, &feedback, &ok );
1205 QVERIFY( ok );
1206 context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
1207 catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
1208 QVERIFY( catRenderer );
1209
1210 QVERIFY( !context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
1211 QVERIFY( !context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
1212
1213 QCOMPARE( catRenderer->categories().count(), 3 );
1214 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
1215 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name(), QStringLiteral( "#00ff00" ) );
1216 QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name(), QStringLiteral( "#0000ff" ) );
1217 }
1218
extractBinary()1219 void TestQgsProcessingAlgs::extractBinary()
1220 {
1221 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:extractbinary" ) ) );
1222 QVERIFY( alg != nullptr );
1223
1224 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
1225 QgsProject p;
1226 context->setProject( &p );
1227
1228 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
1229 const QString source = dataDir + QStringLiteral( "/attachments.gdb|layername=points__ATTACH" );
1230
1231 QVariantMap parameters;
1232 parameters.insert( QStringLiteral( "INPUT" ), source );
1233 parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "DATA" ) );
1234 parameters.insert( QStringLiteral( "FILENAME" ), QStringLiteral( "'test' || \"ATTACHMENTID\" || '.jpg'" ) );
1235 const QString folder = QDir::tempPath();
1236 parameters.insert( QStringLiteral( "FOLDER" ), folder );
1237
1238 bool ok = false;
1239 QgsProcessingFeedback feedback;
1240 const QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
1241 QVERIFY( ok );
1242
1243 QCOMPARE( results.count(), 1 );
1244 QCOMPARE( results.value( QStringLiteral( "FOLDER" ) ).toString(), folder );
1245
1246 QFile file( folder + "/test1.jpg" );
1247 QVERIFY( file.open( QIODevice::ReadOnly ) );
1248 QByteArray d = file.readAll();
1249 QCOMPARE( QString( QCryptographicHash::hash( d, QCryptographicHash::Md5 ).toHex() ), QStringLiteral( "ef3dbc530cc39a545832a6c82aac57b6" ) );
1250
1251 QFile file2( folder + "/test2.jpg" );
1252 QVERIFY( file2.open( QIODevice::ReadOnly ) );
1253 d = file2.readAll();
1254 QCOMPARE( QString( QCryptographicHash::hash( d, QCryptographicHash::Md5 ).toHex() ), QStringLiteral( "4b952b80e4288ca5111be2f6dd5d6809" ) );
1255 }
1256
createDirectory()1257 void TestQgsProcessingAlgs::createDirectory()
1258 {
1259 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createdirectory" ) ) );
1260 QVERIFY( alg != nullptr );
1261
1262 // first make a path to an existing file
1263 QString outputPath = QDir::tempPath() + "/test.txt";
1264 if ( QFile::exists( outputPath ) )
1265 QFile::remove( outputPath );
1266 QFile file( outputPath );
1267 file.open( QIODevice::ReadWrite );
1268 file.close();
1269
1270 QVariantMap parameters;
1271 parameters.insert( QStringLiteral( "PATH" ), outputPath );
1272
1273 bool ok = false;
1274 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
1275 QgsProcessingFeedback feedback;
1276 QVariantMap results;
1277 results = alg->run( parameters, *context, &feedback, &ok );
1278 // path is an existing file
1279 QVERIFY( !ok );
1280
1281 outputPath = QDir::tempPath() + "/createdir/test/test2";
1282 QDir().rmdir( QDir::tempPath() + "/createdir/test/test2" );
1283 QDir().rmdir( QDir::tempPath() + "/createdir/test" );
1284 QDir().rmdir( QDir::tempPath() + "/createdir" );
1285
1286 parameters.insert( QStringLiteral( "PATH" ), outputPath );
1287 results = alg->run( parameters, *context, &feedback, &ok );
1288 QVERIFY( ok );
1289
1290 QVERIFY( QFile::exists( outputPath ) );
1291 QVERIFY( QFileInfo( outputPath ).isDir() );
1292
1293 // run a second time -- should be OK, no exception raised
1294 results = alg->run( parameters, *context, &feedback, &ok );
1295 QVERIFY( ok );
1296
1297 QVERIFY( QFile::exists( outputPath ) );
1298 QVERIFY( QFileInfo( outputPath ).isDir() );
1299 }
1300
flattenRelations()1301 void TestQgsProcessingAlgs::flattenRelations()
1302 {
1303 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:flattenrelationships" ) ) );
1304 QVERIFY( alg != nullptr );
1305
1306 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
1307 QgsProject p;
1308 context->setProject( &p );
1309
1310 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
1311
1312 QgsProcessingFeedback feedback;
1313
1314 QgsVectorLayer *parent = new QgsVectorLayer( dataDir + QStringLiteral( "/points_relations.shp" ), QStringLiteral( "parent" ) );
1315 QgsVectorLayer *child = new QgsVectorLayer( dataDir + QStringLiteral( "/points.shp" ), QStringLiteral( "child" ) );
1316 QVERIFY( parent->isValid() );
1317 QVERIFY( child->isValid() );
1318 p.addMapLayer( parent );
1319 p.addMapLayer( child );
1320
1321 QVariantMap parameters;
1322 parameters.insert( QStringLiteral( "INPUT" ), parent->id() );
1323 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
1324
1325 bool ok = false;
1326 QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
1327 // no relations!
1328 QVERIFY( !ok );
1329
1330 // create relationship
1331 const QgsRelationContext relationContext( &p );
1332 QgsRelation relation( relationContext );
1333 relation.setId( QStringLiteral( "rel" ) );
1334 relation.setName( QStringLiteral( "my relation" ) );
1335 relation.setReferencedLayer( parent->id() );
1336 relation.setReferencingLayer( child->id() );
1337 relation.addFieldPair( QStringLiteral( "class" ), QStringLiteral( "class" ) );
1338 QVERIFY( relation.isValid() );
1339 p.relationManager()->addRelation( relation );
1340
1341 results = alg->run( parameters, *context, &feedback, &ok );
1342 // one relation - should be ok!
1343 QVERIFY( ok );
1344
1345 QgsVectorLayer *outputLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
1346 QVERIFY( outputLayer );
1347 QCOMPARE( outputLayer->fields().count(), 8 );
1348 QCOMPARE( outputLayer->fields().at( 0 ).name(), QStringLiteral( "Class" ) );
1349 QCOMPARE( outputLayer->fields().at( 1 ).name(), QStringLiteral( "id" ) );
1350 QCOMPARE( outputLayer->fields().at( 2 ).name(), QStringLiteral( "Class_2" ) );
1351 QCOMPARE( outputLayer->fields().at( 3 ).name(), QStringLiteral( "Heading" ) );
1352 QCOMPARE( outputLayer->fields().at( 4 ).name(), QStringLiteral( "Importance" ) );
1353 QCOMPARE( outputLayer->fields().at( 5 ).name(), QStringLiteral( "Pilots" ) );
1354 QCOMPARE( outputLayer->fields().at( 6 ).name(), QStringLiteral( "Cabin Crew" ) );
1355 QCOMPARE( outputLayer->fields().at( 7 ).name(), QStringLiteral( "Staff" ) );
1356
1357 QCOMPARE( outputLayer->featureCount(), 17L );
1358
1359 QSet< QgsAttributes > res;
1360 QgsFeature f;
1361 QgsFeatureIterator it = outputLayer->getFeatures();
1362 while ( it.nextFeature( f ) )
1363 {
1364 QgsAttributes created = f.attributes();
1365 created << f.geometry().asWkt( 0 );
1366 res.insert( created );
1367 }
1368
1369 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 0LL << 10.0 << 2 << 1 << 3 << QStringLiteral( "Point (-103 23)" ) ) );
1370 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 12LL << 10.0 << 1 << 1 << 2 << QStringLiteral( "Point (-103 23)" ) ) );
1371 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 34LL << 10.0 << 2 << 1 << 3 << QStringLiteral( "Point (-103 23)" ) ) );
1372 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 80LL << 10.0 << 2 << 1 << 3 << QStringLiteral( "Point (-103 23)" ) ) );
1373 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 90LL << 3.0 << 2 << 0 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
1374 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 85LL << 3.0 << 1 << 1 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
1375 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 95LL << 3.0 << 1 << 1 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
1376 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 90LL << 3.0 << 1 << 0 << 1 << QStringLiteral( "Point (-117 37)" ) ) );
1377 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 160LL << 4.0 << 2 << 0 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
1378 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 180LL << 3.0 << 1 << 0 << 1 << QStringLiteral( "Point (-117 37)" ) ) );
1379 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 140LL << 10.0 << 1 << 1 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
1380 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 100LL << 20.0 << 3 << 0 << 3 << QStringLiteral( "Point (-117 37)" ) ) );
1381 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 0LL << 1.0 << 3 << 3 << 6 << QStringLiteral( "Point (-83 34)" ) ) );
1382 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 340LL << 1.0 << 3 << 3 << 6 << QStringLiteral( "Point (-83 34)" ) ) );
1383 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 300LL << 1.0 << 3 << 2 << 5 << QStringLiteral( "Point (-83 34)" ) ) );
1384 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 270LL << 1.0 << 3 << 4 << 7 << QStringLiteral( "Point (-83 34)" ) ) );
1385 QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 240LL << 1.0 << 3 << 2 << 5 << QStringLiteral( "Point (-83 34)" ) ) );
1386
1387 QgsRelation relation2( relationContext );
1388 relation2.setId( QStringLiteral( "rel2" ) );
1389 relation2.setName( QStringLiteral( "my relation2" ) );
1390 relation2.setReferencedLayer( parent->id() );
1391 relation2.setReferencingLayer( child->id() );
1392 relation2.addFieldPair( QStringLiteral( "class" ), QStringLiteral( "class" ) );
1393 QVERIFY( relation2.isValid() );
1394 p.relationManager()->addRelation( relation2 );
1395 results = alg->run( parameters, *context, &feedback, &ok );
1396 // two relations - should not run
1397 QVERIFY( !ok );
1398 }
1399
1400
polygonsToLines_data()1401 void TestQgsProcessingAlgs::polygonsToLines_data()
1402 {
1403 QTest::addColumn<QgsGeometry>( "sourceGeometry" );
1404 QTest::addColumn<QgsGeometry>( "expectedGeometry" );
1405
1406 QTest::newRow( "Simple Polygon" )
1407 << QgsGeometry::fromWkt( "Polygon((1 1, 2 2, 1 3, 1 1))" )
1408 << QgsGeometry::fromWkt( "MultiLineString ((1 1, 2 2, 1 3, 1 1))" );
1409
1410 const QgsGeometry geomNoRing( std::make_unique<QgsMultiPolygon>() );
1411
1412 QTest::newRow( "Polygon without exterior ring" )
1413 << geomNoRing
1414 << QgsGeometry::fromWkt( "MultiLineString ()" );
1415
1416 QTest::newRow( "MultiPolygon" )
1417 << QgsGeometry::fromWkt( "MultiPolygon(((1 1, 2 2, 1 3, 1 1)), ((0 0, 0 10, 10 10, 10 0, 0 0), (3 3, 3 6, 6 6, 6 3, 3 3)))" )
1418 << QgsGeometry::fromWkt( "MultiLineString ((1 1, 2 2, 1 3, 1 1),(0 0, 0 10, 10 10, 10 0, 0 0),(3 3, 3 6, 6 6, 6 3, 3 3))" );
1419
1420 QTest::newRow( "Polygon with inner ring" )
1421 << QgsGeometry::fromWkt( "Polygon((0 0, 0 10, 10 10, 10 0, 0 0), (3 3, 3 6, 6 6, 6 3, 3 3))" )
1422 << QgsGeometry::fromWkt( "MultiLineString ((0 0, 0 10, 10 10, 10 0, 0 0),(3 3, 3 6, 6 6, 6 3, 3 3))" );
1423 }
1424
1425
polygonsToLines()1426 void TestQgsProcessingAlgs::polygonsToLines()
1427 {
1428 QFETCH( QgsGeometry, sourceGeometry );
1429 QFETCH( QgsGeometry, expectedGeometry );
1430
1431 const std::unique_ptr< QgsProcessingFeatureBasedAlgorithm > alg( featureBasedAlg( "native:polygonstolines" ) );
1432
1433 QgsFeature feature;
1434 feature.setGeometry( sourceGeometry );
1435
1436 const QgsFeature result = runForFeature( alg, feature, QStringLiteral( "Polygon" ) );
1437
1438 QVERIFY2( result.geometry().equals( expectedGeometry ), QStringLiteral( "Result: %1, Expected: %2" ).arg( result.geometry().asWkt(), expectedGeometry.asWkt() ).toUtf8().constData() );
1439 }
1440
Q_DECLARE_METATYPE(Qgis::DataType)1441 Q_DECLARE_METATYPE( Qgis::DataType )
1442 void TestQgsProcessingAlgs::createConstantRaster_data()
1443 {
1444 QTest::addColumn<QString>( "inputExtent" );
1445 QTest::addColumn<QString>( "expectedRaster" );
1446 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
1447 QTest::addColumn<QString>( "crs" );
1448 QTest::addColumn<double>( "pixelSize" );
1449 QTest::addColumn<double>( "constantValue" );
1450 QTest::addColumn<int>( "typeId" );
1451
1452 /*
1453 * Testcase 1
1454 *
1455 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1456 * crs = EPSG:4326
1457 * pixelSize = 1.0
1458 * constantValue = 12
1459 * Byte Raster Layer
1460 *
1461 */
1462 QTest::newRow( "testcase 1" )
1463 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1464 << QStringLiteral( "/createConstantRaster_testcase1.tif" )
1465 << Qgis::DataType::Byte
1466 << "EPSG:4326"
1467 << 1.0
1468 << 12.0
1469 << 0;
1470
1471 /*
1472 * Testcase 2
1473 *
1474 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1475 * crs = EPSG:4326
1476 * pixelSize = 1.0
1477 * constantValue = -1
1478 * Byte Raster Layer
1479 *
1480 */
1481 QTest::newRow( "testcase 2" )
1482 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1483 << QStringLiteral( "" )
1484 << Qgis::DataType::Byte
1485 << "EPSG:4326"
1486 << 1.0
1487 << -1.0 //fails --> value too small for byte
1488 << 0;
1489
1490
1491 /*
1492 * Testcase 3
1493 *
1494 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1495 * crs = EPSG:4326
1496 * pixelSize = 1.0
1497 * constantValue = -1
1498 * Byte Raster Layer
1499 *
1500 */
1501 QTest::newRow( "testcase 3" )
1502 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1503 << QStringLiteral( "" )
1504 << Qgis::DataType::Byte
1505 << "EPSG:4326"
1506 << 1.0
1507 << 256.0 //fails --> value too big for byte
1508 << 0;
1509
1510 /*
1511 * Testcase 4
1512 *
1513 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1514 * crs = EPSG:4326
1515 * pixelSize = 1.0
1516 * constantValue = 12
1517 * Int16 Raster Layer
1518 *
1519 */
1520 QTest::newRow( "testcase 4" )
1521 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1522 << QStringLiteral( "/createConstantRaster_testcase4.tif" )
1523 << Qgis::DataType::Int16
1524 << "EPSG:4326"
1525 << 1.0
1526 << 12.0
1527 << 1;
1528
1529 /*
1530 * Testcase 5
1531 *
1532 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1533 * crs = EPSG:4326
1534 * pixelSize = 1.0
1535 * constantValue = 12
1536 * Int16 Raster Layer
1537 *
1538 */
1539 QTest::newRow( "testcase 5" )
1540 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1541 << QStringLiteral( "" )
1542 << Qgis::DataType::Int16
1543 << "EPSG:4326"
1544 << 1.0
1545 << -32769.0
1546 << 1;
1547
1548 /*
1549 * Testcase 6
1550 *
1551 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1552 * crs = EPSG:4326
1553 * pixelSize = 1.0
1554 * constantValue = 12
1555 * Int16 Raster Layer
1556 *
1557 */
1558 QTest::newRow( "testcase 6" )
1559 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1560 << QStringLiteral( "" )
1561 << Qgis::DataType::Int16
1562 << "EPSG:4326"
1563 << 1.0
1564 << 32769.0
1565 << 1;
1566
1567 /*
1568 * Testcase 7
1569 *
1570 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1571 * crs = EPSG:4326
1572 * pixelSize = 1.0
1573 * constantValue = 12
1574 * UInt16 Raster Layer
1575 *
1576 */
1577 QTest::newRow( "testcase 7" )
1578 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1579 << QStringLiteral( "/createConstantRaster_testcase7.tif" )
1580 << Qgis::DataType::UInt16
1581 << "EPSG:4326"
1582 << 1.0
1583 << 12.0
1584 << 2;
1585
1586 /*
1587 * Testcase 8
1588 *
1589 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1590 * crs = EPSG:4326
1591 * pixelSize = 1.0
1592 * constantValue = 12
1593 * UInt16 Raster Layer
1594 *
1595 */
1596 QTest::newRow( "testcase 8" )
1597 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1598 << QStringLiteral( "" )
1599 << Qgis::DataType::UInt16
1600 << "EPSG:4326"
1601 << 1.0
1602 << -1.0
1603 << 2;
1604
1605 /*
1606 * Testcase 9
1607 *
1608 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1609 * crs = EPSG:4326
1610 * pixelSize = 1.0
1611 * constantValue = 12
1612 * UInt16 Raster Layer
1613 *
1614 */
1615 QTest::newRow( "testcase 9" )
1616 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1617 << QStringLiteral( "" )
1618 << Qgis::DataType::UInt16
1619 << "EPSG:4326"
1620 << 1.0
1621 << 65536.0
1622 << 2;
1623
1624 /*
1625 * Testcase 10
1626 *
1627 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1628 * crs = EPSG:4326
1629 * pixelSize = 1.0
1630 * constantValue = 12
1631 * UInt16 Raster Layer
1632 *
1633 */
1634 QTest::newRow( "testcase 10" )
1635 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1636 << QStringLiteral( "/createConstantRaster_testcase10.tif" )
1637 << Qgis::DataType::Int32
1638 << "EPSG:4326"
1639 << 1.0
1640 << 12.0
1641 << 3;
1642
1643 /*
1644 * Testcase 10
1645 *
1646 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1647 * crs = EPSG:4326
1648 * pixelSize = 1.0
1649 * constantValue = 12
1650 * Int32 Raster Layer
1651 *
1652 */
1653 QTest::newRow( "testcase 10" )
1654 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1655 << QStringLiteral( "/createConstantRaster_testcase10.tif" )
1656 << Qgis::DataType::Int32
1657 << "EPSG:4326"
1658 << 1.0
1659 << 12.0
1660 << 3;
1661
1662 /*
1663 * Testcase 11
1664 *
1665 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1666 * crs = EPSG:4326
1667 * pixelSize = 1.0
1668 * constantValue = -2147483649.0
1669 * Int32 Raster Layer
1670 *
1671 */
1672 QTest::newRow( "testcase 11" )
1673 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1674 << QStringLiteral( "" )
1675 << Qgis::DataType::Int32
1676 << "EPSG:4326"
1677 << 1.0
1678 << -2147483649.0
1679 << 3;
1680
1681 /*
1682 * Testcase 12
1683 *
1684 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1685 * crs = EPSG:4326
1686 * pixelSize = 1.0
1687 * constantValue = 2147483649.0
1688 * Int32 Raster Layer
1689 *
1690 */
1691 QTest::newRow( "testcase 12" )
1692 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1693 << QStringLiteral( "" )
1694 << Qgis::DataType::Int32
1695 << "EPSG:4326"
1696 << 1.0
1697 << 2147483649.0
1698 << 3;
1699
1700 /*
1701 * Testcase 13
1702 *
1703 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1704 * crs = EPSG:4326
1705 * pixelSize = 1.0
1706 * constantValue = 12.0
1707 * UInt32 Raster Layer
1708 *
1709 */
1710 QTest::newRow( "testcase 13" )
1711 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1712 << QStringLiteral( "/createConstantRaster_testcase13.tif" )
1713 << Qgis::DataType::UInt32
1714 << "EPSG:4326"
1715 << 1.0
1716 << 12.0
1717 << 4;
1718
1719 /*
1720 * Testcase 14
1721 *
1722 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1723 * crs = EPSG:4326
1724 * pixelSize = 1.0
1725 * constantValue = 4294967296.0
1726 * UInt32 Raster Layer
1727 *
1728 */
1729 QTest::newRow( "testcase 14" )
1730 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1731 << QStringLiteral( "" )
1732 << Qgis::DataType::UInt32
1733 << "EPSG:4326"
1734 << 1.0
1735 << 4294967296.0
1736 << 4;
1737
1738 /*
1739 * Testcase 15
1740 *
1741 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1742 * crs = EPSG:4326
1743 * pixelSize = 1.0
1744 * constantValue = -1.0
1745 * UInt32 Raster Layer
1746 *
1747 */
1748 QTest::newRow( "testcase 14" )
1749 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1750 << QStringLiteral( "" )
1751 << Qgis::DataType::UInt32
1752 << "EPSG:4326"
1753 << 1.0
1754 << -1.0
1755 << 4;
1756
1757 /*
1758 * Testcase 16
1759 *
1760 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1761 * crs = EPSG:4326
1762 * pixelSize = 1.0
1763 * constantValue = 12.0
1764 * Float32 Raster Layer
1765 *
1766 */
1767 QTest::newRow( "testcase 16" )
1768 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1769 << QStringLiteral( "/createConstantRaster_testcase16.tif" )
1770 << Qgis::DataType::Float32
1771 << "EPSG:4326"
1772 << 1.0
1773 << 12.12
1774 << 5;
1775
1776 /*
1777 * Testcase 17
1778 *
1779 * inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
1780 * crs = EPSG:4326
1781 * pixelSize = 1.0
1782 * constantValue = 12.0
1783 * Float64 Raster Layer
1784 *
1785 */
1786 QTest::newRow( "testcase 17" )
1787 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
1788 << QStringLiteral( "/createConstantRaster_testcase17.tif" )
1789 << Qgis::DataType::Float64
1790 << "EPSG:4326"
1791 << 1.0
1792 << 12.125789212532487
1793 << 6;
1794
1795
1796 }
1797
createConstantRaster()1798 void TestQgsProcessingAlgs::createConstantRaster()
1799 {
1800 QFETCH( QString, inputExtent );
1801 QFETCH( QString, expectedRaster );
1802 QFETCH( Qgis::DataType, expectedDataType );
1803 QFETCH( QString, crs );
1804 QFETCH( double, pixelSize );
1805 QFETCH( double, constantValue );
1806 QFETCH( int, typeId );
1807
1808 //prepare input params
1809 QgsProject p;
1810 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createconstantrasterlayer" ) ) );
1811
1812 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
1813
1814 //set project crs and ellipsoid from input layer
1815 p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
1816
1817 //set project after layer has been added so that transform context/ellipsoid from crs is also set
1818 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
1819 context->setProject( &p );
1820
1821 QVariantMap parameters;
1822
1823 parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
1824 parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
1825 parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
1826 parameters.insert( QStringLiteral( "NUMBER" ), constantValue );
1827 parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
1828 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
1829
1830 bool ok = false;
1831 QgsProcessingFeedback feedback;
1832 QVariantMap results;
1833
1834 if ( expectedRaster.isEmpty() )
1835 {
1836 //verify if user feedback for unacceptable values are thrown
1837 alg->run( parameters, *context, &feedback, &ok );
1838 QVERIFY( !ok );
1839 }
1840 else
1841 {
1842 //prepare expectedRaster
1843 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_constantRaster" + expectedRaster, "expectedDataset", "gdal" );
1844 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
1845 QgsRasterIterator expectedIter( expectedInterface.get() );
1846 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
1847
1848 //run alg...
1849 results = alg->run( parameters, *context, &feedback, &ok );
1850 QVERIFY( ok );
1851
1852 //...and check results with expected datasets
1853 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
1854 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
1855
1856 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
1857 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
1858 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
1859
1860 QgsRasterIterator outputIter( outputInterface.get() );
1861 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
1862 int outputIterLeft = 0;
1863 int outputIterTop = 0;
1864 int outputIterCols = 0;
1865 int outputIterRows = 0;
1866 int expectedIterLeft = 0;
1867 int expectedIterTop = 0;
1868 int expectedIterCols = 0;
1869 int expectedIterRows = 0;
1870
1871 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
1872 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
1873
1874 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
1875 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
1876 {
1877 for ( int row = 0; row < expectedIterRows; row++ )
1878 {
1879 for ( int column = 0; column < expectedIterCols; column++ )
1880 {
1881 const double expectedValue = expectedRasterBlock->value( row, column );
1882 const double outputValue = outputRasterBlock->value( row, column );
1883 QCOMPARE( outputValue, expectedValue );
1884 }
1885 }
1886 }
1887 }
1888 }
1889
1890
densifyGeometries_data()1891 void TestQgsProcessingAlgs::densifyGeometries_data()
1892 {
1893 QTest::addColumn<QgsGeometry>( "sourceGeometry" );
1894 QTest::addColumn<QgsGeometry>( "expectedGeometry" );
1895 QTest::addColumn<double>( "interval" );
1896 QTest::addColumn<QString>( "geometryType" );
1897
1898 QTest::newRow( "Null geometry" )
1899 << QgsGeometry()
1900 << QgsGeometry()
1901 << 0.1
1902 << "Point";
1903
1904 QTest::newRow( "PointZ" )
1905 << QgsGeometry::fromWkt( "PointZ( 1 2 3 )" )
1906 << QgsGeometry::fromWkt( "PointZ( 1 2 3 )" )
1907 << 0.1
1908 << "Point";
1909
1910 QTest::newRow( "MultiPoint" )
1911 << QgsGeometry::fromWkt( "MULTIPOINT ((155 271), (150 360), (260 360), (271 265), (280 260), (270 370), (154 354), (150 260))" )
1912 << QgsGeometry::fromWkt( "MULTIPOINT ((155 271), (150 360), (260 360), (271 265), (280 260), (270 370), (154 354), (150 260))" )
1913 << 0.1
1914 << "Point";
1915
1916 QTest::newRow( "LineString big distance" )
1917 << QgsGeometry::fromWkt( "LineString( 0 0, 10 0, 10 10 )" )
1918 << QgsGeometry::fromWkt( "LineString( 0 0, 10 0, 10 10 )" )
1919 << 100.
1920 << "LineString";
1921
1922 QTest::newRow( "LineString small distance" )
1923 << QgsGeometry::fromWkt( "LineString( 0 0, 10 0, 10 10 )" )
1924 << QgsGeometry::fromWkt( "LineString (0 0, 2.5 0, 5 0, 7.5 0, 10 0, 10 2.5, 10 5, 10 7.5, 10 10)" )
1925 << 3.
1926 << "LineString";
1927
1928 QTest::newRow( "LineStringZ" )
1929 << QgsGeometry::fromWkt( "LineStringZ( 0 0 1, 10 0 2, 10 10 0)" )
1930 << QgsGeometry::fromWkt( "LineStringZ (0 0 1, 5 0 1.5, 10 0 2, 10 5 1, 10 10 0)" )
1931 << 6.
1932 << "LineString";
1933
1934 QTest::newRow( "LineStringM" )
1935 << QgsGeometry::fromWkt( "LineStringM( 0 0 0, 10 0 2, 10 10 0)" )
1936 << QgsGeometry::fromWkt( "LineStringM (0 0 0, 2.5 0 0.5, 5 0 1, 7.5 0 1.5, 10 0 2, 10 2.5 1.5, 10 5 1, 10 7.5 0.5, 10 10 0)" )
1937 << 3.
1938 << "LineString";
1939
1940 QTest::newRow( "LineStringZM" )
1941 << QgsGeometry::fromWkt( "LineStringZM( 0 0 1 10, 10 0 2 8, 10 10 0 4)" )
1942 << QgsGeometry::fromWkt( "LineStringZM (0 0 1 10, 5 0 1.5 9, 10 0 2 8, 10 5 1 6, 10 10 0 4)" )
1943 << 6.
1944 << "LineString";
1945
1946 QTest::newRow( "Polygon" )
1947 << QgsGeometry::fromWkt( "Polygon(( 0 0, 20 0, 20 20, 0 0 ))" )
1948 << QgsGeometry::fromWkt( "Polygon ((0 0, 5 0, 10 0, 15 0, 20 0, 20 5, 20 10, 20 15, 20 20, 16 16, 12 12, 7.99999999999999822 7.99999999999999822, 4 4, 0 0))" )
1949 << 6.
1950 << "Polygon";
1951 }
1952
densifyGeometries()1953 void TestQgsProcessingAlgs::densifyGeometries()
1954 {
1955 QFETCH( QgsGeometry, sourceGeometry );
1956 QFETCH( QgsGeometry, expectedGeometry );
1957 QFETCH( double, interval );
1958 QFETCH( QString, geometryType );
1959
1960 const std::unique_ptr< QgsProcessingFeatureBasedAlgorithm > alg( featureBasedAlg( "native:densifygeometriesgivenaninterval" ) );
1961
1962 QVariantMap parameters;
1963 parameters.insert( QStringLiteral( "INTERVAL" ), interval );
1964
1965 QgsFeature feature;
1966 feature.setGeometry( sourceGeometry );
1967
1968 const QgsFeature result = runForFeature( alg, feature, geometryType, parameters );
1969
1970 if ( expectedGeometry.isNull() )
1971 QVERIFY( result.geometry().isNull() );
1972 else
1973 QVERIFY2( result.geometry().equals( expectedGeometry ), QStringLiteral( "Result: %1, Expected: %2" ).arg( result.geometry().asWkt(), expectedGeometry.asWkt() ).toUtf8().constData() );
1974 }
1975
fillNoData_data()1976 void TestQgsProcessingAlgs::fillNoData_data()
1977 {
1978 QTest::addColumn<QString>( "inputRaster" );
1979 QTest::addColumn<QString>( "expectedRaster" );
1980 QTest::addColumn<int>( "inputBand" );
1981 QTest::addColumn<double>( "fillValue" );
1982
1983 /*
1984 * Testcase 1
1985 *
1986 * NoData raster layer
1987 * band = 1
1988 * fillValue = 2.0
1989 */
1990 QTest::newRow( "testcase 1" )
1991 << "/raster/band1_int16_noct_epsg4326.tif"
1992 << QStringLiteral( "/fillnodata_testcase1.tif" )
1993 << 1
1994 << 2.0;
1995
1996 /*
1997 * Testcase 2
1998 *
1999 * WGS84 data without weights
2000 * searchRadius = 3
2001 * pixelSize = 1.8
2002 */
2003 QTest::newRow( "testcase 2" )
2004 << "/raster/band1_int16_noct_epsg4326.tif"
2005 << QStringLiteral( "/fillnodata_testcase2.tif" )
2006 << 1
2007 << 1.8;
2008
2009 /*
2010 * Testcase 3
2011 *
2012 * WGS84 data without weights
2013 * searchRadius = 3
2014 * pixelSize = 1.8
2015 */
2016 QTest::newRow( "testcase 2" )
2017 << "/raster/band1_float32_noct_epsg4326.tif"
2018 << QStringLiteral( "/fillnodata_testcase3.tif" )
2019 << 1
2020 << 1.8;
2021
2022 }
2023
fillNoData()2024 void TestQgsProcessingAlgs::fillNoData()
2025 {
2026 QFETCH( QString, inputRaster );
2027 QFETCH( QString, expectedRaster );
2028 QFETCH( int, inputBand );
2029 QFETCH( double, fillValue );
2030
2031 //prepare input params
2032 QgsProject p;
2033 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:fillnodata" ) ) );
2034
2035 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
2036
2037 std::unique_ptr<QgsRasterLayer> inputRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + inputRaster, "inputDataset", "gdal" );
2038
2039 //set project crs and ellipsoid from input layer
2040 p.setCrs( inputRasterLayer->crs(), true );
2041
2042 //set project after layer has been added so that transform context/ellipsoid from crs is also set
2043 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
2044 context->setProject( &p );
2045
2046 QVariantMap parameters;
2047
2048 parameters.insert( QStringLiteral( "INPUT" ), QString( myDataPath + inputRaster ) );
2049 parameters.insert( QStringLiteral( "BAND" ), inputBand );
2050 parameters.insert( QStringLiteral( "FILL_VALUE" ), fillValue );
2051 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
2052
2053 //prepare expectedRaster
2054 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/fillNoData/" + expectedRaster, "expectedDataset", "gdal" );
2055 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
2056 QgsRasterIterator expectedIter( expectedInterface.get() );
2057 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
2058
2059 //run alg...
2060
2061 bool ok = false;
2062 QgsProcessingFeedback feedback;
2063 QVariantMap results;
2064
2065 results = alg->run( parameters, *context, &feedback, &ok );
2066 QVERIFY( ok );
2067
2068 //...and check results with expected datasets
2069 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
2070 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
2071
2072 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
2073 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
2074
2075 QgsRasterIterator outputIter( outputInterface.get() );
2076 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
2077 int outputIterLeft = 0;
2078 int outputIterTop = 0;
2079 int outputIterCols = 0;
2080 int outputIterRows = 0;
2081 int expectedIterLeft = 0;
2082 int expectedIterTop = 0;
2083 int expectedIterCols = 0;
2084 int expectedIterRows = 0;
2085
2086 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
2087 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
2088
2089 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
2090 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
2091 {
2092 for ( int row = 0; row < expectedIterRows; row++ )
2093 {
2094 for ( int column = 0; column < expectedIterCols; column++ )
2095 {
2096 const double expectedValue = expectedRasterBlock->value( row, column );
2097 const double outputValue = outputRasterBlock->value( row, column );
2098 QCOMPARE( outputValue, expectedValue );
2099 }
2100 }
2101 }
2102 }
2103
lineDensity_data()2104 void TestQgsProcessingAlgs::lineDensity_data()
2105 {
2106 QTest::addColumn<QString>( "inputDataset" );
2107 QTest::addColumn<QString>( "expectedDataset" );
2108 QTest::addColumn<double>( "searchRadius" );
2109 QTest::addColumn<double>( "pixelSize" );
2110 QTest::addColumn<QString>( "weightField" );
2111
2112 /*
2113 * Testcase 1
2114 *
2115 * WGS84 data with weights
2116 * searchRadius = 3
2117 * pixelSize = 2
2118 */
2119 QTest::newRow( "testcase 1" )
2120 << "/linedensity.gml"
2121 << QStringLiteral( "/linedensity_testcase1.tif" )
2122 << 3.0
2123 << 2.0
2124 << QStringLiteral( "weight" );
2125
2126 /*
2127 * Testcase 2
2128 *
2129 * WGS84 data without weights
2130 * searchRadius = 3
2131 * pixelSize = 2
2132 */
2133 QTest::newRow( "testcase_2" )
2134 << "/linedensity.gml"
2135 << QStringLiteral( "/linedensity_testcase2.tif" )
2136 << 3.0
2137 << 2.0
2138 << QStringLiteral( "" );
2139
2140 }
2141
lineDensity()2142 void TestQgsProcessingAlgs::lineDensity()
2143 {
2144 QFETCH( QString, inputDataset );
2145 QFETCH( QString, expectedDataset );
2146 QFETCH( double, searchRadius );
2147 QFETCH( double, pixelSize );
2148 QFETCH( QString, weightField );
2149
2150 //prepare input params
2151 QgsProject p;
2152 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:linedensity" ) ) );
2153
2154 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
2155
2156 QgsVectorLayer *layer = new QgsVectorLayer( myDataPath + inputDataset + "|layername=linedensity", QStringLiteral( "layer" ), QStringLiteral( "ogr" ) );
2157 p.addMapLayer( layer );
2158 QVERIFY( layer->isValid() );
2159
2160 //set project crs and ellipsoid from input layer
2161 p.setCrs( layer->crs(), true );
2162
2163 //set project after layer has been added so that transform context/ellipsoid from crs is also set
2164 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
2165 context->setProject( &p );
2166
2167 QVariantMap parameters;
2168
2169 parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( layer ) );
2170 parameters.insert( QStringLiteral( "WEIGHT" ), weightField );
2171 parameters.insert( QStringLiteral( "RADIUS" ), searchRadius );
2172 parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
2173 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
2174
2175 //prepare expectedRaster
2176 std::unique_ptr<QgsRasterLayer> expectedRaster = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_raster_linedensity" + expectedDataset, "expectedDataset", "gdal" );
2177 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRaster->dataProvider()->clone() );
2178 QgsRasterIterator expectedIter( expectedInterface.get() );
2179 expectedIter.startRasterRead( 1, expectedRaster->width(), expectedRaster->height(), expectedInterface->extent() );
2180
2181 //run alg...
2182
2183 bool ok = false;
2184 QgsProcessingFeedback feedback;
2185 QVariantMap results;
2186
2187 results = alg->run( parameters, *context, &feedback, &ok );
2188 QVERIFY( ok );
2189
2190 //...and check results with expected datasets
2191 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
2192 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
2193
2194 QCOMPARE( outputRaster->width(), expectedRaster->width() );
2195 QCOMPARE( outputRaster->height(), expectedRaster->height() );
2196
2197 QgsRasterIterator outputIter( outputInterface.get() );
2198 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
2199 int outputIterLeft = 0;
2200 int outputIterTop = 0;
2201 int outputIterCols = 0;
2202 int outputIterRows = 0;
2203 int expectedIterLeft = 0;
2204 int expectedIterTop = 0;
2205 int expectedIterCols = 0;
2206 int expectedIterRows = 0;
2207
2208 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
2209 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
2210
2211 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
2212 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
2213 {
2214 for ( int row = 0; row < expectedIterRows; row++ )
2215 {
2216 for ( int column = 0; column < expectedIterCols; column++ )
2217 {
2218 const double expectedValue = expectedRasterBlock->value( row, column );
2219 const double outputValue = outputRasterBlock->value( row, column );
2220 QGSCOMPARENEAR( outputValue, expectedValue, 0.0000000002 );
2221 }
2222 }
2223 }
2224 }
2225
rasterLogicOp_data()2226 void TestQgsProcessingAlgs::rasterLogicOp_data()
2227 {
2228 QTest::addColumn<QVector< double >>( "input1" );
2229 QTest::addColumn<QVector< double >>( "input2" );
2230 QTest::addColumn<QVector< double >>( "input3" );
2231 QTest::addColumn<bool>( "treatNodataAsFalse" );
2232 QTest::addColumn<qgssize>( "expectedOrNoDataCount" );
2233 QTest::addColumn<qgssize>( "expectedOrTrueCount" );
2234 QTest::addColumn<qgssize>( "expectedOrFalseCount" );
2235 QTest::addColumn<QVector< double >>( "expectedOr" );
2236 QTest::addColumn<qgssize>( "expectedAndNoDataCount" );
2237 QTest::addColumn<qgssize>( "expectedAndTrueCount" );
2238 QTest::addColumn<qgssize>( "expectedAndFalseCount" );
2239 QTest::addColumn<QVector< double >>( "expectedAnd" );
2240 QTest::addColumn<int>( "nRows" );
2241 QTest::addColumn<int>( "nCols" );
2242 QTest::addColumn<double>( "destNoDataValue" );
2243 QTest::addColumn<int>( "dataType" );
2244
2245 QTest::newRow( "no nodata" ) << QVector< double > { 1, 2, 0, 0, 0, 0 }
2246 << QVector< double > { 1, 0, 1, 1, 0, 1 }
2247 << QVector< double > { 1, 2, 0, 0, 0, -1 }
2248 << false
2249 << 0ULL << 5ULL << 1ULL
2250 << QVector< double > { 1, 1, 1, 1, 0, 1 }
2251 << 0ULL << 1ULL << 5ULL
2252 << QVector< double > { 1, 0, 0, 0, 0, 0 }
2253 << 3 << 2
2254 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2255 QTest::newRow( "nodata" ) << QVector< double > { 1, -9999, 0, 0, 0, 0 }
2256 << QVector< double > { 1, 0, 1, 1, 0, 1 }
2257 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2258 << false
2259 << 2ULL << 3ULL << 1ULL
2260 << QVector< double > { 1, -9999, 1, -9999, 0, 1 }
2261 << 2ULL << 1ULL << 3ULL
2262 << QVector< double > { 1, -9999, 0, -9999, 0, 0 }
2263 << 3 << 2
2264 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2265 QTest::newRow( "nodata as false" ) << QVector< double > { 1, -9999, 0, 0, 0, 0 }
2266 << QVector< double > { 1, 0, 1, 1, 0, 1 }
2267 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2268 << true
2269 << 0ULL << 5ULL << 1ULL
2270 << QVector< double > { 1, 1, 1, 1, 0, 1 }
2271 << 0ULL << 1ULL << 5ULL
2272 << QVector< double > { 1, 0, 0, 0, 0, 0 }
2273 << 3 << 2
2274 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2275 QTest::newRow( "missing block 1" ) << QVector< double > {}
2276 << QVector< double > { 1, 0, 1, 1, 0, 1 }
2277 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2278 << false
2279 << 6ULL << 0ULL << 0ULL
2280 << QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
2281 << 6ULL << 0ULL << 0ULL
2282 << QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
2283 << 3 << 2
2284 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2285 QTest::newRow( "missing block 1 nodata as false" ) << QVector< double > {}
2286 << QVector< double > { 1, 0, 1, 1, 0, 1 }
2287 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2288 << true
2289 << 0ULL << 5ULL << 1ULL
2290 << QVector< double > { 1, 1, 1, 1, 0, 1 }
2291 << 0ULL << 0ULL << 6ULL
2292 << QVector< double > { 0, 0, 0, 0, 0, 0 }
2293 << 3 << 2
2294 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2295 QTest::newRow( "missing block 2" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
2296 << QVector< double > {}
2297 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2298 << false
2299 << 6ULL << 0ULL << 0ULL
2300 << QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
2301 << 6ULL << 0ULL << 0ULL
2302 << QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
2303 << 3 << 2
2304 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2305 QTest::newRow( "missing block 2 nodata as false" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
2306 << QVector< double > {}
2307 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2308 << true
2309 << 0ULL << 5ULL << 1ULL
2310 << QVector< double > { 1, 1, 1, 1, 0, 1 }
2311 << 0ULL << 0ULL << 6ULL
2312 << QVector< double > { 0, 0, 0, 0, 0, 0 }
2313 << 3 << 2
2314 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2315 QTest::newRow( "missing block 3" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
2316 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2317 << QVector< double > {}
2318 << false
2319 << 6ULL << 0ULL << 0ULL
2320 << QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
2321 << 6ULL << 0ULL << 0ULL
2322 << QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
2323 << 3 << 2
2324 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2325 QTest::newRow( "missing block 3 nodata as false" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
2326 << QVector< double > { 1, 2, 0, -9999, 0, -1 }
2327 << QVector< double > {}
2328 << true
2329 << 0ULL << 5ULL << 1ULL
2330 << QVector< double > { 1, 1, 1, 1, 0, 1 }
2331 << 0ULL << 0ULL << 6ULL
2332 << QVector< double > { 0, 0, 0, 0, 0, 0 }
2333 << 3 << 2
2334 << -9999.0 << static_cast< int >( Qgis::DataType::Float32 );
2335 }
2336
rasterLogicOp()2337 void TestQgsProcessingAlgs::rasterLogicOp()
2338 {
2339 QFETCH( QVector< double >, input1 );
2340 QFETCH( QVector< double >, input2 );
2341 QFETCH( QVector< double >, input3 );
2342 QVector< QVector< double > > input;
2343 input << input1 << input2 << input3;
2344 QFETCH( bool, treatNodataAsFalse );
2345 QFETCH( qgssize, expectedOrNoDataCount );
2346 QFETCH( qgssize, expectedOrTrueCount );
2347 QFETCH( qgssize, expectedOrFalseCount );
2348 QFETCH( QVector< double >, expectedOr );
2349 QFETCH( qgssize, expectedAndNoDataCount );
2350 QFETCH( qgssize, expectedAndTrueCount );
2351 QFETCH( qgssize, expectedAndFalseCount );
2352 QFETCH( QVector< double >, expectedAnd );
2353 QFETCH( int, nRows );
2354 QFETCH( int, nCols );
2355 QFETCH( double, destNoDataValue );
2356 QFETCH( int, dataType );
2357
2358 QgsRasterLogicalOrAlgorithm orAlg;
2359 QgsRasterLogicalAndAlgorithm andAlg;
2360
2361 const QgsRectangle extent = QgsRectangle( 0, 0, nRows, nCols );
2362 const QgsRectangle badExtent = QgsRectangle( -100, -100, 90, 90 );
2363 const QgsCoordinateReferenceSystem crs( QStringLiteral( "EPSG:3857" ) );
2364 double tform[] =
2365 {
2366 extent.xMinimum(), extent.width() / nCols, 0.0,
2367 extent.yMaximum(), 0.0, -extent.height() / nRows
2368 };
2369
2370 std::vector< QgsRasterAnalysisUtils::RasterLogicInput > inputs;
2371 for ( int ii = 0; ii < 3; ++ii )
2372 {
2373 // generate unique filename (need to open the file first to generate it)
2374 QTemporaryFile tmpFile;
2375 tmpFile.open();
2376 tmpFile.close();
2377
2378 // create a GeoTIFF - this will create data provider in editable mode
2379 const QString filename = tmpFile.fileName();
2380
2381 std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( filename );
2382 writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
2383 writer->setOutputFormat( QStringLiteral( "GTiff" ) );
2384 std::unique_ptr<QgsRasterDataProvider > dp( writer->createOneBandRaster( Qgis::DataType::Float32, nCols, nRows, input[ii].empty() ? badExtent : extent, crs ) );
2385 QVERIFY( dp->isValid() );
2386 dp->setNoDataValue( 1, -9999 );
2387 std::unique_ptr< QgsRasterBlock > block( dp->block( 1, input[ii].empty() ? badExtent : extent, nCols, nRows ) );
2388 if ( !dp->isEditable() )
2389 {
2390 QVERIFY( dp->setEditable( true ) );
2391 }
2392 int i = 0;
2393 for ( int row = 0; row < nRows; row++ )
2394 {
2395 for ( int col = 0; col < nCols; col++ )
2396 {
2397 if ( !input[ii].empty() )
2398 block->setValue( row, col, input[ii][i++] );
2399 }
2400 }
2401 QVERIFY( dp->writeBlock( block.get(), 1 ) );
2402 QVERIFY( dp->setEditable( false ) );
2403
2404 QgsRasterAnalysisUtils::RasterLogicInput input;
2405 input.sourceDataProvider = std::move( dp );
2406 input.hasNoDataValue = true;
2407 input.interface = input.sourceDataProvider.get();
2408
2409 inputs.emplace_back( std::move( input ) );
2410 }
2411
2412 // make destination OR raster
2413 QTemporaryFile tmpFile2;
2414 tmpFile2.open();
2415 tmpFile2.close();
2416
2417 // create a GeoTIFF - this will create data provider in editable mode
2418 QString filename = tmpFile2.fileName();
2419 std::unique_ptr< QgsRasterDataProvider > dpOr( QgsRasterDataProvider::create( QStringLiteral( "gdal" ), filename, QStringLiteral( "GTiff" ), 1, static_cast< Qgis::DataType >( dataType ), 10, 10, tform, crs ) );
2420 QVERIFY( dpOr->isValid() );
2421
2422 // make destination AND raster
2423 QTemporaryFile tmpFile3;
2424 tmpFile3.open();
2425 tmpFile3.close();
2426
2427 // create a GeoTIFF - this will create data provider in editable mode
2428 filename = tmpFile3.fileName();
2429 std::unique_ptr< QgsRasterDataProvider > dpAnd( QgsRasterDataProvider::create( QStringLiteral( "gdal" ), filename, QStringLiteral( "GTiff" ), 1, static_cast< Qgis::DataType >( dataType ), 10, 10, tform, crs ) );
2430 QVERIFY( dpAnd->isValid() );
2431
2432 QgsFeedback feedback;
2433 qgssize noDataCount = 0;
2434 qgssize trueCount = 0;
2435 qgssize falseCount = 0;
2436 QgsRasterAnalysisUtils::applyRasterLogicOperator( inputs, dpOr.get(), destNoDataValue, treatNodataAsFalse, nCols, nRows,
2437 extent, &feedback, orAlg.mExtractValFunc, noDataCount, trueCount, falseCount );
2438
2439 QCOMPARE( noDataCount, expectedOrNoDataCount );
2440 QCOMPARE( trueCount, expectedOrTrueCount );
2441 QCOMPARE( falseCount, expectedOrFalseCount );
2442
2443 // read back in values
2444 std::unique_ptr< QgsRasterBlock > block( dpOr->block( 1, extent, nCols, nRows ) );
2445 QVector< double > res( nCols * nRows );
2446 int i = 0;
2447 for ( int row = 0; row < nRows; row++ )
2448 {
2449 for ( int col = 0; col < nCols; col++ )
2450 {
2451 res[i++] = block->value( row, col );
2452 }
2453 }
2454
2455 for ( int row = 0; row < nRows; row++ )
2456 {
2457 for ( int col = 0; col < nCols; col++ )
2458 {
2459 QCOMPARE( res[row * nCols + col], expectedOr[row * nCols + col] );
2460 }
2461 }
2462
2463 noDataCount = 0;
2464 trueCount = 0;
2465 falseCount = 0;
2466 QgsRasterAnalysisUtils::applyRasterLogicOperator( inputs, dpAnd.get(), destNoDataValue, treatNodataAsFalse, nCols, nRows,
2467 extent, &feedback, andAlg.mExtractValFunc, noDataCount, trueCount, falseCount );
2468
2469 QCOMPARE( noDataCount, expectedAndNoDataCount );
2470 QCOMPARE( trueCount, expectedAndTrueCount );
2471 QCOMPARE( falseCount, expectedAndFalseCount );
2472
2473 // read back in values
2474 block.reset( dpAnd->block( 1, extent, nCols, nRows ) );
2475 QVector< double > res2( nCols * nRows );
2476 i = 0;
2477 for ( int row = 0; row < nRows; row++ )
2478 {
2479 for ( int col = 0; col < nCols; col++ )
2480 {
2481 res2[i++] = block->value( row, col );
2482 }
2483 }
2484
2485 for ( int row = 0; row < nRows; row++ )
2486 {
2487 for ( int col = 0; col < nCols; col++ )
2488 {
2489 QCOMPARE( res2[row * nCols + col], expectedAnd[row * nCols + col] );
2490 }
2491 }
2492 }
2493
cellStatistics_data()2494 void TestQgsProcessingAlgs::cellStatistics_data()
2495 {
2496 QTest::addColumn<QStringList>( "inputRasters" );
2497 QTest::addColumn<QString>( "referenceLayer" );
2498 QTest::addColumn<int>( "statistic" );
2499 QTest::addColumn<bool>( "ignoreNoData" );
2500 QTest::addColumn<QString>( "expectedRaster" );
2501 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
2502
2503 /*
2504 * Testcase 1: sum
2505 */
2506 QTest::newRow( "testcase_1" )
2507 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2508 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2509 << 0
2510 << false
2511 << QStringLiteral( "/cellstatistics_sum_result.tif" )
2512 << Qgis::DataType::Float64;
2513
2514 /*
2515 * Testcase 2: count
2516 */
2517 QTest::newRow( "testcase_2" )
2518 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2519 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2520 << 1
2521 << false
2522 << QStringLiteral( "/cellstatistics_count_result.tif" )
2523 << Qgis::DataType::Int32;
2524
2525 /*
2526 * Testcase 3: mean
2527 */
2528 QTest::newRow( "testcase_3" )
2529 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2530 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2531 << 2
2532 << false
2533 << QStringLiteral( "/cellstatistics_mean_result.tif" )
2534 << Qgis::DataType::Float64;
2535
2536 /*
2537 * Testcase 4: median
2538 */
2539 QTest::newRow( "testcase_4" )
2540 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2541 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2542 << 3
2543 << false
2544 << QStringLiteral( "/cellstatistics_median_result.tif" )
2545 << Qgis::DataType::Float64;
2546
2547 /*
2548 * Testcase 5: Standard deviation
2549 */
2550 QTest::newRow( "testcase_5" )
2551 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2552 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2553 << 4
2554 << false
2555 << QStringLiteral( "/cellstatistics_stddev_result.tif" )
2556 << Qgis::DataType::Float64;
2557
2558 /*
2559 * Testcase 6: Variance
2560 */
2561 QTest::newRow( "testcase_6" )
2562 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2563 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2564 << 5
2565 << false
2566 << QStringLiteral( "/cellstatistics_variance_result.tif" )
2567 << Qgis::DataType::Float64;
2568
2569 /*
2570 * Testcase 7: Minimum
2571 */
2572 QTest::newRow( "testcase_7" )
2573 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2574 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2575 << 6
2576 << false
2577 << QStringLiteral( "/cellstatistics_min_result.tif" )
2578 << Qgis::DataType::Float64;
2579
2580 /*
2581 * Testcase 8: Maximum
2582 */
2583 QTest::newRow( "testcase_8" )
2584 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2585 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2586 << 7
2587 << false
2588 << QStringLiteral( "/cellstatistics_max_result.tif" )
2589 << Qgis::DataType::Float64;
2590
2591 /*
2592 * Testcase 9: Minority
2593 */
2594 QTest::newRow( "testcase_9" )
2595 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2596 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2597 << 8
2598 << false
2599 << QStringLiteral( "/cellstatistics_minority_result.tif" )
2600 << Qgis::DataType::Float64;
2601
2602 /*
2603 * Testcase 10: Majority
2604 */
2605 QTest::newRow( "testcase_10" )
2606 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2607 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2608 << 9
2609 << false
2610 << QStringLiteral( "/cellstatistics_majority_result.tif" )
2611 << Qgis::DataType::Float64;
2612
2613 /*
2614 * Testcase 11: Range
2615 */
2616 QTest::newRow( "testcase_11" )
2617 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2618 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2619 << 10
2620 << false
2621 << QStringLiteral( "/cellstatistics_range_result.tif" )
2622 << Qgis::DataType::Float64;
2623
2624 /*
2625 * Testcase 12: Variety
2626 */
2627 QTest::newRow( "testcase_12" )
2628 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2629 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2630 << 11
2631 << false
2632 << QStringLiteral( "/cellstatistics_variety_result.tif" )
2633 << Qgis::DataType::Int32;
2634
2635 /*
2636 * Testcase 13: Sum (integer)
2637 */
2638 QTest::newRow( "testcase_13" )
2639 << QStringList( {"/raster/statisticsRas1_int32.tif", "/raster/statisticsRas2_int32.tif", "/raster/statisticsRas3_int32.tif"} )
2640 << QStringLiteral( "/raster/statisticsRas1_int32.tif" )
2641 << 0
2642 << false
2643 << QStringLiteral( "/cellstatistics_sum_result_int32.tif" )
2644 << Qgis::DataType::Int32;
2645
2646 /*
2647 * Testcase 14: sum (ignore nodata)
2648 */
2649 QTest::newRow( "testcase_14" )
2650 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2651 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2652 << 0
2653 << true
2654 << QStringLiteral( "/cellstatistics_sum_ignore_nodata_result.tif" )
2655 << Qgis::DataType::Float64;
2656
2657 /*
2658 * Testcase 15: mean
2659 */
2660 QTest::newRow( "testcase_15" )
2661 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2662 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2663 << 2
2664 << true
2665 << QStringLiteral( "/cellstatistics_mean_ignore_nodata_result.tif" )
2666 << Qgis::DataType::Float64;
2667
2668 /*
2669 * Testcase 16: Sum (integer)
2670 */
2671 QTest::newRow( "testcase_16" )
2672 << QStringList( {"/raster/statisticsRas1_int32.tif", "/raster/statisticsRas2_int32.tif", "/raster/statisticsRas3_int32.tif"} )
2673 << QStringLiteral( "/raster/statisticsRas1_int32.tif" )
2674 << 5
2675 << false
2676 << QStringLiteral( "/cellstatistics_variance_result_float32.tif" )
2677 << Qgis::DataType::Float32;
2678
2679 /*
2680 * Testcase 17: median with even number of layers
2681 */
2682 QTest::newRow( "testcase_17" )
2683 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
2684 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2685 << 3
2686 << false
2687 << QStringLiteral( "/cellstatistics_median_result_fourLayers.tif" )
2688 << Qgis::DataType::Float64;
2689
2690 /*
2691 * Testcase 18: median with even number of layers and integer inputs
2692 */
2693 QTest::newRow( "testcase_18" )
2694 << QStringList( {"/raster/statisticsRas1_int32.tif", "/raster/statisticsRas1_int32.tif", "/raster/statisticsRas2_int32.tif", "/raster/statisticsRas3_int32.tif"} )
2695 << QStringLiteral( "/raster/statisticsRas1_int32.tif" )
2696 << 3
2697 << false
2698 << QStringLiteral( "/cellstatistics_median_result_fourLayers_float32.tif" )
2699 << Qgis::DataType::Float32;
2700
2701 /*
2702 * Testcase 19: sum with raster cell stacks containing only nodata
2703 */
2704 QTest::newRow( "testcase_19" )
2705 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas1_float64.asc"} )
2706 << QStringLiteral( "/raster/statisticsRas3_int32.tif" )
2707 << 0
2708 << true
2709 << QStringLiteral( "/cellstatistics_sum_result_ignoreNoData.tif" )
2710 << Qgis::DataType::Float64;
2711
2712 }
2713
cellStatistics()2714 void TestQgsProcessingAlgs::cellStatistics()
2715 {
2716 QFETCH( QStringList, inputRasters );
2717 QFETCH( QString, referenceLayer );
2718 QFETCH( int, statistic );
2719 QFETCH( bool, ignoreNoData );
2720 QFETCH( QString, expectedRaster );
2721 QFETCH( Qgis::DataType, expectedDataType );
2722
2723
2724 //prepare input params
2725 QgsProject p;
2726 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:cellstatistics" ) ) );
2727
2728 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
2729
2730 QStringList inputDatasetPaths;
2731
2732 for ( const auto &raster : inputRasters )
2733 {
2734 inputDatasetPaths << myDataPath + raster;
2735 }
2736
2737 std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = std::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
2738
2739 //set project crs and ellipsoid from input layer
2740 p.setCrs( inputRasterLayer1->crs(), true );
2741
2742 //set project after layer has been added so that transform context/ellipsoid from crs is also set
2743 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
2744 context->setProject( &p );
2745
2746 QVariantMap parameters;
2747
2748 parameters.insert( QStringLiteral( "INPUT" ), inputDatasetPaths );
2749 parameters.insert( QStringLiteral( "STATISTIC" ), statistic );
2750 parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
2751 parameters.insert( QStringLiteral( "REFERENCE_LAYER" ), QString( myDataPath + referenceLayer ) );
2752 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
2753
2754 //prepare expectedRaster
2755 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_cellStatistics/" + expectedRaster, "expectedDataset", "gdal" );
2756 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
2757 QgsRasterIterator expectedIter( expectedInterface.get() );
2758 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
2759
2760 //run alg...
2761
2762 bool ok = false;
2763 QgsProcessingFeedback feedback;
2764 QVariantMap results;
2765
2766 results = alg->run( parameters, *context, &feedback, &ok );
2767 QVERIFY( ok );
2768
2769 //...and check results with expected datasets
2770 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
2771 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
2772
2773 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
2774 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
2775 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
2776
2777 QgsRasterIterator outputIter( outputInterface.get() );
2778 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
2779 int outputIterLeft = 0;
2780 int outputIterTop = 0;
2781 int outputIterCols = 0;
2782 int outputIterRows = 0;
2783 int expectedIterLeft = 0;
2784 int expectedIterTop = 0;
2785 int expectedIterCols = 0;
2786 int expectedIterRows = 0;
2787
2788 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
2789 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
2790
2791 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
2792 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
2793 {
2794 for ( int row = 0; row < expectedIterRows; row++ )
2795 {
2796 for ( int column = 0; column < expectedIterCols; column++ )
2797 {
2798 const double expectedValue = expectedRasterBlock->value( row, column );
2799 const double outputValue = outputRasterBlock->value( row, column );
2800 QCOMPARE( outputValue, expectedValue );
2801 }
2802 }
2803 }
2804 }
2805
Q_DECLARE_METATYPE(QgsRasterAnalysisUtils::CellValuePercentileMethods)2806 Q_DECLARE_METATYPE( QgsRasterAnalysisUtils::CellValuePercentileMethods )
2807 void TestQgsProcessingAlgs::percentileFunctions_data()
2808 {
2809 QTest::addColumn<QgsRasterAnalysisUtils::CellValuePercentileMethods>( "function" );
2810 QTest::addColumn<std::vector<double>>( "inputValues" );
2811 QTest::addColumn<std::vector<double>>( "inputPercentiles" );
2812 QTest::addColumn<std::vector<double>>( "expectedValues" );
2813
2814 QTest::newRow( "testcase_1" )
2815 << QgsRasterAnalysisUtils::NearestRankPercentile
2816 << std::vector<double>( {100, 24, 49, 36, 2, 18, 98, 64, 20, 20} )
2817 << std::vector<double>( {0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1} )
2818 << std::vector<double>( {2, 2, 18, 20, 20, 24, 36, 49, 64, 98, 100} );
2819
2820 QTest::newRow( "testcase_2" )
2821 << QgsRasterAnalysisUtils::InterpolatedPercentileInc
2822 << std::vector<double>( {100, 24, 49, 36, 2, 18, 98, 64, 20, 20} )
2823 << std::vector<double>( {0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1} )
2824 << std::vector<double>( {2.0, 16.4, 19.6, 20.0, 22.4, 30.0, 41.2, 53.5, 70.8, 98.2, 100} );
2825
2826 QTest::newRow( "testcase_3" )
2827 << QgsRasterAnalysisUtils::InterpolatedPercentileExc
2828 << std::vector<double>( {100, 24, 49, 36, 2, 18, 98, 64, 20, 20} )
2829 << std::vector<double>( {0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1} )
2830 << std::vector<double>( {-9999, 3.6, 18.4, 20, 21.6, 30, 43.8, 59.5, 91.2, 99.8, -9999} );
2831 }
2832
percentileFunctions()2833 void TestQgsProcessingAlgs::percentileFunctions()
2834 {
2835 QFETCH( QgsRasterAnalysisUtils::CellValuePercentileMethods, function );
2836 QFETCH( std::vector<double>, inputValues );
2837 QFETCH( std::vector<double>, inputPercentiles );
2838 QFETCH( std::vector<double>, expectedValues );
2839
2840 const int inputValuesSize = static_cast<int>( inputValues.size() );
2841 const int percentileSize = static_cast<int>( inputPercentiles.size() );
2842 double result;
2843
2844 for ( int i = 0; i < percentileSize; i++ )
2845 {
2846 const double percentile = inputPercentiles[i];
2847 const double expectedValue = expectedValues[i];
2848
2849 switch ( function )
2850 {
2851 case ( QgsRasterAnalysisUtils::NearestRankPercentile ):
2852 {
2853 result = QgsRasterAnalysisUtils::nearestRankPercentile( inputValues, inputValuesSize, percentile );
2854 QCOMPARE( result, expectedValue );
2855 break;
2856 }
2857 case ( QgsRasterAnalysisUtils::InterpolatedPercentileInc ):
2858 {
2859 result = QgsRasterAnalysisUtils::interpolatedPercentileInc( inputValues, inputValuesSize, percentile );
2860 QCOMPARE( result, expectedValue );
2861 break;
2862 }
2863 case ( QgsRasterAnalysisUtils::InterpolatedPercentileExc ):
2864 {
2865 result = QgsRasterAnalysisUtils::interpolatedPercentileExc( inputValues, inputValuesSize, percentile, -9999 );
2866 QCOMPARE( result, expectedValue );
2867 break;
2868 }
2869 }
2870 }
2871 }
2872
percentileRaster_data()2873 void TestQgsProcessingAlgs::percentileRaster_data()
2874 {
2875 QTest::addColumn<QStringList>( "inputRasters" );
2876 QTest::addColumn<QString>( "referenceLayer" );
2877 QTest::addColumn<int>( "method" );
2878 QTest::addColumn<double>( "percentile" );
2879 QTest::addColumn<bool>( "ignoreNoData" );
2880 QTest::addColumn<QString>( "expectedRaster" );
2881 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
2882
2883 /*
2884 * Testcase 1: nearest, ignoreNoData = true, dataType = Float64
2885 */
2886 QTest::newRow( "testcase_1" )
2887 << QStringList( {"/raster/statisticsRas1_float64.asc",
2888 "/raster/statisticsRas4_float64.asc",
2889 "/raster/rnd_percentile_raster1_float64.tif",
2890 "/raster/rnd_percentile_raster2_float64.tif",
2891 "/raster/rnd_percentile_raster3_float64.tif",
2892 "/raster/rnd_percentile_raster4_float64.tif",
2893 "/raster/rnd_percentile_raster5_float64.tif"} )
2894 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2895 << 0
2896 << 0.789
2897 << true
2898 << QStringLiteral( "/percentile_nearest_ignoreTrue_float64.tif" )
2899 << Qgis::DataType::Float64;
2900
2901 /*
2902 * Testcase 2: inc, ignoreNoData = true, dataType = Float64
2903 */
2904 QTest::newRow( "testcase_2" )
2905 << QStringList( {"/raster/statisticsRas1_float64.asc",
2906 "/raster/statisticsRas4_float64.asc",
2907 "/raster/rnd_percentile_raster1_float64.tif",
2908 "/raster/rnd_percentile_raster2_float64.tif",
2909 "/raster/rnd_percentile_raster3_float64.tif",
2910 "/raster/rnd_percentile_raster4_float64.tif",
2911 "/raster/rnd_percentile_raster5_float64.tif"} )
2912 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2913 << 1
2914 << 0.789
2915 << true
2916 << QStringLiteral( "/percentile_inc_ignoreTrue_float64.tif" )
2917 << Qgis::DataType::Float64;
2918
2919 /*
2920 * Testcase 3: exc, ignoreNoData = true, dataType = Float64
2921 */
2922 QTest::newRow( "testcase_3" )
2923 << QStringList( {"/raster/statisticsRas1_float64.asc",
2924 "/raster/statisticsRas4_float64.asc",
2925 "/raster/rnd_percentile_raster1_float64.tif",
2926 "/raster/rnd_percentile_raster2_float64.tif",
2927 "/raster/rnd_percentile_raster3_float64.tif",
2928 "/raster/rnd_percentile_raster4_float64.tif",
2929 "/raster/rnd_percentile_raster5_float64.tif"} )
2930 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2931 << 2
2932 << 0.789
2933 << true
2934 << QStringLiteral( "/percentile_exc_ignoreTrue_float64.tif" )
2935 << Qgis::DataType::Float64;
2936
2937 /*
2938 * Testcase 4: nearest, ignoreNoData = false, dataType = Float64
2939 */
2940 QTest::newRow( "testcase_4" )
2941 << QStringList( {"/raster/statisticsRas1_float64.asc",
2942 "/raster/statisticsRas4_float64.asc",
2943 "/raster/rnd_percentile_raster1_float64.tif",
2944 "/raster/rnd_percentile_raster2_float64.tif",
2945 "/raster/rnd_percentile_raster3_float64.tif",
2946 "/raster/rnd_percentile_raster4_float64.tif",
2947 "/raster/rnd_percentile_raster5_float64.tif"} )
2948 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2949 << 0
2950 << 0.789
2951 << false
2952 << QStringLiteral( "/percentile_nearest_ignoreFalse_float64.tif" )
2953 << Qgis::DataType::Float64;
2954
2955 /*
2956 * Testcase 5: inc, ignoreNoData = false, dataType = Float64
2957 */
2958 QTest::newRow( "testcase_5" )
2959 << QStringList( {"/raster/statisticsRas1_float64.asc",
2960 "/raster/statisticsRas4_float64.asc",
2961 "/raster/rnd_percentile_raster1_float64.tif",
2962 "/raster/rnd_percentile_raster2_float64.tif",
2963 "/raster/rnd_percentile_raster3_float64.tif",
2964 "/raster/rnd_percentile_raster4_float64.tif",
2965 "/raster/rnd_percentile_raster5_float64.tif"} )
2966 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2967 << 1
2968 << 0.789
2969 << false
2970 << QStringLiteral( "/percentile_inc_ignoreFalse_float64.tif" )
2971 << Qgis::DataType::Float64;
2972
2973 /*
2974 * Testcase 6: exc, ignoreNoData = false, dataType = Float64
2975 */
2976 QTest::newRow( "testcase_6" )
2977 << QStringList( {"/raster/statisticsRas1_float64.asc",
2978 "/raster/statisticsRas4_float64.asc",
2979 "/raster/rnd_percentile_raster1_float64.tif",
2980 "/raster/rnd_percentile_raster2_float64.tif",
2981 "/raster/rnd_percentile_raster3_float64.tif",
2982 "/raster/rnd_percentile_raster4_float64.tif",
2983 "/raster/rnd_percentile_raster5_float64.tif"} )
2984 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
2985 << 2
2986 << 0.789
2987 << false
2988 << QStringLiteral( "/percentile_exc_ignoreFalse_float64.tif" )
2989 << Qgis::DataType::Float64;
2990
2991 /*
2992 * Testcase 7: exc, ignoreNoData = false, dataType = Byte
2993 */
2994 QTest::newRow( "testcase_7" )
2995 << QStringList( {"/raster/rnd_percentile_raster1_byte.tif",
2996 "/raster/rnd_percentile_raster2_byte.tif",
2997 "/raster/rnd_percentile_raster3_byte.tif",
2998 "/raster/rnd_percentile_raster4_byte.tif",
2999 "/raster/rnd_percentile_raster5_byte.tif"} )
3000 << QStringLiteral( "/raster/rnd_percentile_raster1_byte.tif" )
3001 << 0
3002 << 0.789
3003 << false
3004 << QStringLiteral( "/percentile_nearest_ignoreFalse_byte.tif" )
3005 << Qgis::DataType::Byte;
3006 }
3007
3008
percentileRaster()3009 void TestQgsProcessingAlgs::percentileRaster()
3010 {
3011 QFETCH( QStringList, inputRasters );
3012 QFETCH( QString, referenceLayer );
3013 QFETCH( int, method );
3014 QFETCH( double, percentile );
3015 QFETCH( bool, ignoreNoData );
3016 QFETCH( QString, expectedRaster );
3017 QFETCH( Qgis::DataType, expectedDataType );
3018
3019 //prepare input params
3020 QgsProject p;
3021 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:cellstackpercentile" ) ) );
3022
3023 const QString myDataPath( TEST_DATA_DIR ); //defined in CMakeLists.txt
3024
3025 QStringList inputDatasetPaths;
3026
3027 for ( const auto &raster : inputRasters )
3028 {
3029 inputDatasetPaths << myDataPath + raster;
3030 }
3031
3032 std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = std::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
3033
3034 //set project crs and ellipsoid from input layer
3035 p.setCrs( inputRasterLayer1->crs(), true );
3036
3037 //set project after layer has been added so that transform context/ellipsoid from crs is also set
3038 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
3039 context->setProject( &p );
3040
3041 QVariantMap parameters;
3042
3043 parameters.insert( QStringLiteral( "INPUT" ), inputDatasetPaths );
3044 parameters.insert( QStringLiteral( "METHOD" ), method );
3045 parameters.insert( QStringLiteral( "PERCENTILE" ), percentile );
3046 parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
3047 parameters.insert( QStringLiteral( "REFERENCE_LAYER" ), QString( myDataPath + referenceLayer ) );
3048 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
3049
3050 //prepare expectedRaster
3051 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_cellStackPercentile/" + expectedRaster, "expectedDataset", "gdal" );
3052 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
3053 QgsRasterIterator expectedIter( expectedInterface.get() );
3054 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
3055
3056 //run alg...
3057 bool ok = false;
3058 QgsProcessingFeedback feedback;
3059 QVariantMap results;
3060
3061 results = alg->run( parameters, *context, &feedback, &ok );
3062 QVERIFY( ok );
3063
3064 //...and check results with expected datasets
3065 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
3066 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
3067
3068 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
3069 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
3070 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
3071
3072 QgsRasterIterator outputIter( outputInterface.get() );
3073 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
3074 int outputIterLeft = 0;
3075 int outputIterTop = 0;
3076 int outputIterCols = 0;
3077 int outputIterRows = 0;
3078 int expectedIterLeft = 0;
3079 int expectedIterTop = 0;
3080 int expectedIterCols = 0;
3081 int expectedIterRows = 0;
3082
3083 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
3084 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
3085
3086 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
3087 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
3088 {
3089 for ( int row = 0; row < expectedIterRows; row++ )
3090 {
3091 for ( int column = 0; column < expectedIterCols; column++ )
3092 {
3093 const double roundedExpectedValue = std::round( expectedRasterBlock->value( row, column ) * 4 ) * 4;
3094 const double roundedOutputValue = std::round( outputRasterBlock->value( row, column ) * 4 ) * 4;
3095 QCOMPARE( roundedOutputValue, roundedExpectedValue );
3096 }
3097 }
3098 }
3099 }
3100
Q_DECLARE_METATYPE(QgsRasterAnalysisUtils::CellValuePercentRankMethods)3101 Q_DECLARE_METATYPE( QgsRasterAnalysisUtils::CellValuePercentRankMethods )
3102 void TestQgsProcessingAlgs::percentrankFunctions_data()
3103 {
3104 QTest::addColumn<QgsRasterAnalysisUtils::CellValuePercentRankMethods>( "function" );
3105 QTest::addColumn<std::vector<double>>( "inputValues" );
3106 QTest::addColumn<std::vector<double>>( "inputPercentrank" );
3107 QTest::addColumn<std::vector<double>>( "expectedValues" );
3108
3109 QTest::newRow( "testcase_1" )
3110 << QgsRasterAnalysisUtils::InterpolatedPercentRankInc
3111 << std::vector<double>( {100, 24, 49, 36, 2, 18, 98, 64, 20, 20} )
3112 << std::vector<double>( {-8, 2, 18, 20, 33, 47, 29, 39.5, 57, 39, 12, 100, 150} )
3113 << std::vector<double>( {-9999, 0, 0.111111111111, 0.222222222222, 0.527777777778, 0.649572649573, 0.490740740741, 0.58547008547, 0.725925925926, 0.581196581197, 0.0694444444444, 1, -9999} );
3114
3115 QTest::newRow( "testcase_2" )
3116 << QgsRasterAnalysisUtils::InterpolatedPercentRankExc
3117 << std::vector<double>( {100, 24, 49, 36, 2, 18, 98, 64, 20, 20} )
3118 << std::vector<double>( {-8, 2, 18, 20, 33, 47, 29, 39.5, 57, 39, 12, 100, 150} )
3119 << std::vector<double>( {-9999, 0.0909090909091, 0.1818181818181, 0.272727272727, 0.522727272727, 0.622377622378, 0.492424242424, 0.56993006993, 0.684848484848, 0.566433566434, 0.1477272727272, 0.909090909091, -9999} );
3120 }
3121
percentrankFunctions()3122 void TestQgsProcessingAlgs::percentrankFunctions()
3123 {
3124 QFETCH( QgsRasterAnalysisUtils::CellValuePercentRankMethods, function );
3125 QFETCH( std::vector<double>, inputValues );
3126 QFETCH( std::vector<double>, inputPercentrank );
3127 QFETCH( std::vector<double>, expectedValues );
3128
3129 const int inputValuesSize = static_cast<int>( inputValues.size() );
3130 const int percentrankSize = static_cast<int>( inputPercentrank.size() );
3131 double result;
3132
3133 for ( int i = 0; i < percentrankSize; i++ )
3134 {
3135 const double percentrank = inputPercentrank[i];
3136 const double expectedValue = expectedValues[i];
3137
3138 switch ( function )
3139 {
3140 case ( QgsRasterAnalysisUtils::InterpolatedPercentRankInc ):
3141 {
3142 result = QgsRasterAnalysisUtils::interpolatedPercentRankInc( inputValues, inputValuesSize, percentrank, -9999 );
3143 QCOMPARE( result, expectedValue );
3144 break;
3145 }
3146 case ( QgsRasterAnalysisUtils::InterpolatedPercentRankExc ):
3147 {
3148 result = QgsRasterAnalysisUtils::interpolatedPercentRankExc( inputValues, inputValuesSize, percentrank, -9999 );
3149 QCOMPARE( result, expectedValue );
3150 break;
3151 }
3152 }
3153 }
3154
3155 std::vector<double> cellVal = std::vector<double>( {13, 36, 13, 44, 60} );
3156
3157 qDebug() << QgsRasterAnalysisUtils::interpolatedPercentRankInc( cellVal, 5, 13, 200 );
3158
3159 QVERIFY( true );
3160 }
3161
percentrankByRaster_data()3162 void TestQgsProcessingAlgs::percentrankByRaster_data()
3163 {
3164 QTest::addColumn<QString>( "valueLayer" );
3165 QTest::addColumn<int>( "valueLayerBand" );
3166 QTest::addColumn<QStringList>( "inputRasters" );
3167 QTest::addColumn<QString>( "referenceLayer" );
3168 QTest::addColumn<int>( "method" );
3169 QTest::addColumn<bool>( "ignoreNoData" );
3170 QTest::addColumn<double>( "noDataValue" );
3171 QTest::addColumn<QString>( "expectedRaster" );
3172 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
3173
3174 /*
3175 * Testcase 1: nearest, ignoreNoData = true, dataType = Float64
3176 */
3177 QTest::newRow( "testcase_1" )
3178 << QStringLiteral( "/raster/rnd_percentrank_valueraster_float64.tif" )
3179 << 1
3180 << QStringList( {"/raster/statisticsRas1_float64.asc",
3181 "/raster/statisticsRas4_float64.asc",
3182 "/raster/rnd_percentile_raster1_float64.tif",
3183 "/raster/rnd_percentile_raster2_float64.tif",
3184 "/raster/rnd_percentile_raster3_float64.tif",
3185 "/raster/rnd_percentile_raster4_float64.tif",
3186 "/raster/rnd_percentile_raster5_float64.tif"} )
3187 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3188 << 0
3189 << true
3190 << -9999.0
3191 << QStringLiteral( "/percentRankByRaster_inc_ignoreTrue_float64.tif" )
3192 << Qgis::DataType::Float32;
3193
3194 /*
3195 * Testcase 2: inc, ignoreNoData = true, dataType = Float64
3196 */
3197 QTest::newRow( "testcase_2" )
3198 << QStringLiteral( "/raster/rnd_percentrank_valueraster_float64.tif" )
3199 << 1
3200 << QStringList( {"/raster/statisticsRas1_float64.asc",
3201 "/raster/statisticsRas4_float64.asc",
3202 "/raster/rnd_percentile_raster1_float64.tif",
3203 "/raster/rnd_percentile_raster2_float64.tif",
3204 "/raster/rnd_percentile_raster3_float64.tif",
3205 "/raster/rnd_percentile_raster4_float64.tif",
3206 "/raster/rnd_percentile_raster5_float64.tif"} )
3207 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3208 << 1
3209 << true
3210 << -9999.0
3211 << QStringLiteral( "/percentRankByRaster_exc_ignoreTrue_float64.tif" )
3212 << Qgis::DataType::Float32;
3213
3214 /*
3215 * Testcase 3: nearest, ignoreNoData = false, dataType = Float64
3216 */
3217 QTest::newRow( "testcase_3" )
3218 << QStringLiteral( "/raster/rnd_percentrank_valueraster_float64.tif" )
3219 << 1
3220 << QStringList( {"/raster/statisticsRas1_float64.asc",
3221 "/raster/statisticsRas4_float64.asc",
3222 "/raster/rnd_percentile_raster1_float64.tif",
3223 "/raster/rnd_percentile_raster2_float64.tif",
3224 "/raster/rnd_percentile_raster3_float64.tif",
3225 "/raster/rnd_percentile_raster4_float64.tif",
3226 "/raster/rnd_percentile_raster5_float64.tif"} )
3227 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3228 << 0
3229 << false
3230 << -9999.0
3231 << QStringLiteral( "/percentRankByRaster_inc_ignoreFalse_float64.tif" )
3232 << Qgis::DataType::Float32;
3233
3234 /*
3235 * Testcase 4: inc, ignoreNoData = false, dataType = Float64
3236 */
3237 QTest::newRow( "testcase_4" )
3238 << QStringLiteral( "/raster/rnd_percentrank_valueraster_float64.tif" )
3239 << 1
3240 << QStringList( {"/raster/statisticsRas1_float64.asc",
3241 "/raster/statisticsRas4_float64.asc",
3242 "/raster/rnd_percentile_raster1_float64.tif",
3243 "/raster/rnd_percentile_raster2_float64.tif",
3244 "/raster/rnd_percentile_raster3_float64.tif",
3245 "/raster/rnd_percentile_raster4_float64.tif",
3246 "/raster/rnd_percentile_raster5_float64.tif"} )
3247 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3248 << 1
3249 << false
3250 << -9999.0
3251 << QStringLiteral( "/percentRankByRaster_exc_ignoreFalse_float64.tif" )
3252 << Qgis::DataType::Float32;
3253
3254
3255 /*
3256 * Testcase 5: inc, ignoreNoData = false, dataType = Byte
3257 */
3258 QTest::newRow( "testcase_5" )
3259 << QStringLiteral( "/raster/rnd_percentile_raster1_byte.tif" )
3260 << 1
3261 << QStringList( {"/raster/rnd_percentile_raster1_byte.tif",
3262 "/raster/rnd_percentile_raster2_byte.tif",
3263 "/raster/rnd_percentile_raster3_byte.tif",
3264 "/raster/rnd_percentile_raster4_byte.tif",
3265 "/raster/rnd_percentile_raster5_byte.tif"} )
3266 << QStringLiteral( "/raster/rnd_percentile_raster1_byte.tif" )
3267 << 0
3268 << false
3269 << 200.0
3270 << QStringLiteral( "/percentRankByRaster_inc_ignoreFalse_byte.tif" )
3271 << Qgis::DataType::Float32;
3272 }
3273
3274
percentrankByRaster()3275 void TestQgsProcessingAlgs::percentrankByRaster()
3276 {
3277 QFETCH( QString, valueLayer );
3278 QFETCH( int, valueLayerBand );
3279 QFETCH( QStringList, inputRasters );
3280 QFETCH( QString, referenceLayer );
3281 QFETCH( int, method );
3282 QFETCH( bool, ignoreNoData );
3283 QFETCH( double, noDataValue );
3284 QFETCH( QString, expectedRaster );
3285 QFETCH( Qgis::DataType, expectedDataType );
3286
3287 //prepare input params
3288 QgsProject p;
3289 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:cellstackpercentrankfromrasterlayer" ) ) );
3290
3291 const QString myDataPath( TEST_DATA_DIR ); //defined in CMakeLists.txt
3292
3293 QStringList inputDatasetPaths;
3294
3295 for ( const auto &raster : inputRasters )
3296 {
3297 inputDatasetPaths << myDataPath + raster;
3298 }
3299
3300 std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = std::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
3301
3302 //set project crs and ellipsoid from input layer
3303 p.setCrs( inputRasterLayer1->crs(), true );
3304
3305 //set project after layer has been added so that transform context/ellipsoid from crs is also set
3306 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
3307 context->setProject( &p );
3308
3309 QVariantMap parameters;
3310
3311 parameters.insert( QStringLiteral( "INPUT" ), inputDatasetPaths );
3312 parameters.insert( QStringLiteral( "INPUT_VALUE_RASTER" ), QString( myDataPath + valueLayer ) );
3313 parameters.insert( QStringLiteral( "VALUE_RASTER_BAND" ), valueLayerBand );
3314 parameters.insert( QStringLiteral( "METHOD" ), method );
3315 parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
3316 parameters.insert( QStringLiteral( "OUTPUT_NODATA_VALUE" ), noDataValue );
3317 parameters.insert( QStringLiteral( "REFERENCE_LAYER" ), QString( myDataPath + referenceLayer ) );
3318 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
3319
3320 //prepare expectedRaster
3321 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_cellStackPercentrankFromRaster/" + expectedRaster, "expectedDataset", "gdal" );
3322 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
3323 QgsRasterIterator expectedIter( expectedInterface.get() );
3324 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
3325
3326 //run alg...
3327 bool ok = false;
3328 QgsProcessingFeedback feedback;
3329 QVariantMap results;
3330
3331 results = alg->run( parameters, *context, &feedback, &ok );
3332 QVERIFY( ok );
3333
3334 //...and check results with expected datasets
3335 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
3336 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
3337
3338 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
3339 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
3340 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
3341
3342 QgsRasterIterator outputIter( outputInterface.get() );
3343 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
3344 int outputIterLeft = 0;
3345 int outputIterTop = 0;
3346 int outputIterCols = 0;
3347 int outputIterRows = 0;
3348 int expectedIterLeft = 0;
3349 int expectedIterTop = 0;
3350 int expectedIterCols = 0;
3351 int expectedIterRows = 0;
3352
3353 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
3354 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
3355
3356 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
3357 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
3358 {
3359 for ( int row = 0; row < expectedIterRows; row++ )
3360 {
3361 for ( int column = 0; column < expectedIterCols; column++ )
3362 {
3363 const double roundedExpectedValue = std::round( expectedRasterBlock->value( row, column ) * 4 ) * 4;
3364 const double roundedOutputValue = std::round( outputRasterBlock->value( row, column ) * 4 ) * 4;
3365 QCOMPARE( roundedOutputValue, roundedExpectedValue );
3366 }
3367 }
3368 }
3369 }
3370
percentrankByValue_data()3371 void TestQgsProcessingAlgs::percentrankByValue_data()
3372 {
3373 QTest::addColumn<QStringList>( "inputRasters" );
3374 QTest::addColumn<QString>( "referenceLayer" );
3375 QTest::addColumn<double>( "value" );
3376 QTest::addColumn<int>( "method" );
3377 QTest::addColumn<bool>( "ignoreNoData" );
3378 QTest::addColumn<double>( "noDataValue" );
3379 QTest::addColumn<QString>( "expectedRaster" );
3380 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
3381
3382 /*
3383 * Testcase 1: nearest, ignoreNoData = true, dataType = Float64
3384 */
3385 QTest::newRow( "testcase_1" )
3386 << QStringList( {"/raster/statisticsRas1_float64.asc",
3387 "/raster/statisticsRas4_float64.asc",
3388 "/raster/rnd_percentile_raster1_float64.tif",
3389 "/raster/rnd_percentile_raster2_float64.tif",
3390 "/raster/rnd_percentile_raster3_float64.tif",
3391 "/raster/rnd_percentile_raster4_float64.tif",
3392 "/raster/rnd_percentile_raster5_float64.tif"} )
3393 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3394 << 83.327
3395 << 0
3396 << true
3397 << -9999.0
3398 << QStringLiteral( "/percentRankByValue_inc_ignoreTrue_float64.tif" )
3399 << Qgis::DataType::Float32;
3400
3401 /*
3402 * Testcase 2: inc, ignoreNoData = true, dataType = Float64
3403 */
3404 QTest::newRow( "testcase_2" )
3405 << QStringList( {"/raster/statisticsRas1_float64.asc",
3406 "/raster/statisticsRas4_float64.asc",
3407 "/raster/rnd_percentile_raster1_float64.tif",
3408 "/raster/rnd_percentile_raster2_float64.tif",
3409 "/raster/rnd_percentile_raster3_float64.tif",
3410 "/raster/rnd_percentile_raster4_float64.tif",
3411 "/raster/rnd_percentile_raster5_float64.tif"} )
3412 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3413 << 7.99
3414 << 1
3415 << true
3416 << -9999.0
3417 << QStringLiteral( "/percentRankByValue_exc_ignoreTrue_float64.tif" )
3418 << Qgis::DataType::Float32;
3419
3420 /*
3421 * Testcase 3: nearest, ignoreNoData = false, dataType = Float64
3422 */
3423 QTest::newRow( "testcase_3" )
3424 << QStringList( {"/raster/statisticsRas1_float64.asc",
3425 "/raster/statisticsRas4_float64.asc",
3426 "/raster/rnd_percentile_raster1_float64.tif",
3427 "/raster/rnd_percentile_raster2_float64.tif",
3428 "/raster/rnd_percentile_raster3_float64.tif",
3429 "/raster/rnd_percentile_raster4_float64.tif",
3430 "/raster/rnd_percentile_raster5_float64.tif"} )
3431 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3432 << 200.78
3433 << 0
3434 << false
3435 << -9999.0
3436 << QStringLiteral( "/percentRankByValue_inc_ignoreFalse_float64.tif" )
3437 << Qgis::DataType::Float32;
3438
3439 /*
3440 * Testcase 4: inc, ignoreNoData = false, dataType = Float64
3441 */
3442 QTest::newRow( "testcase_4" )
3443 << QStringList( {"/raster/statisticsRas1_float64.asc",
3444 "/raster/statisticsRas4_float64.asc",
3445 "/raster/rnd_percentile_raster1_float64.tif",
3446 "/raster/rnd_percentile_raster2_float64.tif",
3447 "/raster/rnd_percentile_raster3_float64.tif",
3448 "/raster/rnd_percentile_raster4_float64.tif",
3449 "/raster/rnd_percentile_raster5_float64.tif"} )
3450 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3451 << 56.78
3452 << 1
3453 << false
3454 << -9999.0
3455 << QStringLiteral( "/percentRankByValue_exc_ignoreFalse_float64.tif" )
3456 << Qgis::DataType::Float32;
3457
3458
3459 /*
3460 * Testcase 5: inc, ignoreNoData = false, dataType = Byte
3461 */
3462 QTest::newRow( "testcase_5" )
3463 << QStringList( {"/raster/rnd_percentile_raster1_byte.tif",
3464 "/raster/rnd_percentile_raster2_byte.tif",
3465 "/raster/rnd_percentile_raster3_byte.tif",
3466 "/raster/rnd_percentile_raster4_byte.tif",
3467 "/raster/rnd_percentile_raster5_byte.tif"} )
3468 << QStringLiteral( "/raster/rnd_percentile_raster1_byte.tif" )
3469 << 19.0
3470 << 0
3471 << false
3472 << 200.0
3473 << QStringLiteral( "/percentRankByValue_inc_ignoreFalse_byte.tif" )
3474 << Qgis::DataType::Float32;
3475 }
3476
3477
percentrankByValue()3478 void TestQgsProcessingAlgs::percentrankByValue()
3479 {
3480 QFETCH( QStringList, inputRasters );
3481 QFETCH( QString, referenceLayer );
3482 QFETCH( double, value );
3483 QFETCH( int, method );
3484 QFETCH( bool, ignoreNoData );
3485 QFETCH( double, noDataValue );
3486 QFETCH( QString, expectedRaster );
3487 QFETCH( Qgis::DataType, expectedDataType );
3488
3489 //prepare input params
3490 QgsProject p;
3491 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:cellstackpercentrankfromvalue" ) ) );
3492
3493 const QString myDataPath( TEST_DATA_DIR ); //defined in CMakeLists.txt
3494
3495 QStringList inputDatasetPaths;
3496
3497 for ( const auto &raster : inputRasters )
3498 {
3499 inputDatasetPaths << myDataPath + raster;
3500 }
3501
3502 std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = std::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
3503
3504 //set project crs and ellipsoid from input layer
3505 p.setCrs( inputRasterLayer1->crs(), true );
3506
3507 //set project after layer has been added so that transform context/ellipsoid from crs is also set
3508 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
3509 context->setProject( &p );
3510
3511 QVariantMap parameters;
3512
3513 parameters.insert( QStringLiteral( "INPUT" ), inputDatasetPaths );
3514 parameters.insert( QStringLiteral( "VALUE" ), value );
3515 parameters.insert( QStringLiteral( "METHOD" ), method );
3516 parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
3517 parameters.insert( QStringLiteral( "OUTPUT_NODATA_VALUE" ), noDataValue );
3518 parameters.insert( QStringLiteral( "REFERENCE_LAYER" ), QString( myDataPath + referenceLayer ) );
3519 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
3520
3521 //prepare expectedRaster
3522 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_cellStackPercentrankFromValue/" + expectedRaster, "expectedDataset", "gdal" );
3523 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
3524 QgsRasterIterator expectedIter( expectedInterface.get() );
3525 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
3526
3527 //run alg...
3528 bool ok = false;
3529 QgsProcessingFeedback feedback;
3530 QVariantMap results;
3531
3532 results = alg->run( parameters, *context, &feedback, &ok );
3533 QVERIFY( ok );
3534
3535 //...and check results with expected datasets
3536 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
3537 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
3538
3539 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
3540 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
3541 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
3542
3543 QgsRasterIterator outputIter( outputInterface.get() );
3544 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
3545 int outputIterLeft = 0;
3546 int outputIterTop = 0;
3547 int outputIterCols = 0;
3548 int outputIterRows = 0;
3549 int expectedIterLeft = 0;
3550 int expectedIterTop = 0;
3551 int expectedIterCols = 0;
3552 int expectedIterRows = 0;
3553
3554 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
3555 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
3556
3557 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
3558 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
3559 {
3560 for ( int row = 0; row < expectedIterRows; row++ )
3561 {
3562 for ( int column = 0; column < expectedIterCols; column++ )
3563 {
3564 const double roundedExpectedValue = std::round( expectedRasterBlock->value( row, column ) * 4 ) * 4;
3565 const double roundedOutputValue = std::round( outputRasterBlock->value( row, column ) * 4 ) * 4;
3566 QCOMPARE( roundedOutputValue, roundedExpectedValue );
3567 }
3568 }
3569 }
3570 }
3571
rasterFrequencyByComparisonOperator_data()3572 void TestQgsProcessingAlgs::rasterFrequencyByComparisonOperator_data()
3573 {
3574 QTest::addColumn<QString>( "algName" );
3575 QTest::addColumn<QString>( "inputValueRaster" );
3576 QTest::addColumn<int>( "inputValueRasterBand" );
3577 QTest::addColumn<QStringList>( "inputRasters" );
3578 QTest::addColumn<bool>( "ignoreNoData" );
3579 QTest::addColumn<QString>( "expectedRaster" );
3580 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
3581
3582 /*
3583 * Testcase 1 - equal to frequency: don't ignore NoData
3584 */
3585 QTest::newRow( "testcase_1" )
3586 << QStringLiteral( "native:equaltofrequency" )
3587 << QStringLiteral( "/raster/valueRas1_float64.asc" )
3588 << 1
3589 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3590 << false
3591 << QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest1.tif" )
3592 << Qgis::DataType::Int32;
3593 /*
3594 * Testcase 2 - equal to frequency: ignore NoData
3595 */
3596 QTest::newRow( "testcase_2" )
3597 << QStringLiteral( "native:equaltofrequency" )
3598 << QStringLiteral( "/raster/valueRas1_float64.asc" )
3599 << 1
3600 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3601 << true
3602 << QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest2.tif" )
3603 << Qgis::DataType::Int32;
3604
3605 /*
3606 * Testcase 3 - equal to frequency: NoData in value raster
3607 */
3608 QTest::newRow( "testcase_3" )
3609 << QStringLiteral( "native:equaltofrequency" )
3610 << QStringLiteral( "/raster/valueRas2_float64.asc" )
3611 << 1
3612 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3613 << false
3614 << QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest3.tif" )
3615 << Qgis::DataType::Int32;
3616
3617 /*
3618 * Testcase 4 - equal to frequency: test with random byte raster
3619 */
3620 QTest::newRow( "testcase_4" )
3621 << QStringLiteral( "native:equaltofrequency" )
3622 << QStringLiteral( "/raster/valueRas3_float64.asc" )
3623 << 1
3624 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3625 << false
3626 << QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest4.tif" )
3627 << Qgis::DataType::Int32;
3628
3629 /*
3630 * Testcase 5 - greater than frequency: don't ignore NoData
3631 */
3632 QTest::newRow( "testcase_5" )
3633 << QStringLiteral( "native:greaterthanfrequency" )
3634 << QStringLiteral( "/raster/valueRas1_float64.asc" )
3635 << 1
3636 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3637 << false
3638 << QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest1.tif" )
3639 << Qgis::DataType::Int32;
3640 /*
3641 * Testcase 6 - greater than frequency: ignore NoData
3642 */
3643 QTest::newRow( "testcase_6" )
3644 << QStringLiteral( "native:greaterthanfrequency" )
3645 << QStringLiteral( "/raster/valueRas1_float64.asc" )
3646 << 1
3647 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3648 << true
3649 << QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest2.tif" )
3650 << Qgis::DataType::Int32;
3651
3652 /*
3653 * Testcase 7 - greater than frequency: NoData in value raster
3654 */
3655 QTest::newRow( "testcase_7" )
3656 << QStringLiteral( "native:greaterthanfrequency" )
3657 << QStringLiteral( "/raster/valueRas2_float64.asc" )
3658 << 1
3659 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3660 << false
3661 << QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest3.tif" )
3662 << Qgis::DataType::Int32;
3663
3664 /*
3665 * Testcase 8 - greater than frequency: test with random byte raster
3666 */
3667 QTest::newRow( "testcase_8" )
3668 << QStringLiteral( "native:greaterthanfrequency" )
3669 << QStringLiteral( "/raster/valueRas3_float64.asc" )
3670 << 1
3671 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3672 << false
3673 << QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest4.tif" )
3674 << Qgis::DataType::Int32;
3675
3676 /*
3677 * Testcase 9 - less than frequency: don't ignore NoData
3678 */
3679 QTest::newRow( "testcase_9" )
3680 << QStringLiteral( "native:lessthanfrequency" )
3681 << QStringLiteral( "/raster/valueRas1_float64.asc" )
3682 << 1
3683 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3684 << false
3685 << QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest1.tif" )
3686 << Qgis::DataType::Int32;
3687 /*
3688 * Testcase 10 - greater than frequency: ignore NoData
3689 */
3690 QTest::newRow( "testcase_10" )
3691 << QStringLiteral( "native:lessthanfrequency" )
3692 << QStringLiteral( "/raster/valueRas1_float64.asc" )
3693 << 1
3694 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3695 << true
3696 << QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest2.tif" )
3697 << Qgis::DataType::Int32;
3698
3699 /*
3700 * Testcase 11 - less than frequency: NoData in value raster
3701 */
3702 QTest::newRow( "testcase_11" )
3703 << QStringLiteral( "native:lessthanfrequency" )
3704 << QStringLiteral( "/raster/valueRas2_float64.asc" )
3705 << 1
3706 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3707 << false
3708 << QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest3.tif" )
3709 << Qgis::DataType::Int32;
3710
3711 /*
3712 * Testcase 12 - less than frequency: test with random byte raster
3713 */
3714 QTest::newRow( "testcase_12" )
3715 << QStringLiteral( "native:lessthanfrequency" )
3716 << QStringLiteral( "/raster/valueRas3_float64.asc" )
3717 << 1
3718 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3719 << false
3720 << QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest4.tif" )
3721 << Qgis::DataType::Int32;
3722 }
3723
rasterFrequencyByComparisonOperator()3724 void TestQgsProcessingAlgs::rasterFrequencyByComparisonOperator()
3725 {
3726 QFETCH( QString, algName );
3727 QFETCH( QString, inputValueRaster );
3728 QFETCH( int, inputValueRasterBand );
3729 QFETCH( QStringList, inputRasters );
3730 QFETCH( bool, ignoreNoData );
3731 QFETCH( QString, expectedRaster );
3732 QFETCH( Qgis::DataType, expectedDataType );
3733
3734 //prepare input params
3735 QgsProject p;
3736 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algName ) );
3737
3738 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
3739
3740 QStringList inputDatasetPaths;
3741
3742 for ( const auto &raster : inputRasters )
3743 {
3744 inputDatasetPaths << myDataPath + raster;
3745 }
3746
3747 std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = std::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
3748
3749 //set project crs and ellipsoid from input layer
3750 p.setCrs( inputRasterLayer1->crs(), true );
3751
3752 //set project after layer has been added so that transform context/ellipsoid from crs is also set
3753 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
3754 context->setProject( &p );
3755
3756 QVariantMap parameters;
3757
3758 parameters.insert( QStringLiteral( "INPUT_VALUE_RASTER" ), QString( myDataPath + inputValueRaster ) );
3759 parameters.insert( QStringLiteral( "INPUT_VALUE_RASTER_BAND" ), inputValueRasterBand );
3760 parameters.insert( QStringLiteral( "INPUT_RASTERS" ), inputDatasetPaths );
3761 parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
3762 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
3763
3764 //prepare expectedRaster
3765 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images" + expectedRaster, "expectedDataset", "gdal" );
3766 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
3767 QgsRasterIterator expectedIter( expectedInterface.get() );
3768 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
3769
3770 //run alg...
3771
3772 bool ok = false;
3773 QgsProcessingFeedback feedback;
3774 QVariantMap results;
3775
3776 results = alg->run( parameters, *context, &feedback, &ok );
3777 QVERIFY( ok );
3778
3779 //...and check results with expected datasets
3780 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
3781 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
3782
3783 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
3784 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
3785 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
3786
3787 QgsRasterIterator outputIter( outputInterface.get() );
3788 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
3789 int outputIterLeft = 0;
3790 int outputIterTop = 0;
3791 int outputIterCols = 0;
3792 int outputIterRows = 0;
3793 int expectedIterLeft = 0;
3794 int expectedIterTop = 0;
3795 int expectedIterCols = 0;
3796 int expectedIterRows = 0;
3797
3798 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
3799 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
3800
3801 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
3802 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
3803 {
3804 for ( int row = 0; row < expectedIterRows; row++ )
3805 {
3806 for ( int column = 0; column < expectedIterCols; column++ )
3807 {
3808 const double expectedValue = expectedRasterBlock->value( row, column );
3809 const double outputValue = outputRasterBlock->value( row, column );
3810 QCOMPARE( outputValue, expectedValue );
3811
3812 const Qgis::DataType outputDataType = outputRasterBlock->dataType();
3813 QCOMPARE( outputDataType, expectedDataType );
3814 }
3815 }
3816 }
3817 }
3818
3819
rasterLocalPosition_data()3820 void TestQgsProcessingAlgs::rasterLocalPosition_data()
3821 {
3822 QTest::addColumn<QString>( "algName" );
3823 QTest::addColumn<QStringList>( "inputRasters" );
3824 QTest::addColumn<QString>( "referenceRaster" );
3825 QTest::addColumn<bool>( "ignoreNoData" );
3826 QTest::addColumn<QString>( "expectedRaster" );
3827
3828 QTest::newRow( "testcase_1" )
3829 << QStringLiteral( "native:lowestpositioninrasterstack" )
3830 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3831 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3832 << false
3833 << QStringLiteral( "/expected_lowestPosition/expectedLowestPositionTest1.tif" );
3834
3835 QTest::newRow( "testcase_2" )
3836 << QStringLiteral( "native:lowestpositioninrasterstack" )
3837 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3838 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3839 << true
3840 << QStringLiteral( "/expected_lowestPosition/expectedLowestPositionTest2.tif" );
3841
3842 QTest::newRow( "testcase_3" )
3843 << QStringLiteral( "native:lowestpositioninrasterstack" )
3844 << QStringList( {"/raster/statisticsRas2_float64.asc", "/raster/statisticsRas1_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3845 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3846 << false
3847 << QStringLiteral( "/expected_lowestPosition/expectedLowestPositionTest3.tif" );
3848
3849 QTest::newRow( "testcase_4" )
3850 << QStringLiteral( "native:highestpositioninrasterstack" )
3851 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3852 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3853 << false
3854 << QStringLiteral( "/expected_highestPosition/expectedHighestPositionTest1.tif" );
3855
3856 QTest::newRow( "testcase_5" )
3857 << QStringLiteral( "native:highestpositioninrasterstack" )
3858 << QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3859 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3860 << true
3861 << QStringLiteral( "/expected_highestPosition/expectedHighestPositionTest2.tif" );
3862
3863 QTest::newRow( "testcase_6" )
3864 << QStringLiteral( "native:highestpositioninrasterstack" )
3865 << QStringList( {"/raster/statisticsRas2_float64.asc", "/raster/statisticsRas1_float64.asc", "/raster/statisticsRas3_float64.asc"} )
3866 << QStringLiteral( "/raster/statisticsRas1_float64.asc" )
3867 << false
3868 << QStringLiteral( "/expected_highestPosition/expectedHighestPositionTest3.tif" );
3869 }
3870
rasterLocalPosition()3871 void TestQgsProcessingAlgs::rasterLocalPosition()
3872 {
3873 QFETCH( QString, algName );
3874 QFETCH( QStringList, inputRasters );
3875 QFETCH( QString, referenceRaster );
3876 QFETCH( bool, ignoreNoData );
3877 QFETCH( QString, expectedRaster );
3878
3879 //prepare input params
3880 QgsProject p;
3881 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algName ) );
3882
3883 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
3884
3885 QStringList inputDatasetPaths;
3886
3887 for ( const auto &raster : inputRasters )
3888 {
3889 inputDatasetPaths << myDataPath + raster;
3890 }
3891
3892 std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = std::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
3893
3894 //set project crs and ellipsoid from input layer
3895 p.setCrs( inputRasterLayer1->crs(), true );
3896
3897 //set project after layer has been added so that transform context/ellipsoid from crs is also set
3898 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
3899 context->setProject( &p );
3900
3901 QVariantMap parameters;
3902
3903 parameters.insert( QStringLiteral( "INPUT_RASTERS" ), inputDatasetPaths );
3904 parameters.insert( QStringLiteral( "REFERENCE_LAYER" ), QString( myDataPath + referenceRaster ) );
3905 parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
3906 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
3907
3908 //prepare expectedRaster
3909 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images" + expectedRaster, "expectedDataset", "gdal" );
3910 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
3911 QgsRasterIterator expectedIter( expectedInterface.get() );
3912 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
3913
3914 //run alg...
3915 bool ok = false;
3916 QgsProcessingFeedback feedback;
3917 QVariantMap results;
3918
3919 results = alg->run( parameters, *context, &feedback, &ok );
3920 QVERIFY( ok );
3921
3922 //...and check results with expected datasets
3923 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
3924 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
3925
3926 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
3927 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
3928
3929 QgsRasterIterator outputIter( outputInterface.get() );
3930 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
3931 int outputIterLeft = 0;
3932 int outputIterTop = 0;
3933 int outputIterCols = 0;
3934 int outputIterRows = 0;
3935 int expectedIterLeft = 0;
3936 int expectedIterTop = 0;
3937 int expectedIterCols = 0;
3938 int expectedIterRows = 0;
3939
3940 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
3941 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
3942
3943 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
3944 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
3945 {
3946 for ( int row = 0; row < expectedIterRows; row++ )
3947 {
3948 for ( int column = 0; column < expectedIterCols; column++ )
3949 {
3950 const double expectedValue = expectedRasterBlock->value( row, column );
3951 const double outputValue = outputRasterBlock->value( row, column );
3952 QCOMPARE( outputValue, expectedValue );
3953
3954 const Qgis::DataType outputDataType = outputRasterBlock->dataType();
3955 QCOMPARE( outputDataType, Qgis::DataType::Int32 );
3956 }
3957 }
3958 }
3959 }
3960
roundRasterValues_data()3961 void TestQgsProcessingAlgs::roundRasterValues_data()
3962 {
3963 QTest::addColumn<QString>( "inputRaster" );
3964 QTest::addColumn<QString>( "expectedRaster" );
3965 QTest::addColumn<int>( "inputBand" );
3966 QTest::addColumn<int>( "roundingDirection" );
3967 QTest::addColumn<int>( "decimals" );
3968 QTest::addColumn<int>( "baseN" );
3969
3970 /*
3971 * Testcase 1
3972 *
3973 * Integer Raster Layer
3974 * band = 1
3975 * roundingDirection = nearest
3976 * decimals = 2
3977 */
3978 QTest::newRow( "testcase 1" )
3979 << "/raster/dem.tif"
3980 << QStringLiteral( "/roundRasterValues_testcase1.tif" ) //no output expected: can't round integer
3981 << 1
3982 << 1
3983 << 2
3984 << 10;
3985
3986 /*
3987 * Testcase 2
3988 *
3989 * WGS84 dem
3990 * band = 1
3991 * roundingDirection = up
3992 * decimals = 2
3993 */
3994 QTest::newRow( "testcase 2" )
3995 << "/raster/dem.tif"
3996 << QStringLiteral( "/roundRasterValues_testcase2.tif" )
3997 << 1
3998 << 0
3999 << 2
4000 << 10;
4001
4002 /*
4003 * Testcase 3
4004 *
4005 * WGS84 dem
4006 * band = 1
4007 * roundingDirection = down
4008 * decimals = 1
4009 */
4010 QTest::newRow( "testcase 3" )
4011 << "/raster/dem.tif"
4012 << QStringLiteral( "/roundRasterValues_testcase3.tif" )
4013 << 1
4014 << 2
4015 << 1
4016 << 10;
4017
4018 /*
4019 * Testcase 4
4020 *
4021 * WGS84 dem
4022 * band = 1
4023 * roundingDirection = nearest
4024 * decimals = -1
4025 */
4026 QTest::newRow( "testcase 4" )
4027 << "/raster/dem.tif"
4028 << QStringLiteral( "/roundRasterValues_testcase4.tif" )
4029 << 1
4030 << 1
4031 << -1
4032 << 10;
4033
4034 /*
4035 * Testcase 5
4036 *
4037 * WGS84 dem
4038 * band = 1
4039 * roundingDirection = up
4040 * decimals = -1
4041 */
4042 QTest::newRow( "testcase 5" )
4043 << "/raster/dem.tif"
4044 << QStringLiteral( "/roundRasterValues_testcase5.tif" )
4045 << 1
4046 << 0
4047 << -1
4048 << 10;
4049
4050 /*
4051 * Testcase 6
4052 *
4053 * WGS84 dem
4054 * band = 1
4055 * roundingDirection = down
4056 * decimals = -1
4057 */
4058 QTest::newRow( "testcase 6" )
4059 << "/raster/dem.tif"
4060 << QStringLiteral( "/roundRasterValues_testcase6.tif" )
4061 << 1
4062 << 2
4063 << -1
4064 << 10;
4065
4066 /*
4067 * Testcase 7
4068 *
4069 * WGS84 int
4070 * band = 1
4071 * roundingDirection = nearest
4072 * decimals = 2
4073 */
4074 QTest::newRow( "testcase 7" )
4075 << "/raster/band1_int16_noct_epsg4326.tif"
4076 << QStringLiteral( "/roundRasterValues_testcase7.tif" )
4077 << 1
4078 << 1
4079 << -1
4080 << 10;
4081
4082 /*
4083 * Testcase 8
4084 *
4085 * WGS84 int
4086 * band = 1
4087 * roundingDirection = nearest
4088 * decimals = -1
4089 */
4090 QTest::newRow( "testcase 8" )
4091 << "/raster/band1_int16_noct_epsg4326.tif"
4092 << QStringLiteral( "/roundRasterValues_testcase8.tif" )
4093 << 1
4094 << 1
4095 << -1
4096 << 10;
4097
4098 }
4099
roundRasterValues()4100 void TestQgsProcessingAlgs::roundRasterValues()
4101 {
4102 QFETCH( QString, inputRaster );
4103 QFETCH( QString, expectedRaster );
4104 QFETCH( int, inputBand );
4105 QFETCH( int, roundingDirection );
4106 QFETCH( int, decimals );
4107 QFETCH( int, baseN );
4108
4109 //prepare input params
4110 QgsProject p;
4111 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:roundrastervalues" ) ) );
4112
4113 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
4114
4115 std::unique_ptr<QgsRasterLayer> inputRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + inputRaster, "inputDataset", "gdal" );
4116
4117 //set project crs and ellipsoid from input layer
4118 p.setCrs( inputRasterLayer->crs(), true );
4119
4120 //set project after layer has been added so that transform context/ellipsoid from crs is also set
4121 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4122 context->setProject( &p );
4123
4124 QVariantMap parameters;
4125
4126 parameters.insert( QStringLiteral( "INPUT" ), QString( myDataPath + inputRaster ) );
4127 parameters.insert( QStringLiteral( "BAND" ), inputBand );
4128 parameters.insert( QStringLiteral( "ROUNDING_DIRECTION" ), roundingDirection );
4129 parameters.insert( QStringLiteral( "DECIMAL_PLACES" ), decimals );
4130 parameters.insert( QStringLiteral( "BASE_N" ), baseN );
4131 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
4132
4133 //prepare expectedRaster
4134 std::unique_ptr<QgsRasterLayer> expectedRasterLayer = std::make_unique< QgsRasterLayer >( myDataPath + "/control_images/roundRasterValues/" + expectedRaster, "expectedDataset", "gdal" );
4135 std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
4136 QgsRasterIterator expectedIter( expectedInterface.get() );
4137 expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
4138
4139 //run alg...
4140
4141 bool ok = false;
4142 QgsProcessingFeedback feedback;
4143 QVariantMap results;
4144
4145 results = alg->run( parameters, *context, &feedback, &ok );
4146 QVERIFY( ok );
4147
4148 //...and check results with expected datasets
4149 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
4150 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
4151
4152 QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
4153 QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
4154
4155 QgsRasterIterator outputIter( outputInterface.get() );
4156 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
4157 int outputIterLeft = 0;
4158 int outputIterTop = 0;
4159 int outputIterCols = 0;
4160 int outputIterRows = 0;
4161 int expectedIterLeft = 0;
4162 int expectedIterTop = 0;
4163 int expectedIterCols = 0;
4164 int expectedIterRows = 0;
4165
4166 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
4167 std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
4168
4169 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
4170 expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
4171 {
4172 for ( int row = 0; row < expectedIterRows; row++ )
4173 {
4174 for ( int column = 0; column < expectedIterCols; column++ )
4175 {
4176 const double expectedValue = expectedRasterBlock->value( row, column );
4177 const double outputValue = outputRasterBlock->value( row, column );
4178 QCOMPARE( outputValue, expectedValue );
4179 }
4180 }
4181 }
4182 }
4183
layoutMapExtent()4184 void TestQgsProcessingAlgs::layoutMapExtent()
4185 {
4186 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:printlayoutmapextenttolayer" ) ) );
4187 QVERIFY( alg != nullptr );
4188
4189 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4190 QgsProject p;
4191 context->setProject( &p );
4192
4193 QVariantMap parameters;
4194 parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "l" ) );
4195 parameters.insert( QStringLiteral( "MAP" ), QStringLiteral( "m" ) );
4196 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
4197
4198 bool ok = false;
4199 QgsProcessingFeedback feedback;
4200 QVariantMap results;
4201 // no layout
4202 results = alg->run( parameters, *context, &feedback, &ok );
4203 QVERIFY( !ok );
4204
4205 QgsPrintLayout *layout = new QgsPrintLayout( &p );
4206 layout->setName( QStringLiteral( "l" ) );
4207 p.layoutManager()->addLayout( layout );
4208
4209 // no matching map
4210 results = alg->run( parameters, *context, &feedback, &ok );
4211 QVERIFY( !ok );
4212
4213 QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
4214 layout->addLayoutItem( map );
4215 map->setId( QStringLiteral( "m" ) );
4216 map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) );
4217 map->attemptSetSceneRect( QRectF( 100, 100, 150, 180 ) );
4218 map->zoomToExtent( QgsRectangle( 10000, 100000, 60000, 180000 ) );
4219 map->setMapRotation( 45 );
4220 map->setScale( 10000 );
4221 QgsLayoutItemMap *map2 = new QgsLayoutItemMap( layout );
4222 layout->addLayoutItem( map2 );
4223 map2->setId( QStringLiteral( "m2" ) );
4224 map2->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3785" ) ) );
4225 map2->attemptSetSceneRect( QRectF( 100, 100, 50, 80 ) );
4226 map2->zoomToExtent( QgsRectangle( 10000, 100000, 5000, 8000 ) );
4227 map2->setMapRotation( 0 );
4228 map2->setScale( 1000 );
4229
4230 results = alg->run( parameters, *context, &feedback, &ok );
4231 QVERIFY( ok );
4232
4233 QCOMPARE( results.value( QStringLiteral( "WIDTH" ) ).toDouble(), 150.0 );
4234 QCOMPARE( results.value( QStringLiteral( "HEIGHT" ) ).toDouble(), 180.0 );
4235 QCOMPARE( results.value( QStringLiteral( "SCALE" ) ).toDouble(), 10000.0 );
4236 QCOMPARE( results.value( QStringLiteral( "ROTATION" ) ).toDouble(), 45.0 );
4237
4238 QgsFeature f;
4239 QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:3111" ) );
4240 QVERIFY( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures().nextFeature( f ) );
4241 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m" ) );
4242 QCOMPARE( f.attribute( 1 ).toDouble(), 150.0 );
4243 QCOMPARE( f.attribute( 2 ).toDouble(), 180.0 );
4244 QCOMPARE( f.attribute( 3 ).toDouble(), 10000.0 );
4245 QCOMPARE( f.attribute( 4 ).toDouble(), 45.0 );
4246 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((33833 140106, 34894 141167, 36167 139894, 35106 138833, 33833 140106))" ) );
4247
4248 // all maps
4249 parameters.remove( QStringLiteral( "MAP" ) );
4250 results = alg->run( parameters, *context, &feedback, &ok );
4251 QVERIFY( ok );
4252
4253 QVERIFY( !results.value( QStringLiteral( "WIDTH" ) ).isValid() );
4254 QVERIFY( !results.value( QStringLiteral( "HEIGHT" ) ).isValid() );
4255 QVERIFY( !results.value( QStringLiteral( "SCALE" ) ).isValid() );
4256 QVERIFY( !results.value( QStringLiteral( "ROTATION" ) ).isValid() );
4257
4258 QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:3785" ) );
4259 QgsFeatureIterator it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
4260 QgsFeature f1;
4261 QVERIFY( it.nextFeature( f1 ) );
4262 QgsFeature f2;
4263 QVERIFY( it.nextFeature( f2 ) );
4264 f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f1 : f2;
4265 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m" ) );
4266 QCOMPARE( f.attribute( 1 ).toDouble(), 150.0 );
4267 QCOMPARE( f.attribute( 2 ).toDouble(), 180.0 );
4268 QCOMPARE( f.attribute( 3 ).toDouble(), 10000.0 );
4269 QCOMPARE( f.attribute( 4 ).toDouble(), 45.0 );
4270 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((12077408 -7108521, 12079627 -7107575, 12080760 -7110245, 12078540 -7111191, 12077408 -7108521))" ) );
4271 f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f2 : f1;
4272 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m2" ) );
4273 QCOMPARE( f.attribute( 1 ).toDouble(), 50.0 );
4274 QCOMPARE( f.attribute( 2 ).toDouble(), 80.0 );
4275 QGSCOMPARENEAR( f.attribute( 3 ).toDouble(), 1000.0, 0.0001 );
4276 QCOMPARE( f.attribute( 4 ).toDouble(), 0.0 );
4277 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((7475 54040, 7525 54040, 7525 53960, 7475 53960, 7475 54040))" ) );
4278
4279 // crs override
4280 parameters.insert( QStringLiteral( "CRS" ), QStringLiteral( "EPSG:3111" ) );
4281 results = alg->run( parameters, *context, &feedback, &ok );
4282 QVERIFY( ok );
4283
4284 QVERIFY( !results.value( QStringLiteral( "WIDTH" ) ).isValid() );
4285 QVERIFY( !results.value( QStringLiteral( "HEIGHT" ) ).isValid() );
4286 QVERIFY( !results.value( QStringLiteral( "SCALE" ) ).isValid() );
4287 QVERIFY( !results.value( QStringLiteral( "ROTATION" ) ).isValid() );
4288
4289 QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:3111" ) );
4290 it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
4291 QVERIFY( it.nextFeature( f1 ) );
4292 QVERIFY( it.nextFeature( f2 ) );
4293 f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f1 : f2;
4294 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m" ) );
4295 QCOMPARE( f.attribute( 1 ).toDouble(), 150.0 );
4296 QCOMPARE( f.attribute( 2 ).toDouble(), 180.0 );
4297 QCOMPARE( f.attribute( 3 ).toDouble(), 10000.0 );
4298 QCOMPARE( f.attribute( 4 ).toDouble(), 45.0 );
4299 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((33833 140106, 34894 141167, 36167 139894, 35106 138833, 33833 140106))" ) );
4300 f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f2 : f1;
4301 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m2" ) );
4302 QCOMPARE( f.attribute( 1 ).toDouble(), 50.0 );
4303 QCOMPARE( f.attribute( 2 ).toDouble(), 80.0 );
4304 QGSCOMPARENEAR( f.attribute( 3 ).toDouble(), 1000.0, 0.0001 );
4305 QCOMPARE( f.attribute( 4 ).toDouble(), 0.0 );
4306 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((-10399464 -5347896, -10399461 -5347835, -10399364 -5347840, -10399367 -5347901, -10399464 -5347896))" ) );
4307
4308 }
4309
styleFromProject()4310 void TestQgsProcessingAlgs::styleFromProject()
4311 {
4312 QgsProject p;
4313 QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
4314 QVERIFY( vl->isValid() );
4315 p.addMapLayer( vl );
4316 QgsSimpleMarkerSymbolLayer *simpleMarkerLayer = new QgsSimpleMarkerSymbolLayer();
4317 QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol();
4318 markerSymbol->changeSymbolLayer( 0, simpleMarkerLayer );
4319 vl->setRenderer( new QgsSingleSymbolRenderer( markerSymbol ) );
4320 // rule based renderer
4321 QgsVectorLayer *vl2 = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
4322 QVERIFY( vl2->isValid() );
4323 p.addMapLayer( vl2 );
4324 QgsSymbol *s1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
4325 s1->setColor( QColor( 0, 255, 0 ) );
4326 QgsSymbol *s2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
4327 s2->setColor( QColor( 0, 255, 255 ) );
4328 QgsRuleBasedRenderer::Rule *rootRule = new QgsRuleBasedRenderer::Rule( nullptr );
4329 QgsRuleBasedRenderer::Rule *rule2 = new QgsRuleBasedRenderer::Rule( s1, 0, 0, QStringLiteral( "fld >= 5 and fld <= 20" ) );
4330 rootRule->appendChild( rule2 );
4331 QgsRuleBasedRenderer::Rule *rule3 = new QgsRuleBasedRenderer::Rule( s2, 0, 0, QStringLiteral( "fld <= 10" ) );
4332 rule2->appendChild( rule3 );
4333 vl2->setRenderer( new QgsRuleBasedRenderer( rootRule ) );
4334 // labeling
4335 QgsPalLayerSettings settings;
4336 settings.fieldName = QStringLiteral( "Class" );
4337 vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
4338 // raster layer
4339 QgsRasterLayer *rl = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/tenbytenraster.asc",
4340 QStringLiteral( "rl" ) );
4341 QVERIFY( rl->isValid() );
4342 p.addMapLayer( rl );
4343
4344 QgsRasterShader *rasterShader = new QgsRasterShader();
4345 QgsColorRampShader *colorRampShader = new QgsColorRampShader();
4346 colorRampShader->setColorRampType( QgsColorRampShader::Interpolated );
4347 colorRampShader->setSourceColorRamp( new QgsGradientColorRamp( QColor( 255, 255, 0 ), QColor( 255, 0, 255 ) ) );
4348 rasterShader->setRasterShaderFunction( colorRampShader );
4349 QgsSingleBandPseudoColorRenderer *r = new QgsSingleBandPseudoColorRenderer( rl->dataProvider(), 1, rasterShader );
4350 rl->setRenderer( r );
4351
4352 // with layout
4353 QgsPrintLayout *l = new QgsPrintLayout( &p );
4354 l->setName( QStringLiteral( "test layout" ) );
4355 l->initializeDefaults();
4356 QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( l );
4357 scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) );
4358 l->addLayoutItem( scalebar );
4359 scalebar->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) );
4360
4361 p.layoutManager()->addLayout( l );
4362
4363 // with annotations
4364 QgsTextAnnotation *annotation = new QgsTextAnnotation();
4365 QgsSymbol *a1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
4366 a1->setColor( QColor( 0, 200, 0 ) );
4367 annotation->setMarkerSymbol( static_cast< QgsMarkerSymbol * >( a1 ) );
4368 QgsSymbol *a2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry );
4369 a2->setColor( QColor( 200, 200, 0 ) );
4370 annotation->setFillSymbol( static_cast< QgsFillSymbol * >( a2 ) );
4371 p.annotationManager()->addAnnotation( annotation );
4372
4373 // ok, run alg
4374 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:stylefromproject" ) ) );
4375 QVERIFY( alg != nullptr );
4376
4377 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4378 context->setProject( &p );
4379
4380 QVariantMap parameters;
4381 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
4382
4383 bool ok = false;
4384 QgsProcessingFeedback feedback;
4385 QVariantMap results;
4386 results = alg->run( parameters, *context, &feedback, &ok );
4387 QVERIFY( ok );
4388 QCOMPARE( results.value( QStringLiteral( "SYMBOLS" ) ).toInt(), 6 );
4389 QCOMPARE( results.value( QStringLiteral( "COLORRAMPS" ) ).toInt(), 1 );
4390 QCOMPARE( results.value( QStringLiteral( "TEXTFORMATS" ) ).toInt(), 1 );
4391 QCOMPARE( results.value( QStringLiteral( "LABELSETTINGS" ) ).toInt(), 1 );
4392
4393 // read style file back in
4394 QgsStyle s;
4395 s.createMemoryDatabase();
4396 QVERIFY( s.importXml( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
4397 QCOMPARE( s.symbolCount(), 6 );
4398 QVERIFY( s.symbolNames().contains( QStringLiteral( "Annotation Fill" ) ) );
4399 QVERIFY( s.symbolNames().contains( QStringLiteral( "Annotation Marker" ) ) );
4400 QVERIFY( s.symbolNames().contains( QStringLiteral( "test layout Page" ) ) );
4401 QVERIFY( s.symbolNames().contains( QStringLiteral( "vl" ) ) );
4402 QVERIFY( s.symbolNames().contains( QStringLiteral( "vl2" ) ) );
4403 QVERIFY( s.symbolNames().contains( QStringLiteral( "vl2 (2)" ) ) );
4404 QCOMPARE( s.colorRampCount(), 1 );
4405 QVERIFY( s.colorRampNames().contains( QStringLiteral( "rl" ) ) );
4406 QCOMPARE( s.textFormatCount(), 1 );
4407 QVERIFY( s.textFormatNames().contains( QStringLiteral( "test layout <Scalebar>" ) ) );
4408 QCOMPARE( s.labelSettingsCount(), 1 );
4409 QVERIFY( s.labelSettingsNames().contains( QStringLiteral( "vl" ) ) );
4410
4411 // using a project path
4412 QTemporaryFile tmpFile;
4413 tmpFile.open();
4414 tmpFile.close();
4415 QVERIFY( p.write( tmpFile.fileName() ) );
4416 p.clear();
4417 parameters.insert( QStringLiteral( "INPUT" ), tmpFile.fileName() );
4418 ok = false;
4419 results = alg->run( parameters, *context, &feedback, &ok );
4420 QVERIFY( ok );
4421 QCOMPARE( results.value( QStringLiteral( "SYMBOLS" ) ).toInt(), 6 );
4422 // this should be 1, but currently raster layers aren't supported -
4423 // we first need to allow raster renderers to be read and restored for invalid layer sources
4424 QCOMPARE( results.value( QStringLiteral( "COLORRAMPS" ) ).toInt(), 0 );
4425 QCOMPARE( results.value( QStringLiteral( "TEXTFORMATS" ) ).toInt(), 1 );
4426 QCOMPARE( results.value( QStringLiteral( "LABELSETTINGS" ) ).toInt(), 1 );
4427 }
4428
combineStyles()4429 void TestQgsProcessingAlgs::combineStyles()
4430 {
4431 QgsStyle s1;
4432 s1.createMemoryDatabase();
4433 QgsStyle s2;
4434 s2.createMemoryDatabase();
4435
4436 QgsSimpleMarkerSymbolLayer *simpleMarkerLayer = new QgsSimpleMarkerSymbolLayer();
4437 QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol();
4438 markerSymbol->changeSymbolLayer( 0, simpleMarkerLayer );
4439 s1.addSymbol( QStringLiteral( "sym1" ), markerSymbol, true );
4440 s1.tagSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ), QStringList() << QStringLiteral( "t1" ) << QStringLiteral( "t2" ) );
4441
4442 QgsSymbol *sym1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
4443 s2.addSymbol( QStringLiteral( "sym2" ), sym1, true );
4444 QgsSymbol *sym2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
4445 s2.addSymbol( QStringLiteral( "sym1" ), sym2, true );
4446
4447 QgsPalLayerSettings settings;
4448 settings.fieldName = QStringLiteral( "Class" );
4449 s1.addLabelSettings( QStringLiteral( "label1" ), settings, true );
4450
4451 s2.addColorRamp( QStringLiteral( "ramp1" ), new QgsGradientColorRamp( QColor( 255, 255, 0 ), QColor( 255, 0, 255 ) ), true );
4452 s2.addTextFormat( QStringLiteral( "format2" ), QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ), true );
4453
4454 QTemporaryFile tmpFile;
4455 tmpFile.open();
4456 tmpFile.close();
4457 QVERIFY( s1.exportXml( tmpFile.fileName() ) );
4458 QTemporaryFile tmpFile2;
4459 tmpFile2.open();
4460 tmpFile2.close();
4461 QVERIFY( s2.exportXml( tmpFile2.fileName() ) );
4462
4463 // ok, run alg
4464 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:combinestyles" ) ) );
4465 QVERIFY( alg != nullptr );
4466
4467 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4468
4469 QVariantMap parameters;
4470 parameters.insert( QStringLiteral( "INPUT" ), QStringList() << tmpFile.fileName() << tmpFile2.fileName() );
4471 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
4472
4473 bool ok = false;
4474 QgsProcessingFeedback feedback;
4475 QVariantMap results;
4476 results = alg->run( parameters, *context, &feedback, &ok );
4477 QVERIFY( ok );
4478 QCOMPARE( results.value( QStringLiteral( "SYMBOLS" ) ).toInt(), 3 );
4479 QCOMPARE( results.value( QStringLiteral( "COLORRAMPS" ) ).toInt(), 1 );
4480 QCOMPARE( results.value( QStringLiteral( "TEXTFORMATS" ) ).toInt(), 1 );
4481 QCOMPARE( results.value( QStringLiteral( "LABELSETTINGS" ) ).toInt(), 1 );
4482
4483 // check result
4484 QgsStyle s;
4485 s.createMemoryDatabase();
4486 QVERIFY( s.importXml( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
4487 QCOMPARE( s.symbolCount(), 3 );
4488 QVERIFY( s.symbolNames().contains( QStringLiteral( "sym1" ) ) );
4489 QVERIFY( s.symbolNames().contains( QStringLiteral( "sym2" ) ) );
4490 QVERIFY( s.symbolNames().contains( QStringLiteral( "sym1 (2)" ) ) );
4491 QCOMPARE( s.tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ) ).count(), 2 );
4492 QVERIFY( s.tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ) ).contains( QStringLiteral( "t1" ) ) );
4493 QVERIFY( s.tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ) ).contains( QStringLiteral( "t2" ) ) );
4494 QCOMPARE( s.colorRampCount(), 1 );
4495 QVERIFY( s.colorRampNames().contains( QStringLiteral( "ramp1" ) ) );
4496 QCOMPARE( s.textFormatCount(), 1 );
4497 QVERIFY( s.textFormatNames().contains( QStringLiteral( "format2" ) ) );
4498 QCOMPARE( s.labelSettingsCount(), 1 );
4499 QVERIFY( s.labelSettingsNames().contains( QStringLiteral( "label1" ) ) );
4500 }
4501
bookmarksToLayer()4502 void TestQgsProcessingAlgs::bookmarksToLayer()
4503 {
4504 QgsApplication::bookmarkManager()->clear();
4505 // create some bookmarks
4506 QgsBookmark b1;
4507 b1.setName( QStringLiteral( "test name" ) );
4508 b1.setGroup( QStringLiteral( "test group" ) );
4509 b1.setExtent( QgsReferencedRectangle( QgsRectangle( 1, 2, 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ) );
4510 QgsApplication::bookmarkManager()->addBookmark( b1 );
4511
4512 QgsBookmark b2;
4513 b2.setName( QStringLiteral( "test name 2" ) );
4514 b2.setExtent( QgsReferencedRectangle( QgsRectangle( 16259461, -2477192, 16391255, -2372535 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) ) );
4515 QgsApplication::bookmarkManager()->addBookmark( b2 );
4516 QgsBookmark b3;
4517 b3.setName( QStringLiteral( "test name 3" ) );
4518 b3.setExtent( QgsReferencedRectangle( QgsRectangle( 11, 21, 31, 41 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ) );
4519 QgsProject p;
4520 p.bookmarkManager()->addBookmark( b3 );
4521
4522 // ok, run alg
4523 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:bookmarkstolayer" ) ) );
4524 QVERIFY( alg != nullptr );
4525
4526 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4527 context->setProject( &p );
4528
4529 QVariantMap parameters;
4530 parameters.insert( QStringLiteral( "SOURCE" ), QVariantList() << 0 );
4531 parameters.insert( QStringLiteral( "CRS" ), QStringLiteral( "EPSG:4326" ) );
4532 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
4533
4534 bool ok = false;
4535 QgsProcessingFeedback feedback;
4536 QVariantMap results;
4537 results = alg->run( parameters, *context, &feedback, &ok );
4538 QVERIFY( ok );
4539
4540 // check result
4541 QgsFeature f;
4542 QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:4326" ) );
4543 QgsFeatureIterator it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
4544 QVERIFY( it.nextFeature( f ) );
4545 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 3" ) );
4546 QCOMPARE( f.attribute( 1 ).toString(), QString() );
4547 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((11 21, 31 21, 31 41, 11 41, 11 21))" ) );
4548 QVERIFY( !it.nextFeature( f ) );
4549
4550 // user bookmarks
4551 parameters.insert( QStringLiteral( "SOURCE" ), QVariantList() << 1 );
4552 ok = false;
4553 alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:bookmarkstolayer" ) ) );
4554 results = alg->run( parameters, *context, &feedback, &ok );
4555 QVERIFY( ok );
4556 QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:4326" ) );
4557 it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
4558 QVERIFY( it.nextFeature( f ) );
4559 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name" ) );
4560 QCOMPARE( f.attribute( 1 ).toString(), QStringLiteral( "test group" ) );
4561 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((1 2, 3 2, 3 4, 1 4, 1 2))" ) );
4562 QVERIFY( it.nextFeature( f ) );
4563 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 2" ) );
4564 QCOMPARE( f.attribute( 1 ).toString(), QString() );
4565 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22))" ) );
4566 QVERIFY( !it.nextFeature( f ) );
4567
4568 // both
4569 parameters.insert( QStringLiteral( "SOURCE" ), QVariantList() << 0 << 1 );
4570 ok = false;
4571 alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:bookmarkstolayer" ) ) );
4572 results = alg->run( parameters, *context, &feedback, &ok );
4573 QVERIFY( ok );
4574 QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:4326" ) );
4575 it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
4576 QVERIFY( it.nextFeature( f ) );
4577 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 3" ) );
4578 QCOMPARE( f.attribute( 1 ).toString(), QString() );
4579 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((11 21, 31 21, 31 41, 11 41, 11 21))" ) );
4580 QVERIFY( it.nextFeature( f ) );
4581 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name" ) );
4582 QCOMPARE( f.attribute( 1 ).toString(), QStringLiteral( "test group" ) );
4583 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((1 2, 3 2, 3 4, 1 4, 1 2))" ) );
4584 QVERIFY( it.nextFeature( f ) );
4585 QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 2" ) );
4586 QCOMPARE( f.attribute( 1 ).toString(), QString() );
4587 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22))" ) );
4588 QVERIFY( !it.nextFeature( f ) );
4589
4590 }
4591
layerToBookmarks()4592 void TestQgsProcessingAlgs::layerToBookmarks()
4593 {
4594 std::unique_ptr<QgsVectorLayer> inputLayer( std::make_unique<QgsVectorLayer>( QStringLiteral( "Polygon?crs=epsg:4326&field=province:string&field=municipality:string" ), QStringLiteral( "layer" ), QStringLiteral( "memory" ) ) );
4595 QVERIFY( inputLayer->isValid() );
4596
4597 QgsFeature f;
4598 f.setAttributes( QgsAttributes() << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
4599 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((11 21, 31 21, 31 41, 11 41, 11 21))" ) ) );
4600 inputLayer->dataProvider()->addFeature( f );
4601 f.setAttributes( QgsAttributes() << QStringLiteral( "b2" ) << QString() );
4602 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -22, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 147 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -21, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22, 146 -22))" ) ) );
4603 inputLayer->dataProvider()->addFeature( f );
4604
4605 QgsApplication::bookmarkManager()->clear();
4606
4607 // run alg
4608 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:layertobookmarks" ) ) );
4609 QVERIFY( alg != nullptr );
4610
4611 QgsProject p;
4612 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4613 context->setProject( &p );
4614
4615 QVariantMap parameters;
4616 parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( inputLayer.get() ) );
4617 parameters.insert( QStringLiteral( "DESTINATION" ), 0 );
4618 parameters.insert( QStringLiteral( "NAME_EXPRESSION" ), QStringLiteral( "upper(province)" ) );
4619 parameters.insert( QStringLiteral( "GROUP_EXPRESSION" ), QStringLiteral( "upper(municipality)" ) );
4620
4621 bool ok = false;
4622 QgsProcessingFeedback feedback;
4623 QVariantMap results;
4624 results = alg->run( parameters, *context, &feedback, &ok );
4625 QVERIFY( ok );
4626
4627 QCOMPARE( p.bookmarkManager()->bookmarks().count(), 2 );
4628 QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).name(), QStringLiteral( "B1" ) );
4629 QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).group(), QStringLiteral( "G1" ) );
4630 QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
4631 QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).extent().toString( 0 ), QStringLiteral( "11,21 : 31,41" ) );
4632 QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).name(), QStringLiteral( "B2" ) );
4633 QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).group(), QString() );
4634 QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
4635 QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).extent().toString( 0 ), QStringLiteral( "146,-22 : 147,-21" ) );
4636 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().count(), 0 );
4637 p.bookmarkManager()->clear();
4638
4639 // send to application bookmarks
4640 parameters.insert( QStringLiteral( "DESTINATION" ), 1 );
4641 parameters.insert( QStringLiteral( "GROUP_EXPRESSION" ), QVariant() );
4642
4643 ok = false;
4644 alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:layertobookmarks" ) ) );
4645 results = alg->run( parameters, *context, &feedback, &ok );
4646 QVERIFY( ok );
4647
4648 QCOMPARE( p.bookmarkManager()->bookmarks().count(), 0 );
4649 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().count(), 2 );
4650 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).name(), QStringLiteral( "B1" ) );
4651 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).group(), QString() );
4652 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
4653 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).extent().toString( 0 ), QStringLiteral( "11,21 : 31,41" ) );
4654 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).name(), QStringLiteral( "B2" ) );
4655 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).group(), QString() );
4656 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
4657 QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).extent().toString( 0 ), QStringLiteral( "146,-22 : 147,-21" ) );
4658 }
4659
repairShapefile()4660 void TestQgsProcessingAlgs::repairShapefile()
4661 {
4662 const QTemporaryDir tmpPath;
4663
4664 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
4665 QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
4666 QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.prj" ) ) );
4667 QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.dbf" ) ) );
4668 // no shx!!
4669
4670 std::unique_ptr< QgsVectorLayer > layer = std::make_unique< QgsVectorLayer >( tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
4671 QVERIFY( !layer->isValid() );
4672
4673 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:repairshapefile" ) ) );
4674 QVERIFY( alg != nullptr );
4675
4676 QVariantMap parameters;
4677 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "not a file" ) );
4678
4679 bool ok = false;
4680 QgsProcessingFeedback feedback;
4681 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4682
4683 QVariantMap results;
4684 results = alg->run( parameters, *context, &feedback, &ok );
4685 QVERIFY( !ok );
4686
4687 parameters.insert( QStringLiteral( "INPUT" ), tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
4688 ok = false;
4689 results = alg->run( parameters, *context, &feedback, &ok );
4690 QVERIFY( ok );
4691 QCOMPARE( results.value( QStringLiteral( "OUTPUT" ) ).toString(), tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
4692
4693 layer = std::make_unique< QgsVectorLayer >( tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
4694 QVERIFY( layer->isValid() );
4695 }
4696
renameField()4697 void TestQgsProcessingAlgs::renameField()
4698 {
4699 QgsProject p;
4700 QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
4701 QVERIFY( layer->isValid() );
4702 p.addMapLayer( layer );
4703
4704 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:renametablefield" ) ) );
4705 QVERIFY( alg != nullptr );
4706
4707 QVariantMap parameters;
4708 parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( layer ) );
4709 parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "doesntexist" ) );
4710 parameters.insert( QStringLiteral( "NEW_NAME" ), QStringLiteral( "newname" ) );
4711
4712 bool ok = false;
4713 QgsProcessingFeedback feedback;
4714 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4715
4716 QVariantMap results;
4717 results = alg->run( parameters, *context, &feedback, &ok );
4718 // field doesn't exist
4719 QVERIFY( !ok );
4720
4721 parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "col1" ) );
4722 parameters.insert( QStringLiteral( "NEW_NAME" ), QStringLiteral( "pk" ) );
4723
4724 ok = false;
4725 results = alg->run( parameters, *context, &feedback, &ok );
4726 // already a field with this name
4727 QVERIFY( !ok );
4728
4729 parameters.insert( QStringLiteral( "NEW_NAME" ), QStringLiteral( "newname" ) );
4730 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
4731 results = alg->run( parameters, *context, &feedback, &ok );
4732 QVERIFY( ok );
4733
4734 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->fields().at( 1 ).name(), QStringLiteral( "newname" ) );
4735
4736 }
4737
compareDatasets()4738 void TestQgsProcessingAlgs::compareDatasets()
4739 {
4740 QgsProject p;
4741 QgsVectorLayer *pointLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
4742 QVERIFY( pointLayer->isValid() );
4743 p.addMapLayer( pointLayer );
4744 QgsVectorLayer *originalLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326&field=pk:int&field=col1:string&field=col2:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
4745 QVERIFY( originalLayer->isValid() );
4746 p.addMapLayer( originalLayer );
4747 QgsVectorLayer *revisedLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326&field=pk:int&field=col1:string&field=col2:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
4748 QVERIFY( revisedLayer->isValid() );
4749 p.addMapLayer( revisedLayer );
4750 QgsVectorLayer *differentAttrs = new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326&field=pk:int&field=col3:string&field=col2:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
4751 QVERIFY( differentAttrs->isValid() );
4752 p.addMapLayer( differentAttrs );
4753
4754 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:detectvectorchanges" ) ) );
4755 QVERIFY( alg != nullptr );
4756
4757 QVariantMap parameters;
4758 // differing geometry types - alg should fail
4759 parameters.insert( QStringLiteral( "ORIGINAL" ), QVariant::fromValue( pointLayer ) );
4760 parameters.insert( QStringLiteral( "REVISED" ), QVariant::fromValue( originalLayer ) );
4761
4762 bool ok = false;
4763 QgsProcessingFeedback feedback;
4764 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4765
4766 QVariantMap results;
4767 results = alg->run( parameters, *context, &feedback, &ok );
4768 QVERIFY( !ok );
4769
4770 // differing fields - alg should fail
4771 parameters.insert( QStringLiteral( "ORIGINAL" ), QVariant::fromValue( originalLayer ) );
4772 parameters.insert( QStringLiteral( "REVISED" ), QVariant::fromValue( differentAttrs ) );
4773 parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2" ) );
4774 results = alg->run( parameters, *context, &feedback, &ok );
4775 QVERIFY( !ok );
4776
4777 // yet if we aren't comparing the field, we shouldn't fail...
4778 parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col2" ) );
4779 results = alg->run( parameters, *context, &feedback, &ok );
4780 QVERIFY( ok );
4781
4782 // no features, no outputs
4783 parameters.insert( QStringLiteral( "ORIGINAL" ), QVariant::fromValue( originalLayer ) );
4784 parameters.insert( QStringLiteral( "REVISED" ), QVariant::fromValue( revisedLayer ) );
4785 results = alg->run( parameters, *context, &feedback, &ok );
4786 QVERIFY( ok );
4787 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 0LL );
4788 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 0LL );
4789 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 0LL );
4790
4791 parameters.insert( QStringLiteral( "UNCHANGED" ), QgsProcessing::TEMPORARY_OUTPUT );
4792 parameters.insert( QStringLiteral( "ADDED" ), QgsProcessing::TEMPORARY_OUTPUT );
4793 parameters.insert( QStringLiteral( "DELETED" ), QgsProcessing::TEMPORARY_OUTPUT );
4794
4795 // add two features to original
4796 QgsFeature f;
4797 f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
4798 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
4799 originalLayer->dataProvider()->addFeature( f );
4800 f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
4801 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
4802 originalLayer->dataProvider()->addFeature( f );
4803
4804 // just compare two columns
4805 parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2" ) );
4806
4807 results = alg->run( parameters, *context, &feedback, &ok );
4808 QVERIFY( ok );
4809 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 0LL );
4810 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "UNCHANGED" ) ).toString() ) )->featureCount(), 0L );
4811 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 0LL );
4812 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "ADDED" ) ).toString() ) )->featureCount(), 0L );
4813 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 2LL );
4814 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "DELETED" ) ).toString() ) )->featureCount(), 2L );
4815
4816 // add one same to revised - note that the first attributes differs here, but we aren't considering that
4817 f.setAttributes( QgsAttributes() << 55 << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
4818 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
4819 revisedLayer->dataProvider()->addFeature( f );
4820
4821 results = alg->run( parameters, *context, &feedback, &ok );
4822 QVERIFY( ok );
4823 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 1LL );
4824 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "UNCHANGED" ) ).toString() ) )->featureCount(), 1L );
4825 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 0LL );
4826 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "ADDED" ) ).toString() ) )->featureCount(), 0L );
4827 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
4828 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "DELETED" ) ).toString() ) )->featureCount(), 1L );
4829
4830 // ok, let's compare the differing attribute too
4831 parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2;pk" ) );
4832 results = alg->run( parameters, *context, &feedback, &ok );
4833 QVERIFY( ok );
4834 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 0LL );
4835 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 1LL );
4836 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 2LL );
4837
4838 parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2" ) );
4839 // similar to the second feature, but geometry differs
4840 f.setAttributes( QgsAttributes() << 55 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
4841 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 11 0)" ) ) );
4842 revisedLayer->dataProvider()->addFeature( f );
4843 results = alg->run( parameters, *context, &feedback, &ok );
4844 QVERIFY( ok );
4845 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 1LL );
4846 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "UNCHANGED" ) ).toString() ) )->featureCount(), 1L );
4847 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 1LL );
4848 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "ADDED" ) ).toString() ) )->featureCount(), 1L );
4849 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
4850 QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "DELETED" ) ).toString() ) )->featureCount(), 1L );
4851 // note - we skip the featureCount checks after this -- we can be confident at this stage that all sinks are correctly being populated
4852
4853
4854 // add another which is identical to first, must be considered as another "added" feature (i.e.
4855 // don't match to same original feature multiple times)
4856 f.setAttributes( QgsAttributes() << 555 << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
4857 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
4858 revisedLayer->dataProvider()->addFeature( f );
4859 results = alg->run( parameters, *context, &feedback, &ok );
4860 QVERIFY( ok );
4861 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 1LL );
4862 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
4863 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
4864
4865 // add a match for the second feature
4866 f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
4867 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
4868 revisedLayer->dataProvider()->addFeature( f );
4869 results = alg->run( parameters, *context, &feedback, &ok );
4870 QVERIFY( ok );
4871 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 2LL );
4872 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
4873 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 0LL );
4874
4875 // test topological match (different number of vertices)
4876 f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
4877 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 10, 5 10, 10 10)" ) ) );
4878 originalLayer->dataProvider()->addFeature( f );
4879 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 10, 1 10, 5 10, 10 10)" ) ) );
4880 revisedLayer->dataProvider()->addFeature( f );
4881
4882 // exact match shouldn't equate the two
4883 parameters.insert( QStringLiteral( "MATCH_TYPE" ), 0 );
4884 results = alg->run( parameters, *context, &feedback, &ok );
4885 QVERIFY( ok );
4886 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 2LL );
4887 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 3LL );
4888 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
4889
4890 // but topological match should
4891 parameters.insert( QStringLiteral( "MATCH_TYPE" ), 1 );
4892 results = alg->run( parameters, *context, &feedback, &ok );
4893 QVERIFY( ok );
4894 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 3LL );
4895 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
4896 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 0LL );
4897
4898 // null geometry comparisons
4899 f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "d1" ) << QStringLiteral( "g1" ) );
4900 f.clearGeometry();
4901 originalLayer->dataProvider()->addFeature( f );
4902 results = alg->run( parameters, *context, &feedback, &ok );
4903 QVERIFY( ok );
4904 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 3LL );
4905 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
4906 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
4907
4908 f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "e1" ) << QStringLiteral( "g1" ) );
4909 originalLayer->dataProvider()->addFeature( f );
4910 results = alg->run( parameters, *context, &feedback, &ok );
4911 QVERIFY( ok );
4912 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 3LL );
4913 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
4914 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 2LL );
4915
4916 f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "d1" ) << QStringLiteral( "g1" ) );
4917 revisedLayer->dataProvider()->addFeature( f );
4918 results = alg->run( parameters, *context, &feedback, &ok );
4919 QVERIFY( ok );
4920 QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 4LL );
4921 QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
4922 QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
4923
4924 }
4925
shapefileEncoding()4926 void TestQgsProcessingAlgs::shapefileEncoding()
4927 {
4928 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:shpencodinginfo" ) ) );
4929 QVERIFY( alg != nullptr );
4930
4931 QVariantMap parameters;
4932 parameters.insert( QStringLiteral( "INPUT" ), QString() );
4933
4934 bool ok = false;
4935 QgsProcessingFeedback feedback;
4936 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4937
4938 QVariantMap results;
4939 results = alg->run( parameters, *context, &feedback, &ok );
4940
4941 parameters.insert( QStringLiteral( "INPUT" ), QString( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/iso-8859-1.shp" ) );
4942 results = alg->run( parameters, *context, &feedback, &ok );
4943 QVERIFY( ok );
4944 QCOMPARE( results.value( QStringLiteral( "ENCODING" ) ).toString(), QStringLiteral( "ISO-8859-1" ) );
4945 QCOMPARE( results.value( QStringLiteral( "CPG_ENCODING" ) ).toString(), QStringLiteral( "ISO-8859-1" ) );
4946 QCOMPARE( results.value( QStringLiteral( "LDID_ENCODING" ) ).toString(), QString() );
4947
4948 parameters.insert( QStringLiteral( "INPUT" ), QString( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/windows-1252_ldid.shp" ) );
4949 results = alg->run( parameters, *context, &feedback, &ok );
4950 QVERIFY( ok );
4951 QCOMPARE( results.value( QStringLiteral( "ENCODING" ) ).toString(), QStringLiteral( "CP1252" ) );
4952 QCOMPARE( results.value( QStringLiteral( "CPG_ENCODING" ) ).toString(), QString() );
4953 QCOMPARE( results.value( QStringLiteral( "LDID_ENCODING" ) ).toString(), QStringLiteral( "CP1252" ) );
4954
4955 parameters.insert( QStringLiteral( "INPUT" ), QString( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/system_encoding.shp" ) );
4956 results = alg->run( parameters, *context, &feedback, &ok );
4957 QVERIFY( ok );
4958 QCOMPARE( results.value( QStringLiteral( "ENCODING" ) ).toString(), QString() );
4959 QCOMPARE( results.value( QStringLiteral( "CPG_ENCODING" ) ).toString(), QString() );
4960 QCOMPARE( results.value( QStringLiteral( "LDID_ENCODING" ) ).toString(), QString() );
4961 }
4962
setLayerEncoding()4963 void TestQgsProcessingAlgs::setLayerEncoding()
4964 {
4965 QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/system_encoding.shp",
4966 QStringLiteral( "test" ), QStringLiteral( "ogr" ) );
4967 QVERIFY( vl->isValid() );
4968 QgsProject p;
4969 p.addMapLayers(
4970 QList<QgsMapLayer *>() << vl );
4971
4972 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:setlayerencoding" ) ) );
4973 QVERIFY( alg != nullptr );
4974
4975 QVariantMap parameters;
4976 parameters.insert( QStringLiteral( "INPUT" ), QString() );
4977
4978 bool ok = false;
4979 QgsProcessingFeedback feedback;
4980 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
4981 context->setProject( &p );
4982
4983 QVariantMap results;
4984 results = alg->run( parameters, *context, &feedback, &ok );
4985 QVERIFY( !ok );
4986
4987 parameters.insert( QStringLiteral( "INPUT" ), vl->id() );
4988 parameters.insert( QStringLiteral( "ENCODING" ), QStringLiteral( "ISO-8859-1" ) );
4989 results = alg->run( parameters, *context, &feedback, &ok );
4990 QVERIFY( ok );
4991 QCOMPARE( vl->dataProvider()->encoding(), QStringLiteral( "ISO-8859-1" ) );
4992
4993 }
4994
4995 class TestProcessingFeedback : public QgsProcessingFeedback
4996 {
4997 public:
4998
reportError(const QString & error,bool)4999 void reportError( const QString &error, bool ) override
5000 {
5001 errors << error;
5002 }
5003
5004 QStringList errors;
5005
5006 };
5007
raiseException()5008 void TestQgsProcessingAlgs::raiseException()
5009 {
5010 TestProcessingFeedback feedback;
5011
5012 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:raiseexception" ) ) );
5013 QVERIFY( alg != nullptr );
5014
5015 QVariantMap parameters;
5016 parameters.insert( QStringLiteral( "MESSAGE" ), QStringLiteral( "you done screwed up boy" ) );
5017
5018 bool ok = false;
5019 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5020
5021 QVariantMap results;
5022 results = alg->run( parameters, *context, &feedback, &ok );
5023 QVERIFY( !ok );
5024
5025 QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you done screwed up boy" ) );
5026
5027 parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "FALSE" ) );
5028 feedback.errors.clear();
5029 results = alg->run( parameters, *context, &feedback, &ok );
5030 QVERIFY( ok );
5031
5032 QCOMPARE( feedback.errors, QStringList() );
5033
5034 parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "TRUE" ) );
5035 feedback.errors.clear();
5036 results = alg->run( parameters, *context, &feedback, &ok );
5037 QVERIFY( !ok );
5038
5039 QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you done screwed up boy" ) );
5040 }
5041
raiseWarning()5042 void TestQgsProcessingAlgs::raiseWarning()
5043 {
5044 TestProcessingFeedback feedback;
5045
5046 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:raisewarning" ) ) );
5047 QVERIFY( alg != nullptr );
5048
5049 QVariantMap parameters;
5050 parameters.insert( QStringLiteral( "MESSAGE" ), QStringLiteral( "you mighta screwed up boy, but i aint so sure" ) );
5051
5052 bool ok = false;
5053 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5054
5055 QVariantMap results;
5056 results = alg->run( parameters, *context, &feedback, &ok );
5057 QVERIFY( ok );
5058
5059 QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you mighta screwed up boy, but i aint so sure" ) );
5060
5061 parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "FALSE" ) );
5062 feedback.errors.clear();
5063 results = alg->run( parameters, *context, &feedback, &ok );
5064 QVERIFY( ok );
5065
5066 QCOMPARE( feedback.errors, QStringList() );
5067
5068 parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "TRUE" ) );
5069 feedback.errors.clear();
5070 results = alg->run( parameters, *context, &feedback, &ok );
5071 QVERIFY( ok );
5072
5073 QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you mighta screwed up boy, but i aint so sure" ) );
5074 }
5075
randomFloatingPointDistributionRaster_data()5076 void TestQgsProcessingAlgs::randomFloatingPointDistributionRaster_data()
5077 {
5078 QTest::addColumn<QString>( "inputExtent" );
5079 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
5080 QTest::addColumn<bool>( "succeeds" );
5081 QTest::addColumn<QString>( "crs" );
5082 QTest::addColumn<double>( "pixelSize" );
5083 QTest::addColumn<int>( "typeId" );
5084
5085 QTest::newRow( "testcase 1" )
5086 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5087 << Qgis::DataType::Float32
5088 << true
5089 << "EPSG:4326"
5090 << 1.0
5091 << 0;
5092
5093
5094 QTest::newRow( "testcase 2" )
5095 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5096 << Qgis::DataType::Float64
5097 << true
5098 << "EPSG:4326"
5099 << 1.0
5100 << 1;
5101 }
5102
randomFloatingPointDistributionRaster()5103 void TestQgsProcessingAlgs::randomFloatingPointDistributionRaster()
5104 {
5105 //this test only checks if the correct raster data type is chosen
5106 //value by value comparison is not effective for random rasters
5107 QFETCH( QString, inputExtent );
5108 QFETCH( Qgis::DataType, expectedDataType );
5109 QFETCH( bool, succeeds );
5110 QFETCH( QString, crs );
5111 QFETCH( double, pixelSize );
5112 QFETCH( int, typeId );
5113
5114 //prepare input params
5115 QgsProject p;
5116 //set project crs and ellipsoid from input layer
5117 p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
5118
5119 QStringList alglist = QStringList();
5120 alglist << QStringLiteral( "native:createrandomnormalrasterlayer" )
5121 << QStringLiteral( "native:createrandomexponentialrasterlayer" )
5122 << QStringLiteral( "native:createrandomgammarasterlayer" );
5123
5124 for ( int i = 0; i < alglist.length(); i++ )
5125 {
5126 const QString algname = alglist[i];
5127
5128 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algname ) );
5129 //set project after layer has been added so that transform context/ellipsoid from crs is also set
5130 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5131 context->setProject( &p );
5132
5133 QVariantMap parameters;
5134
5135 parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
5136 parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
5137 parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
5138 parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
5139 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
5140
5141 bool ok = false;
5142 QgsProcessingFeedback feedback;
5143 QVariantMap results;
5144
5145 if ( !succeeds )
5146 {
5147 //verify if user feedback for unacceptable values are thrown
5148 alg->run( parameters, *context, &feedback, &ok );
5149 QCOMPARE( ok, succeeds );
5150 }
5151 else
5152 {
5153 //run alg...
5154 results = alg->run( parameters, *context, &feedback, &ok );
5155 QVERIFY( ok );
5156
5157 //...and check results with expected datasets
5158 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
5159 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
5160
5161 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
5162 }
5163 }
5164 }
5165
5166
randomIntegerDistributionRaster_data()5167 void TestQgsProcessingAlgs::randomIntegerDistributionRaster_data()
5168 {
5169 QTest::addColumn<QString>( "inputExtent" );
5170 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
5171 QTest::addColumn<bool>( "succeeds" );
5172 QTest::addColumn<QString>( "crs" );
5173 QTest::addColumn<double>( "pixelSize" );
5174 QTest::addColumn<int>( "typeId" );
5175
5176 QTest::newRow( "testcase 1" )
5177 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5178 << Qgis::DataType::Int16
5179 << true
5180 << "EPSG:4326"
5181 << 1.0
5182 << 0;
5183
5184 QTest::newRow( "testcase 2" )
5185 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5186 << Qgis::DataType::UInt16
5187 << true
5188 << "EPSG:4326"
5189 << 1.0
5190 << 1;
5191
5192
5193 QTest::newRow( "testcase 3" )
5194 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5195 << Qgis::DataType::Int32
5196 << true
5197 << "EPSG:4326"
5198 << 1.0
5199 << 2;
5200
5201 QTest::newRow( "testcase 4" )
5202 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5203 << Qgis::DataType::UInt32
5204 << true
5205 << "EPSG:4326"
5206 << 1.0
5207 << 3;
5208
5209 QTest::newRow( "testcase 5" )
5210 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5211 << Qgis::DataType::Float32
5212 << true
5213 << "EPSG:4326"
5214 << 1.0
5215 << 4;
5216
5217 QTest::newRow( "testcase 6" )
5218 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5219 << Qgis::DataType::Float64
5220 << true
5221 << "EPSG:4326"
5222 << 1.0
5223 << 5;
5224 }
5225
5226
randomIntegerDistributionRaster()5227 void TestQgsProcessingAlgs::randomIntegerDistributionRaster()
5228 {
5229 //this test only checks if the correct raster data type is chosen
5230 //value by value comparison is not effective for random rasters
5231 QFETCH( QString, inputExtent );
5232 QFETCH( Qgis::DataType, expectedDataType );
5233 QFETCH( bool, succeeds );
5234 QFETCH( QString, crs );
5235 QFETCH( double, pixelSize );
5236 QFETCH( int, typeId );
5237
5238 //prepare input params
5239 QgsProject p;
5240 //set project crs and ellipsoid from input layer
5241 p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
5242
5243 QStringList alglist = QStringList();
5244 alglist << QStringLiteral( "native:createrandombinomialrasterlayer" )
5245 << QStringLiteral( "native:createrandomgeometricrasterlayer" )
5246 << QStringLiteral( "native:createrandomnegativebinomialrasterlayer" )
5247 << QStringLiteral( "native:createrandompoissonrasterlayer" );
5248
5249 for ( int i = 0; i < alglist.length(); i++ )
5250 {
5251 const QString algname = alglist[i];
5252
5253 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algname ) );
5254 //set project after layer has been added so that transform context/ellipsoid from crs is also set
5255 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5256 context->setProject( &p );
5257
5258 QVariantMap parameters;
5259
5260 parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
5261 parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
5262 parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
5263 parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
5264 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
5265
5266 bool ok = false;
5267 QgsProcessingFeedback feedback;
5268 QVariantMap results;
5269
5270 if ( !succeeds )
5271 {
5272 //verify if user feedback for unacceptable values are thrown
5273 alg->run( parameters, *context, &feedback, &ok );
5274 QCOMPARE( ok, succeeds );
5275 }
5276 else
5277 {
5278 //run alg...
5279 results = alg->run( parameters, *context, &feedback, &ok );
5280 QVERIFY( ok );
5281
5282 //...and check results with expected datasets
5283 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
5284 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
5285
5286 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
5287 }
5288 }
5289 }
5290
randomRaster_data()5291 void TestQgsProcessingAlgs::randomRaster_data()
5292 {
5293 QTest::addColumn<QString>( "inputExtent" );
5294 QTest::addColumn<Qgis::DataType>( "expectedDataType" );
5295 QTest::addColumn<bool>( "succeeds" );
5296 QTest::addColumn<QString>( "crs" );
5297 QTest::addColumn<double>( "pixelSize" );
5298 QTest::addColumn<double>( "lowerBound" );
5299 QTest::addColumn<double>( "upperBound" );
5300 QTest::addColumn<int>( "typeId" );
5301
5302
5303 QTest::newRow( "testcase 1" )
5304 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5305 << Qgis::DataType::Byte
5306 << true
5307 << "EPSG:4326"
5308 << 1.0
5309 << 1.0
5310 << 1.0 //should be min max
5311 << 0;
5312
5313 QTest::newRow( "testcase 2" )
5314 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5315 << Qgis::DataType::Byte
5316 << false
5317 << "EPSG:4326"
5318 << 1.0
5319 << -1.0 //fails --> value too small for byte
5320 << 10.0 //fails --> value too large
5321 << 0;
5322
5323 QTest::newRow( "testcase 3" )
5324 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5325 << Qgis::DataType::Byte
5326 << false
5327 << "EPSG:4326"
5328 << 1.0
5329 << 1.0
5330 << 256.0 //fails --> value too big for byte
5331 << 0;
5332
5333 QTest::newRow( "testcase 4" )
5334 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5335 << Qgis::DataType::Int16
5336 << true
5337 << "EPSG:4326"
5338 << 1.0
5339 << 1.0
5340 << 10.0
5341 << 1;
5342
5343 QTest::newRow( "testcase 5" )
5344 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5345 << Qgis::DataType::Int16
5346 << false
5347 << "EPSG:4326"
5348 << 1.0
5349 << -32769.0
5350 << -10.0
5351 << 1;
5352
5353 QTest::newRow( "testcase 6" )
5354 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5355 << Qgis::DataType::Int16
5356 << false
5357 << "EPSG:4326"
5358 << 1.0
5359 << 1.0
5360 << 32769.0
5361 << 1;
5362
5363 QTest::newRow( "testcase 7" )
5364 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5365 << Qgis::DataType::UInt16
5366 << false
5367 << "EPSG:4326"
5368 << 1.0
5369 << -1.0
5370 << 12.0
5371 << 2;
5372
5373 QTest::newRow( "testcase 8" )
5374 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5375 << Qgis::DataType::UInt16
5376 << false
5377 << "EPSG:4326"
5378 << 1.0
5379 << 100.0
5380 << -1.0
5381 << 2;
5382
5383 QTest::newRow( "testcase 9" )
5384 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5385 << Qgis::DataType::UInt16
5386 << false
5387 << "EPSG:4326"
5388 << 1.0
5389 << 0.0
5390 << 65536.0
5391 << 2;
5392
5393 QTest::newRow( "testcase 10" )
5394 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5395 << Qgis::DataType::Int32
5396 << true
5397 << "EPSG:4326"
5398 << 1.0
5399 << 1.0
5400 << 12.0
5401 << 3;
5402
5403 QTest::newRow( "testcase 10" )
5404 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5405 << Qgis::DataType::Int32
5406 << false
5407 << "EPSG:4326"
5408 << 1.0
5409 << 15.0
5410 << 12.0
5411 << 3;
5412
5413 QTest::newRow( "testcase 11" )
5414 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5415 << Qgis::DataType::Int32
5416 << false
5417 << "EPSG:4326"
5418 << 1.0
5419 << -2147483649.0
5420 << 1.0
5421 << 3;
5422
5423 QTest::newRow( "testcase 12" )
5424 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5425 << Qgis::DataType::Int32
5426 << false
5427 << "EPSG:4326"
5428 << 1.0
5429 << 1.0
5430 << 2147483649.0
5431 << 3;
5432
5433 QTest::newRow( "testcase 13" )
5434 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5435 << Qgis::DataType::UInt32
5436 << true
5437 << "EPSG:4326"
5438 << 1.0
5439 << 1.0
5440 << 12.0
5441 << 4;
5442
5443 QTest::newRow( "testcase 14" )
5444 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5445 << Qgis::DataType::UInt32
5446 << false
5447 << "EPSG:4326"
5448 << 1.0
5449 << 1.0
5450 << 4294967296.0
5451 << 4;
5452
5453 QTest::newRow( "testcase 14" )
5454 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5455 << Qgis::DataType::UInt32
5456 << false
5457 << "EPSG:4326"
5458 << 1.0
5459 << -10.0
5460 << -1.0
5461 << 4;
5462
5463 QTest::newRow( "testcase 16" )
5464 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5465 << Qgis::DataType::Float32
5466 << true
5467 << "EPSG:4326"
5468 << 1.0
5469 << 0.0
5470 << 12.12
5471 << 5;
5472
5473 QTest::newRow( "testcase 17" )
5474 << "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
5475 << Qgis::DataType::Float64
5476 << true
5477 << "EPSG:4326"
5478 << 1.0
5479 << -15.0
5480 << 12.125789212532487
5481 << 6;
5482
5483
5484 }
5485
randomRaster()5486 void TestQgsProcessingAlgs::randomRaster()
5487 {
5488 QFETCH( QString, inputExtent );
5489 QFETCH( Qgis::DataType, expectedDataType );
5490 QFETCH( bool, succeeds );
5491 QFETCH( QString, crs );
5492 QFETCH( double, pixelSize );
5493 QFETCH( double, lowerBound );
5494 QFETCH( double, upperBound );
5495 QFETCH( int, typeId );
5496
5497 if ( qgsDoubleNear( lowerBound, upperBound ) )
5498 {
5499 //if bounds are the same, use numeric min and max as bounds
5500 switch ( typeId )
5501 {
5502 case 0:
5503 lowerBound = std::numeric_limits<quint8>::min();
5504 upperBound = std::numeric_limits<quint8>::max();
5505 break;
5506 case 1:
5507 lowerBound = std::numeric_limits<qint16>::min();
5508 upperBound = std::numeric_limits<qint16>::max();
5509 break;
5510 case 2:
5511 lowerBound = std::numeric_limits<quint16>::min();
5512 upperBound = std::numeric_limits<quint16>::max();
5513 break;
5514 case 3:
5515 lowerBound = std::numeric_limits<qint32>::min();
5516 upperBound = std::numeric_limits<qint32>::max();
5517 break;
5518 case 4:
5519 lowerBound = std::numeric_limits<quint32>::min();
5520 upperBound = std::numeric_limits<quint32>::max();
5521 break;
5522 case 5:
5523 lowerBound = std::numeric_limits<float>::min();
5524 upperBound = std::numeric_limits<float>::max();
5525 break;
5526 case 6:
5527 lowerBound = std::numeric_limits<double>::min();
5528 upperBound = std::numeric_limits<double>::max();
5529 break;
5530 }
5531 }
5532
5533 //prepare input params
5534 QgsProject p;
5535 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createrandomuniformrasterlayer" ) ) );
5536
5537 const QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
5538
5539 //set project crs and ellipsoid from input layer
5540 p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
5541
5542 //set project after layer has been added so that transform context/ellipsoid from crs is also set
5543 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5544 context->setProject( &p );
5545
5546 QVariantMap parameters;
5547
5548 parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
5549 parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
5550 parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
5551 parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
5552 parameters.insert( QStringLiteral( "LOWER_BOUND" ), lowerBound );
5553 parameters.insert( QStringLiteral( "UPPER_BOUND" ), upperBound );
5554 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
5555
5556 bool ok = false;
5557 QgsProcessingFeedback feedback;
5558 QVariantMap results;
5559
5560 if ( !succeeds )
5561 {
5562 //verify if user feedback for unacceptable values are thrown
5563 alg->run( parameters, *context, &feedback, &ok );
5564 QCOMPARE( ok, succeeds );
5565 }
5566 else
5567 {
5568 //run alg...
5569 results = alg->run( parameters, *context, &feedback, &ok );
5570 QVERIFY( ok );
5571
5572 //...and check results with expected datasets
5573 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
5574 std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
5575
5576 QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
5577
5578 QgsRasterIterator outputIter( outputInterface.get() );
5579 outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
5580 int outputIterLeft = 0;
5581 int outputIterTop = 0;
5582 int outputIterCols = 0;
5583 int outputIterRows = 0;
5584
5585 std::unique_ptr< QgsRasterBlock > outputRasterBlock;
5586
5587 while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) )
5588 {
5589 for ( int row = 0; row < outputIterRows; row++ )
5590 {
5591 for ( int column = 0; column < outputIterCols; column++ )
5592 {
5593 const double outputValue = outputRasterBlock->value( row, column );
5594 //check if random values are in range
5595 QVERIFY( outputValue >= lowerBound && outputValue <= upperBound );
5596 }
5597 }
5598 }
5599 }
5600 }
5601
filterByLayerType()5602 void TestQgsProcessingAlgs::filterByLayerType()
5603 {
5604 QgsProject p;
5605 QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
5606 QVERIFY( vl->isValid() );
5607 p.addMapLayer( vl );
5608 // raster layer
5609 QgsRasterLayer *rl = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/tenbytenraster.asc", QStringLiteral( "rl" ) );
5610 QVERIFY( rl->isValid() );
5611 p.addMapLayer( rl );
5612
5613
5614 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:filterlayersbytype" ) ) );
5615 QVERIFY( alg != nullptr );
5616
5617 QVariantMap parameters;
5618 // vector input
5619 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "vl" ) );
5620
5621 bool ok = false;
5622 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5623 context->setProject( &p );
5624 QgsProcessingFeedback feedback;
5625 QVariantMap results;
5626 results = alg->run( parameters, *context, &feedback, &ok );
5627 QVERIFY( ok );
5628 QVERIFY( !results.value( QStringLiteral( "VECTOR" ) ).toString().isEmpty() );
5629 QVERIFY( !results.contains( QStringLiteral( "RASTER" ) ) );
5630
5631 // raster input
5632 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "rl" ) );
5633 ok = false;
5634 results = alg->run( parameters, *context, &feedback, &ok );
5635 QVERIFY( ok );
5636 QVERIFY( !results.value( QStringLiteral( "RASTER" ) ).toString().isEmpty() );
5637 QVERIFY( !results.contains( QStringLiteral( "VECTOR" ) ) );
5638 }
5639
conditionalBranch()5640 void TestQgsProcessingAlgs::conditionalBranch()
5641 {
5642 QVariantMap config;
5643 QVariantList conditions;
5644 QVariantMap cond1;
5645 cond1.insert( QStringLiteral( "name" ), QStringLiteral( "name1" ) );
5646 cond1.insert( QStringLiteral( "expression" ), QStringLiteral( "1 * 1" ) );
5647 conditions << cond1;
5648 QVariantMap cond2;
5649 cond2.insert( QStringLiteral( "name" ), QStringLiteral( "name2" ) );
5650 cond2.insert( QStringLiteral( "expression" ), QStringLiteral( "1 * 0" ) );
5651 conditions << cond2;
5652 config.insert( QStringLiteral( "conditions" ), conditions );
5653
5654 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:condition" ), config ) );
5655 QVERIFY( alg != nullptr );
5656
5657 QCOMPARE( alg->outputDefinitions().size(), 2 );
5658 QCOMPARE( alg->outputDefinitions().at( 0 )->name(), QStringLiteral( "name1" ) );
5659 QCOMPARE( alg->outputDefinitions().at( 1 )->name(), QStringLiteral( "name2" ) );
5660
5661 QVariantMap parameters;
5662 // vector input
5663 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "vl" ) );
5664
5665 bool ok = false;
5666 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5667 QgsProcessingFeedback feedback;
5668 QVariantMap results;
5669 results = alg->run( parameters, *context, &feedback, &ok, config );
5670 QVERIFY( ok );
5671 QCOMPARE( results.value( QStringLiteral( "name1" ) ).toInt(), 1 );
5672 QCOMPARE( results.value( QStringLiteral( "name2" ) ).toInt(), 0 );
5673 }
5674
saveLog()5675 void TestQgsProcessingAlgs::saveLog()
5676 {
5677 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:savelog" ) ) );
5678 QVERIFY( alg != nullptr );
5679
5680 QVariantMap parameters;
5681 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
5682
5683 bool ok = false;
5684 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5685 QgsProcessingFeedback feedback;
5686 feedback.reportError( QStringLiteral( "test" ) );
5687 QVariantMap results;
5688 results = alg->run( parameters, *context, &feedback, &ok );
5689 QVERIFY( ok );
5690
5691 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
5692 QFile file( results.value( QStringLiteral( "OUTPUT" ) ).toString() );
5693 QVERIFY( file.open( QFile::ReadOnly | QIODevice::Text ) );
5694 QCOMPARE( QString( file.readAll() ), QStringLiteral( "test\n" ) );
5695
5696 parameters.insert( QStringLiteral( "USE_HTML" ), true );
5697 results = alg->run( parameters, *context, &feedback, &ok );
5698 QVERIFY( ok );
5699 QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
5700 QFile file2( results.value( QStringLiteral( "OUTPUT" ) ).toString() );
5701 QVERIFY( file2.open( QFile::ReadOnly | QIODevice::Text ) );
5702 QCOMPARE( QString( file2.readAll() ), QStringLiteral( "<span style=\"color:red\">test</span><br/>" ) );
5703 }
5704
setProjectVariable()5705 void TestQgsProcessingAlgs::setProjectVariable()
5706 {
5707 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:setprojectvariable" ) ) );
5708 QVERIFY( alg != nullptr );
5709
5710 QVariantMap parameters;
5711 parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "my_var" ) );
5712 parameters.insert( QStringLiteral( "VALUE" ), 11 );
5713
5714 bool ok = false;
5715 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5716 QgsProject p;
5717 context->setProject( &p );
5718 QgsProcessingFeedback feedback;
5719 QVariantMap results;
5720 results = alg->run( parameters, *context, &feedback, &ok );
5721 QVERIFY( ok );
5722
5723 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::projectScope( &p ) );
5724 QCOMPARE( scope->variable( QStringLiteral( "my_var" ) ).toInt(), 11 );
5725
5726 //overwrite existing
5727 parameters.insert( QStringLiteral( "VALUE" ), 13 );
5728 results = alg->run( parameters, *context, &feedback, &ok );
5729 QVERIFY( ok );
5730 scope.reset( QgsExpressionContextUtils::projectScope( &p ) );
5731 QCOMPARE( scope->variable( QStringLiteral( "my_var" ) ).toInt(), 13 );
5732 }
5733
5734 #ifndef QT_NO_PRINTER
exportLayoutPdf()5735 void TestQgsProcessingAlgs::exportLayoutPdf()
5736 {
5737 QgsProject p;
5738 QgsPrintLayout *layout = new QgsPrintLayout( &p );
5739 layout->initializeDefaults();
5740 layout->setName( QStringLiteral( "my layout" ) );
5741 p.layoutManager()->addLayout( layout );
5742
5743 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:printlayouttopdf" ) ) );
5744 QVERIFY( alg != nullptr );
5745
5746 const QString outputPdf = QDir::tempPath() + "/my_layout.pdf";
5747 if ( QFile::exists( outputPdf ) )
5748 QFile::remove( outputPdf );
5749
5750 QVariantMap parameters;
5751 parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "missing" ) );
5752 parameters.insert( QStringLiteral( "OUTPUT" ), outputPdf );
5753
5754 bool ok = false;
5755 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5756 context->setProject( &p );
5757 QgsProcessingFeedback feedback;
5758 QVariantMap results;
5759 results = alg->run( parameters, *context, &feedback, &ok );
5760 // invalid layout name
5761 QVERIFY( !ok );
5762 QVERIFY( !QFile::exists( outputPdf ) );
5763
5764 parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
5765 results = alg->run( parameters, *context, &feedback, &ok );
5766 QVERIFY( ok );
5767
5768 QVERIFY( QFile::exists( outputPdf ) );
5769 }
5770
exportLayoutPng()5771 void TestQgsProcessingAlgs::exportLayoutPng()
5772 {
5773 QgsProject p;
5774 QgsPrintLayout *layout = new QgsPrintLayout( &p );
5775 layout->initializeDefaults();
5776 layout->setName( QStringLiteral( "my layout" ) );
5777
5778 QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
5779 map->setBackgroundEnabled( false );
5780 map->setFrameEnabled( false );
5781 map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
5782 layout->addLayoutItem( map );
5783 map->setExtent( mPointsLayer->extent() );
5784
5785 p.layoutManager()->addLayout( layout );
5786
5787 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:printlayouttoimage" ) ) );
5788 QVERIFY( alg != nullptr );
5789
5790 QString outputPng = QDir::tempPath() + "/my_layout.png";
5791 if ( QFile::exists( outputPng ) )
5792 QFile::remove( outputPng );
5793
5794 QVariantMap parameters;
5795 parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "missing" ) );
5796 parameters.insert( QStringLiteral( "OUTPUT" ), outputPng );
5797
5798 bool ok = false;
5799 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5800 context->setProject( &p );
5801 QgsProcessingFeedback feedback;
5802 QVariantMap results;
5803 results = alg->run( parameters, *context, &feedback, &ok );
5804 // invalid layout name
5805 QVERIFY( !ok );
5806 QVERIFY( !QFile::exists( outputPng ) );
5807
5808 parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
5809 results = alg->run( parameters, *context, &feedback, &ok );
5810 QVERIFY( ok );
5811 QVERIFY( QFile::exists( outputPng ) );
5812
5813 outputPng = QDir::tempPath() + "/my_layout_custom_layers.png";
5814 if ( QFile::exists( outputPng ) )
5815 QFile::remove( outputPng );
5816
5817 parameters.insert( QStringLiteral( "OUTPUT" ), outputPng );
5818 parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( mPointsLayer ) );
5819 parameters.insert( QStringLiteral( "DPI" ), 96 );
5820 results = alg->run( parameters, *context, &feedback, &ok );
5821 QVERIFY( ok );
5822 QVERIFY( QFile::exists( outputPng ) );
5823 QVERIFY( imageCheck( "export_layout_custom_layers", outputPng ) );
5824 }
5825
exportAtlasLayoutPdf()5826 void TestQgsProcessingAlgs::exportAtlasLayoutPdf()
5827 {
5828 QgsMapLayer *polygonLayer = mPolygonLayer->clone();
5829 QgsProject p;
5830 p.addMapLayers( QList<QgsMapLayer *>() << polygonLayer );
5831
5832 QgsPrintLayout *layout = new QgsPrintLayout( &p );
5833 layout->initializeDefaults();
5834 layout->setName( QStringLiteral( "my layout" ) );
5835
5836 QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
5837 map->setBackgroundEnabled( false );
5838 map->setFrameEnabled( false );
5839 map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
5840 layout->addLayoutItem( map );
5841 map->setExtent( polygonLayer->extent() );
5842
5843 p.layoutManager()->addLayout( layout );
5844
5845 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:atlaslayouttopdf" ) ) );
5846 QVERIFY( alg != nullptr );
5847
5848 const QString outputPdf = QDir::tempPath() + "/my_atlas_layout.pdf";
5849 if ( QFile::exists( outputPdf ) )
5850 QFile::remove( outputPdf );
5851
5852 QVariantMap parameters;
5853 parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
5854 parameters.insert( QStringLiteral( "COVERAGE_LAYER" ), QVariant::fromValue( polygonLayer ) );
5855 parameters.insert( QStringLiteral( "OUTPUT" ), outputPdf );
5856 parameters.insert( QStringLiteral( "DPI" ), 96 );
5857
5858 bool ok = false;
5859 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5860 context->setProject( &p );
5861 QgsProcessingFeedback feedback;
5862 QVariantMap results;
5863 results = alg->run( parameters, *context, &feedback, &ok );
5864 QVERIFY( ok );
5865
5866 QVERIFY( QFile::exists( outputPdf ) );
5867 }
5868
exportAtlasLayoutPng()5869 void TestQgsProcessingAlgs::exportAtlasLayoutPng()
5870 {
5871 QgsMapLayer *polygonLayer = mPolygonLayer->clone();
5872 QgsProject p;
5873 p.addMapLayers( QList<QgsMapLayer *>() << polygonLayer );
5874
5875 QgsPrintLayout *layout = new QgsPrintLayout( &p );
5876 layout->initializeDefaults();
5877 layout->setName( QStringLiteral( "my layout" ) );
5878
5879 QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
5880 map->setBackgroundEnabled( false );
5881 map->setFrameEnabled( false );
5882 map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
5883 layout->addLayoutItem( map );
5884 map->setExtent( polygonLayer->extent() );
5885
5886 p.layoutManager()->addLayout( layout );
5887
5888 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:atlaslayouttoimage" ) ) );
5889 QVERIFY( alg != nullptr );
5890
5891 const QDir tempDir( QDir::tempPath() );
5892 if ( !tempDir.mkdir( "my_atlas" ) )
5893 {
5894 const QDir dir( QDir::tempPath() + "/my_atlas" );
5895 const QStringList files = dir.entryList( QStringList() << "*.*", QDir::Files );
5896 for ( const QString &file : files )
5897 QFile::remove( QDir::tempPath() + "/my_atlas/" + file );
5898 }
5899
5900 QVariantMap parameters;
5901 parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
5902 parameters.insert( QStringLiteral( "COVERAGE_LAYER" ), QVariant::fromValue( polygonLayer ) );
5903 parameters.insert( QStringLiteral( "FOLDER" ), QString( QDir::tempPath() + "/my_atlas" ) );
5904 parameters.insert( QStringLiteral( "FILENAME_EXPRESSION" ), QStringLiteral( "'export_'||@atlas_featurenumber" ) );
5905 parameters.insert( QStringLiteral( "DPI" ), 96 );
5906
5907 bool ok = false;
5908 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5909 context->setProject( &p );
5910 QgsProcessingFeedback feedback;
5911 QVariantMap results;
5912 results = alg->run( parameters, *context, &feedback, &ok );
5913 QVERIFY( ok );
5914
5915 QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/export_1.png" ) );
5916 QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/export_10.png" ) );
5917 QVERIFY( imageCheck( "export_atlas", QDir::tempPath() + "/my_atlas/export_1.png" ) );
5918
5919 parameters[QStringLiteral( "FILENAME_EXPRESSION" )] = QStringLiteral( "'custom_'||@atlas_featurenumber" );
5920 parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( mPointsLayer ) );
5921
5922 ok = false;
5923 results.clear();
5924 results = alg->run( parameters, *context, &feedback, &ok );
5925 QVERIFY( ok );
5926
5927 QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/custom_1.png" ) );
5928 QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/custom_10.png" ) );
5929 QVERIFY( imageCheck( "export_atlas_custom_layers", QDir::tempPath() + "/my_atlas/custom_1.png" ) );
5930 }
5931 #endif
5932
tinMeshCreation()5933 void TestQgsProcessingAlgs::tinMeshCreation()
5934 {
5935 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:tinmeshcreation" ) ) );
5936 QVERIFY( alg != nullptr );
5937
5938 QVariantList inputLayers;
5939
5940 QVariantMap pointLayer;
5941 pointLayer[QStringLiteral( "source" )] = "points";
5942 pointLayer[QStringLiteral( "type" )] = 0;
5943 pointLayer[QStringLiteral( "attributeIndex" )] = mPointsLayer->fields().indexOf( "Importance" );
5944
5945 inputLayers.append( pointLayer );
5946
5947 QVariantMap polyLayer;
5948 polyLayer[QStringLiteral( "source" )] = "polygons";
5949 polyLayer[QStringLiteral( "type" )] = 2;
5950 polyLayer[QStringLiteral( "attributeIndex" )] = mPolygonLayer->fields().indexOf( "Value" );
5951
5952 inputLayers.append( polyLayer );
5953
5954 QVariantMap parameters;
5955 parameters.insert( QStringLiteral( "SOURCE_DATA" ), inputLayers );
5956 parameters.insert( QStringLiteral( "OUTPUT_MESH" ), QString( QDir::tempPath() + "/meshLayer.2dm" ) );
5957 parameters.insert( QStringLiteral( "MESH_FORMAT" ), 0 );
5958
5959 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
5960 context->setProject( QgsProject::instance() );
5961 QgsProcessingFeedback feedback;
5962 QVariantMap results;
5963 bool ok = false;
5964 results = alg->run( parameters, *context, &feedback, &ok );
5965 QVERIFY( ok );
5966
5967 QgsMeshLayer meshLayer( QDir::tempPath() + "/meshLayer.2dm", "mesh", "mdal" );
5968 QVERIFY( meshLayer.isValid() );
5969
5970 QgsMeshDataProvider *provider = meshLayer.dataProvider();
5971 QCOMPARE( provider->vertexCount(), 627 );
5972 QCOMPARE( provider->faceCount(), 1218 );
5973
5974 meshLayer.updateTriangularMesh();
5975 QVERIFY( qgsDoubleNear( meshLayer.datasetValue( QgsMeshDatasetIndex( 0, 0 ), QgsPointXY( -103.0, 39.0 ) ).scalar(), 20.0, 0.001 ) );
5976 QVERIFY( qgsDoubleNear( meshLayer.datasetValue( QgsMeshDatasetIndex( 0, 0 ), QgsPointXY( -86.0, 35.0 ) ).scalar(), 1.855, 0.001 ) ) ;
5977 }
5978
exportMeshVertices()5979 void TestQgsProcessingAlgs::exportMeshVertices()
5980 {
5981 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshvertices" ) ) );
5982 QVERIFY( alg != nullptr );
5983
5984 QVariantMap parameters;
5985 parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
5986
5987 QVariantList datasetGroup;
5988 datasetGroup << 1 << 2;
5989 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
5990
5991 QVariantMap datasetTime;
5992 datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
5993 QVariantList datasetIndex;
5994 datasetIndex << 1 << 1;
5995 datasetTime[QStringLiteral( "value" )] = datasetIndex;
5996 parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
5997
5998 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
5999 parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
6000
6001 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6002 context->setProject( QgsProject::instance() );
6003 QgsProcessingFeedback feedback;
6004 QVariantMap results;
6005 bool ok = false;
6006 results = alg->run( parameters, *context, &feedback, &ok );
6007 QVERIFY( ok );
6008
6009 QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
6010 QVERIFY( resultLayer );
6011 QVERIFY( resultLayer->isValid() );
6012 QVERIFY( resultLayer->geometryType() == QgsWkbTypes::PointGeometry );
6013 QCOMPARE( resultLayer->featureCount(), 5l );
6014 const QgsAttributeList attributeList = resultLayer->attributeList();
6015 QCOMPARE( resultLayer->fields().count(), 5 );
6016 QCOMPARE( resultLayer->fields().at( 0 ).name(), QStringLiteral( "VertexScalarDataset" ) );
6017 QCOMPARE( resultLayer->fields().at( 1 ).name(), QStringLiteral( "VertexVectorDataset_x" ) );
6018 QCOMPARE( resultLayer->fields().at( 2 ).name(), QStringLiteral( "VertexVectorDataset_y" ) );
6019 QCOMPARE( resultLayer->fields().at( 3 ).name(), QStringLiteral( "VertexVectorDataset_mag" ) );
6020 QCOMPARE( resultLayer->fields().at( 4 ).name(), QStringLiteral( "VertexVectorDataset_dir" ) );
6021
6022 QgsFeatureIterator featIt = resultLayer->getFeatures();
6023 QgsFeature feat;
6024 featIt.nextFeature( feat );
6025 QCOMPARE( QStringLiteral( "PointZ (1000 2000 20)" ), feat.geometry().asWkt() );
6026 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
6027 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
6028 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
6029 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.828, 2 ) );
6030 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
6031
6032 featIt.nextFeature( feat );
6033 QCOMPARE( QStringLiteral( "PointZ (2000 2000 30)" ), feat.geometry().asWkt() );
6034 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
6035 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
6036 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
6037 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 3.605, 2 ) );
6038 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 56.3099, 2 ) );
6039 featIt.nextFeature( feat );
6040 QCOMPARE( QStringLiteral( "PointZ (3000 2000 40)" ), feat.geometry().asWkt() );
6041 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 4.0 );
6042 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 4.0 );
6043 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
6044 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 5.0, 2 ) );
6045 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 53.130, 2 ) );
6046 featIt.nextFeature( feat );
6047 QCOMPARE( QStringLiteral( "PointZ (2000 3000 50)" ), feat.geometry().asWkt() );
6048 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
6049 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
6050 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
6051 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 4.242, 2 ) );
6052 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45, 2 ) );
6053 featIt.nextFeature( feat );
6054 QCOMPARE( QStringLiteral( "PointZ (1000 3000 10)" ), feat.geometry().asWkt() );
6055 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
6056 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
6057 QCOMPARE( feat.attributes().at( 2 ).toDouble(), -1.0 );
6058 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.236, 2 ) );
6059 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 116.565, 2 ) );
6060 }
6061
exportMeshFaces()6062 void TestQgsProcessingAlgs::exportMeshFaces()
6063 {
6064 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshfaces" ) ) );
6065 QVERIFY( alg != nullptr );
6066
6067 QVariantMap parameters;
6068 parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
6069
6070 QVariantList datasetGroup;
6071 datasetGroup << 3 << 4;
6072 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
6073
6074 QVariantMap datasetTime;
6075 datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6076 QVariantList datasetIndex;
6077 datasetIndex << 1 << 1;
6078 datasetTime[QStringLiteral( "value" )] = datasetIndex;
6079 parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
6080
6081 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
6082 parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
6083
6084 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6085 context->setProject( QgsProject::instance() );
6086 QgsProcessingFeedback feedback;
6087 QVariantMap results;
6088 bool ok = false;
6089 results = alg->run( parameters, *context, &feedback, &ok );
6090 QVERIFY( ok );
6091
6092 QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
6093 QVERIFY( resultLayer );
6094 QVERIFY( resultLayer->isValid() );
6095 QVERIFY( resultLayer->geometryType() == QgsWkbTypes::PolygonGeometry );
6096 QCOMPARE( resultLayer->featureCount(), 2l );
6097 const QgsAttributeList attributeList = resultLayer->attributeList();
6098 QCOMPARE( resultLayer->fields().count(), 5 );
6099 QCOMPARE( resultLayer->fields().at( 0 ).name(), QStringLiteral( "FaceScalarDataset" ) );
6100 QCOMPARE( resultLayer->fields().at( 1 ).name(), QStringLiteral( "FaceVectorDataset_x" ) );
6101 QCOMPARE( resultLayer->fields().at( 2 ).name(), QStringLiteral( "FaceVectorDataset_y" ) );
6102 QCOMPARE( resultLayer->fields().at( 3 ).name(), QStringLiteral( "FaceVectorDataset_mag" ) );
6103 QCOMPARE( resultLayer->fields().at( 4 ).name(), QStringLiteral( "FaceVectorDataset_dir" ) );
6104
6105 QgsFeatureIterator featIt = resultLayer->getFeatures();
6106 QgsFeature feat;
6107 featIt.nextFeature( feat );
6108 QCOMPARE( QStringLiteral( "PolygonZ ((1000 2000 20, 2000 2000 30, 2000 3000 50, 1000 3000 10, 1000 2000 20))" ), feat.geometry().asWkt() );
6109 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
6110 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
6111 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
6112 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.828, 2 ) );
6113 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
6114
6115 featIt.nextFeature( feat );
6116 QCOMPARE( QStringLiteral( "PolygonZ ((2000 2000 30, 3000 2000 40, 2000 3000 50, 2000 2000 30))" ), feat.geometry().asWkt() );
6117 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
6118 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
6119 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
6120 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 4.242, 2 ) );
6121 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
6122 }
6123
exportMeshEdges()6124 void TestQgsProcessingAlgs::exportMeshEdges()
6125 {
6126 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshedges" ) ) );
6127 QVERIFY( alg != nullptr );
6128
6129 QVariantMap parameters;
6130 parameters.insert( QStringLiteral( "INPUT" ), "mesh layer 1D" );
6131
6132 QVariantList datasetGroup;
6133 datasetGroup << 1 << 2;
6134 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
6135
6136 QVariantMap datasetTime;
6137 datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6138 QVariantList datasetIndex;
6139 datasetIndex << 1 << 1;
6140 datasetTime[QStringLiteral( "value" )] = datasetIndex;
6141 parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
6142
6143 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
6144 parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
6145
6146 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6147 context->setProject( QgsProject::instance() );
6148 QgsProcessingFeedback feedback;
6149 QVariantMap results;
6150 bool ok = false;
6151 results = alg->run( parameters, *context, &feedback, &ok );
6152 QVERIFY( ok );
6153
6154 QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
6155 QVERIFY( resultLayer );
6156 QVERIFY( resultLayer->isValid() );
6157 QVERIFY( resultLayer->geometryType() == QgsWkbTypes::LineGeometry );
6158 QCOMPARE( resultLayer->featureCount(), 3l );
6159 const QgsAttributeList attributeList = resultLayer->attributeList();
6160 QCOMPARE( resultLayer->fields().count(), 5 );
6161 QCOMPARE( resultLayer->fields().at( 0 ).name(), QStringLiteral( "EdgeScalarDataset" ) );
6162 QCOMPARE( resultLayer->fields().at( 1 ).name(), QStringLiteral( "EdgeVectorDataset_x" ) );
6163 QCOMPARE( resultLayer->fields().at( 2 ).name(), QStringLiteral( "EdgeVectorDataset_y" ) );
6164 QCOMPARE( resultLayer->fields().at( 3 ).name(), QStringLiteral( "EdgeVectorDataset_mag" ) );
6165 QCOMPARE( resultLayer->fields().at( 4 ).name(), QStringLiteral( "EdgeVectorDataset_dir" ) );
6166
6167 QgsFeatureIterator featIt = resultLayer->getFeatures();
6168 QgsFeature feat;
6169 featIt.nextFeature( feat );
6170 QCOMPARE( QStringLiteral( "LineStringZ (1000 2000 20, 2000 2000 30)" ), feat.geometry().asWkt() );
6171 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
6172 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
6173 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
6174 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.828, 2 ) );
6175 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
6176
6177 featIt.nextFeature( feat );
6178 QCOMPARE( QStringLiteral( "LineStringZ (2000 2000 30, 3000 2000 40)" ), feat.geometry().asWkt() );
6179 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
6180 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
6181 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
6182 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 4.242, 2 ) );
6183 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
6184
6185 featIt.nextFeature( feat );
6186 QCOMPARE( QStringLiteral( "LineStringZ (3000 2000 40, 2000 3000 50)" ), feat.geometry().asWkt() );
6187 QCOMPARE( feat.attributes().at( 0 ).toDouble(), 4.0 );
6188 QCOMPARE( feat.attributes().at( 1 ).toDouble(), 4.0 );
6189 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 4.0 );
6190 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 5.656, 2 ) );
6191 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
6192 }
6193
exportMeshOnGrid()6194 void TestQgsProcessingAlgs::exportMeshOnGrid()
6195 {
6196 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshongrid" ) ) );
6197 QVERIFY( alg != nullptr );
6198
6199 const QString dataDir = QString( TEST_DATA_DIR ); //defined in CmakeLists.txt
6200 const QString meshUri( dataDir + "/mesh/trap_steady_05_3D.nc" );
6201
6202 QVariantMap parameters;
6203 parameters.insert( QStringLiteral( "INPUT" ), meshUri );
6204
6205 QVariantList datasetGroup;
6206 for ( int i = 0; i < 12; ++i )
6207 datasetGroup.append( i );
6208 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
6209
6210 QVariantMap datasetTime;
6211 datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6212 QVariantList datasetIndex;
6213 datasetIndex << 1 << 1;
6214 datasetTime[QStringLiteral( "value" )] = datasetIndex;
6215 parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
6216
6217 parameters.insert( QStringLiteral( "GRID_SPACING" ), 25.0 );
6218
6219 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
6220 parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
6221
6222 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6223 context->setProject( QgsProject::instance() );
6224 QgsProcessingFeedback feedback;
6225 QVariantMap results;
6226 bool ok = false;
6227 results = alg->run( parameters, *context, &feedback, &ok );
6228 QVERIFY( ok );
6229
6230 QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
6231 QVERIFY( resultLayer );
6232 QVERIFY( resultLayer->isValid() );
6233 QVERIFY( resultLayer->geometryType() == QgsWkbTypes::PointGeometry );
6234 QCOMPARE( resultLayer->featureCount(), 205l );
6235 const QgsAttributeList attributeList = resultLayer->attributeList();
6236 QCOMPARE( resultLayer->fields().count(), 21 );
6237 QStringList fieldsName;
6238 fieldsName << QStringLiteral( "Bed Elevation" ) << QStringLiteral( "temperature" ) << QStringLiteral( "temperature/Maximums" )
6239 << QStringLiteral( "temperature/Minimums" ) << QStringLiteral( "temperature/Time at Maximums" ) << QStringLiteral( "temperature/Time at Minimums" )
6240 << QStringLiteral( "velocity_x" ) << QStringLiteral( "velocity_y" ) << QStringLiteral( "velocity_mag" ) << QStringLiteral( "velocity_dir" )
6241 << QStringLiteral( "velocity/Maximums_x" ) << QStringLiteral( "velocity/Maximums_y" ) << QStringLiteral( "velocity/Maximums_mag" ) << QStringLiteral( "velocity/Maximums_dir" )
6242 << QStringLiteral( "velocity/Minimums_x" ) << QStringLiteral( "velocity/Minimums_y" ) << QStringLiteral( "velocity/Minimums_mag" ) << QStringLiteral( "velocity/Minimums_dir" )
6243 << QStringLiteral( "velocity/Time at Maximums" ) << QStringLiteral( "velocity/Time at Minimums" ) << QStringLiteral( "water depth" );
6244
6245 for ( int i = 0; i < fieldsName.count(); ++i )
6246 QCOMPARE( fieldsName.at( i ), resultLayer->fields().at( i ).name() );
6247
6248 QgsFeatureIterator featIt = resultLayer->getFeatures();
6249 QgsFeature feat;
6250 for ( int i = 0; i < 8; ++i )
6251 featIt.nextFeature( feat );
6252 QCOMPARE( QStringLiteral( "Point (25 50)" ), feat.geometry().asWkt() );
6253 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 0 ).toDouble(), -5.025, 2 ) );
6254 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 1 ).toDouble(), 1.424, 2 ) );
6255 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 2 ).toDouble(), 5.00, 2 ) );
6256 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 1.32e-36, 2 ) );
6257 QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 0.02776, 2 ) );
6258
6259 }
6260
rasterizeMesh()6261 void TestQgsProcessingAlgs::rasterizeMesh()
6262 {
6263 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshrasterize" ) ) );
6264 QVERIFY( alg != nullptr );
6265
6266 QVariantMap parameters;
6267 parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
6268
6269 QVariantList datasetGroup;
6270 datasetGroup << 1 << 2 << 3;
6271 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
6272
6273 QVariantMap datasetTime;
6274 datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6275 QVariantList datasetIndex;
6276 datasetIndex << 1 << 1;
6277 datasetTime[QStringLiteral( "value" )] = datasetIndex;
6278 parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
6279
6280 parameters.insert( QStringLiteral( "PIXEL_SIZE" ), 200.0 );
6281
6282 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
6283
6284 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6285 context->setProject( QgsProject::instance() );
6286 QgsProcessingFeedback feedback;
6287 QVariantMap results;
6288 bool ok = false;
6289 results = alg->run( parameters, *context, &feedback, &ok );
6290 QVERIFY( ok );
6291
6292 std::unique_ptr<QgsRasterLayer> outputRaster = std::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
6293 QVERIFY( outputRaster );
6294 QVERIFY( outputRaster->isValid() );
6295 QgsRasterDataProvider *outputProvider = outputRaster->dataProvider();
6296
6297 QCOMPARE( outputProvider->bandCount(), 3 );
6298 QCOMPARE( outputProvider->xSize(), 10 );
6299 QCOMPARE( outputProvider->ySize(), 5 );
6300
6301 std::unique_ptr<QgsRasterBlock> outputBlock_1( outputProvider->block( 1, outputRaster->extent(), 10, 5 ) );
6302 std::unique_ptr<QgsRasterBlock> outputBlock_2( outputProvider->block( 2, outputRaster->extent(), 10, 5 ) );
6303 std::unique_ptr<QgsRasterBlock> outputBlock_3( outputProvider->block( 3, outputRaster->extent(), 10, 5 ) );
6304
6305 // load expected result
6306 const QString dataDir = QString( TEST_DATA_DIR ); //defined in CmakeLists.txt
6307 std::unique_ptr<QgsRasterLayer> expectedRaster = std::make_unique< QgsRasterLayer >( dataDir + "/mesh/rasterized_mesh.tif", "expected", "gdal" );
6308 QVERIFY( expectedRaster );
6309 QVERIFY( expectedRaster->isValid() );
6310 QgsRasterDataProvider *expectedProvider = outputRaster->dataProvider();
6311 std::unique_ptr<QgsRasterBlock> expectedBlock_1( expectedProvider->block( 1, expectedRaster->extent(), 10, 5 ) );
6312 std::unique_ptr<QgsRasterBlock> expectedBlock_2( expectedProvider->block( 2, expectedRaster->extent(), 10, 5 ) );
6313 std::unique_ptr<QgsRasterBlock> expectedBlock_3( expectedProvider->block( 3, expectedRaster->extent(), 10, 5 ) );
6314
6315 for ( int ix = 0; ix < 10; ++ix )
6316 {
6317 for ( int iy = 0; iy < 5; ++iy )
6318 {
6319 if ( !( std::isnan( outputBlock_1->value( iy, ix ) ) && std::isnan( expectedBlock_1->value( iy, ix ) ) ) )
6320 QCOMPARE( outputBlock_1->value( iy, ix ), expectedBlock_1->value( iy, ix ) );
6321 if ( !( std::isnan( outputBlock_2->value( iy, ix ) ) && std::isnan( expectedBlock_2->value( iy, ix ) ) ) )
6322 QCOMPARE( outputBlock_2->value( iy, ix ), expectedBlock_2->value( iy, ix ) );
6323 if ( !( std::isnan( outputBlock_2->value( iy, ix ) ) && std::isnan( expectedBlock_2->value( iy, ix ) ) ) )
6324 QCOMPARE( outputBlock_3->value( iy, ix ), expectedBlock_3->value( iy, ix ) );
6325 }
6326 }
6327 }
6328
exportMeshContours()6329 void TestQgsProcessingAlgs::exportMeshContours()
6330 {
6331 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshcontours" ) ) );
6332 QVERIFY( alg != nullptr );
6333
6334 QVariantMap parameters;
6335 parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
6336
6337 QVariantList datasetGroup;
6338 datasetGroup << 1 << 2 << 3;
6339 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
6340
6341 QVariantMap datasetTime;
6342 datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6343 QVariantList datasetIndex;
6344 datasetIndex << 1 << 1;
6345 datasetTime[QStringLiteral( "value" )] = datasetIndex;
6346 parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
6347
6348 parameters.insert( QStringLiteral( "OUTPUT_LINES" ), QgsProcessing::TEMPORARY_OUTPUT );
6349 parameters.insert( QStringLiteral( "OUTPUT_POLYGONS" ), QgsProcessing::TEMPORARY_OUTPUT );
6350
6351 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6352 context->setProject( QgsProject::instance() );
6353 QgsProcessingFeedback feedback;
6354 QVariantMap results;
6355 bool ok = false;
6356
6357 results = alg->run( parameters, *context, &feedback, &ok );
6358 QVERIFY( !ok );
6359
6360 // min>max
6361 parameters.insert( QStringLiteral( "INCREMENT" ), 0.5 );
6362 parameters.insert( QStringLiteral( "MINIMUM" ), 5.0 );
6363 parameters.insert( QStringLiteral( "MAXIMUM" ), 2.0 );
6364
6365 results = alg->run( parameters, *context, &feedback, &ok );
6366 QVERIFY( !ok );
6367
6368 // min-max<increrment
6369 parameters.insert( QStringLiteral( "INCREMENT" ), 10 );
6370 parameters.insert( QStringLiteral( "MINIMUM" ), 5.0 );
6371 parameters.insert( QStringLiteral( "MAXIMUM" ), 2.0 );
6372
6373 results = alg->run( parameters, *context, &feedback, &ok );
6374 QVERIFY( !ok );
6375
6376 // min-max<increrment
6377 parameters.insert( QStringLiteral( "INCREMENT" ), 2 );
6378 parameters.insert( QStringLiteral( "MINIMUM" ), 0.25 );
6379 parameters.insert( QStringLiteral( "MAXIMUM" ), 6.25 );
6380
6381 results = alg->run( parameters, *context, &feedback, &ok );
6382 QVERIFY( ok );
6383
6384 QgsVectorLayer *resultLinesLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT_LINES" ) ).toString() ) );
6385 QVERIFY( resultLinesLayer );
6386 QVERIFY( resultLinesLayer->isValid() );
6387 QgsAttributeList attributeList = resultLinesLayer->attributeList();
6388 QCOMPARE( resultLinesLayer->fields().count(), 3 );
6389 QCOMPARE( resultLinesLayer->fields().at( 0 ).name(), QStringLiteral( "group" ) );
6390 QCOMPARE( resultLinesLayer->fields().at( 1 ).name(), QStringLiteral( "time" ) );
6391 QCOMPARE( resultLinesLayer->fields().at( 2 ).name(), QStringLiteral( "value" ) );
6392
6393 QCOMPARE( resultLinesLayer->featureCount(), 4l );
6394 QgsFeatureIterator featIt = resultLinesLayer->getFeatures();
6395 QgsFeature feat;
6396 featIt.nextFeature( feat );
6397 QCOMPARE( QStringLiteral( "LineStringZ (1250 3000 20, 1250 2250 27.5, 1250 2000 22.5)" ), feat.geometry().asWkt() );
6398 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexScalarDataset" ) );
6399 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6400 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
6401 featIt.nextFeature( feat );
6402 QCOMPARE( QStringLiteral( "LineStringZ (1006.94319345290614365 3000 10.27772773811624596, 1000 2976.48044676110157525 10.23519553238898538)" ), feat.geometry().asWkt() );
6403 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexVectorDataset" ) );
6404 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6405 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
6406 featIt.nextFeature( feat );
6407 QCOMPARE( QStringLiteral( "LineStringZ (2009.71706923721990279 2990.28293076277986984 49.90282930762779756, 2462.15304528350043256 2000 34.62153045283500319)" ), feat.geometry().asWkt() );
6408 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexVectorDataset" ) );
6409 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6410 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 4.25 );
6411 featIt.nextFeature( feat );
6412 QCOMPARE( QStringLiteral( "LineStringZ (1500 3000 30, 1500 2500 35, 1500 2000 25)" ), feat.geometry().asWkt() );
6413 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "FaceScalarDataset" ) );
6414 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6415 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
6416
6417 QgsVectorLayer *resultpolygonLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT_POLYGONS" ) ).toString() ) );
6418 QVERIFY( resultpolygonLayer );
6419 QVERIFY( resultpolygonLayer->isValid() );
6420 attributeList = resultpolygonLayer->attributeList();
6421 QCOMPARE( resultpolygonLayer->fields().count(), 4 );
6422 QCOMPARE( resultpolygonLayer->fields().at( 0 ).name(), QStringLiteral( "group" ) );
6423 QCOMPARE( resultpolygonLayer->fields().at( 1 ).name(), QStringLiteral( "time" ) );
6424 QCOMPARE( resultpolygonLayer->fields().at( 2 ).name(), QStringLiteral( "min_value" ) );
6425 QCOMPARE( resultpolygonLayer->fields().at( 3 ).name(), QStringLiteral( "max_value" ) );
6426
6427 QCOMPARE( resultpolygonLayer->featureCount(), 6l );
6428 featIt = resultpolygonLayer->getFeatures();
6429 featIt.nextFeature( feat );
6430 QCOMPARE( QStringLiteral( "PolygonZ ((1250 2250 27.5, 1250 2000 22.5, 1000 2000 20, 1000 3000 10, 1250 3000 20, 1250 2250 27.5))" ), feat.geometry().asWkt() );
6431 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexScalarDataset" ) );
6432 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6433 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 0.25 );
6434 QCOMPARE( feat.attributes().at( 3 ).toDouble(), 2.25 );
6435 featIt.nextFeature( feat );
6436 QCOMPARE( QStringLiteral( "PolygonZ ((2000 2000 30, 1250 2000 22.5, 1250 2250 27.5, 1250 3000 20, 2000 3000 50, 3000 2000 40, 2000 2000 30))" ), feat.geometry().asWkt() );
6437 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexScalarDataset" ) );
6438 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6439 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
6440 QCOMPARE( feat.attributes().at( 3 ).toDouble(), 4.25 );
6441 featIt.nextFeature( feat );
6442 QCOMPARE( QStringLiteral( "PolygonZ ((1006.94319345290614365 3000 10.27772773811624596, 1000 3000 10, 1000 2976.48044676110157525 10.23519553238898538, 1006.94319345290614365 3000 10.27772773811624596))" ), feat.geometry().asWkt() );
6443 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexVectorDataset" ) );
6444 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6445 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 0.25 );
6446 QCOMPARE( feat.attributes().at( 3 ).toDouble(), 2.25 );
6447 featIt.nextFeature( feat );
6448 featIt.nextFeature( feat );
6449 QCOMPARE( QStringLiteral( "PolygonZ ((1500 2500 35, 1500 2000 25, 1000 2000 20, 1000 3000 10, 1500 3000 30, 1500 2500 35))" ), feat.geometry().asWkt() );
6450 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "FaceScalarDataset" ) );
6451 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6452 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 0.25 );
6453 QCOMPARE( feat.attributes().at( 3 ).toDouble(), 2.25 );
6454 featIt.nextFeature( feat );
6455 QCOMPARE( QStringLiteral( "PolygonZ ((2000 2000 30, 1500 2000 25, 1500 2500 35, 1500 3000 30, 2000 3000 50, 3000 2000 40, 2000 2000 30))" ), feat.geometry().asWkt() );
6456 QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "FaceScalarDataset" ) );
6457 QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
6458 QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
6459 QCOMPARE( feat.attributes().at( 3 ).toDouble(), 4.25 );
6460
6461 parameters.insert( QStringLiteral( "CONTOUR_LEVEL_LIST" ), QStringLiteral( "4,2,3" ) );
6462 results = alg->run( parameters, *context, &feedback, &ok );
6463 QVERIFY( !ok );
6464
6465 parameters.insert( QStringLiteral( "CONTOUR_LEVEL_LIST" ), QStringLiteral( "2,2,3" ) );
6466 results = alg->run( parameters, *context, &feedback, &ok );
6467 QVERIFY( !ok );
6468
6469 parameters.insert( QStringLiteral( "CONTOUR_LEVEL_LIST" ), QStringLiteral( "1,2,3" ) );
6470 results = alg->run( parameters, *context, &feedback, &ok );
6471 QVERIFY( ok );
6472 }
6473
exportMeshCrossSection()6474 void TestQgsProcessingAlgs::exportMeshCrossSection()
6475 {
6476 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshexportcrosssection" ) ) );
6477 QVERIFY( alg != nullptr );
6478
6479 QVariantMap parameters;
6480 parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
6481
6482 QVariantList datasetGroup;
6483 datasetGroup << 1 << 2 << 3;
6484 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
6485
6486 QVariantMap datasetTime;
6487 datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6488 QVariantList datasetIndex;
6489 datasetIndex << 1 << 1;
6490 datasetTime[QStringLiteral( "value" )] = datasetIndex;
6491 parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
6492
6493 parameters.insert( QStringLiteral( "RESOLUTION" ), 100 );
6494
6495 const QString outputPath = QDir::tempPath() + "/test_mesh_xs.csv";
6496 parameters.insert( QStringLiteral( "OUTPUT" ), outputPath );
6497
6498 QgsVectorLayer *layerLine = new QgsVectorLayer( QStringLiteral( "LineString" ),
6499 QStringLiteral( "lines_for_xs" ),
6500 QStringLiteral( "memory" ) );
6501
6502 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6503 context->setProject( QgsProject::instance() );
6504 QgsProcessingFeedback feedback;
6505 QVariantMap results;
6506 bool ok = false;
6507
6508 results = alg->run( parameters, *context, &feedback, &ok );
6509 QVERIFY( !ok );
6510
6511 QStringList wktLines;
6512 wktLines << QStringLiteral( "LineString (1500 2200, 2500 2200)" );
6513 wktLines << QStringLiteral( "LineString (1500 1500, 1500 3200)" );
6514
6515 QgsFeatureList flist;
6516 for ( const QString &wkt : wktLines )
6517 {
6518 QgsFeature feat;
6519 feat.setGeometry( QgsGeometry::fromWkt( wkt ) );
6520 flist << feat;
6521 }
6522 layerLine->dataProvider()->addFeatures( flist );
6523 QgsProject::instance()->addMapLayer( layerLine ); QgsProject::instance()->addMapLayer( layerLine );
6524 parameters.insert( QStringLiteral( "INPUT_LINES" ), layerLine->name() );
6525
6526 results = alg->run( parameters, *context, &feedback, &ok );
6527 QVERIFY( ok );
6528
6529 QFile outputFile( outputPath );
6530 QVERIFY( outputFile.open( QIODevice::ReadOnly ) );
6531 QTextStream textStream( &outputFile );
6532 const QString header = textStream.readLine();
6533 QCOMPARE( header, QStringLiteral( "fid,x,y,offset,VertexScalarDataset,VertexVectorDataset,FaceScalarDataset" ) );
6534
6535 QStringList expectedLines;
6536 expectedLines << QStringLiteral( "1,1500.00,2200.00,0.00,2.50,3.33,2.00" )
6537 << QStringLiteral( "1,1600.00,2200.00,100.00,2.60,3.41,2.00" )
6538 << QStringLiteral( "1,1700.00,2200.00,200.00,2.70,3.48,2.00" )
6539 << QStringLiteral( "1,1800.00,2200.00,300.00,2.80,3.56,2.00" )
6540 << QStringLiteral( "1,1900.00,2200.00,400.00,2.90,3.64,2.00" )
6541 << QStringLiteral( "1,2000.00,2200.00,500.00,3.00,3.72,2.00" )
6542 << QStringLiteral( "1,2100.00,2200.00,600.00,3.10,3.86,3.00" )
6543 << QStringLiteral( "1,2200.00,2200.00,700.00,3.20,4.00,3.00" )
6544 << QStringLiteral( "1,2300.00,2200.00,800.00,3.30,4.14,3.00" )
6545 << QStringLiteral( "1,2400.00,2200.00,900.00,3.40,4.28,3.00" )
6546 << QStringLiteral( "1,2500.00,2200.00,1000.00,3.50,4.42,3.00" )
6547 << QStringLiteral( "2,1500.00,1500.00,0.00, , , " )
6548 << QStringLiteral( "2,1500.00,1600.00,100.00, , , " )
6549 << QStringLiteral( "2,1500.00,1700.00,200.00, , , " )
6550 << QStringLiteral( "2,1500.00,1800.00,300.00, , , " )
6551 << QStringLiteral( "2,1500.00,1900.00,400.00, , , " )
6552 << QStringLiteral( "2,1500.00,2000.00,500.00,2.50,3.20,2.00" )
6553 << QStringLiteral( "2,1500.00,2100.00,600.00,2.50,3.26,2.00" )
6554 << QStringLiteral( "2,1500.00,2200.00,700.00,2.50,3.33,2.00" )
6555 << QStringLiteral( "2,1500.00,2300.00,800.00,2.50,3.40,2.00" )
6556 << QStringLiteral( "2,1500.00,2400.00,900.00,2.50,3.47,2.00" )
6557 << QStringLiteral( "2,1500.00,2500.00,1000.00,2.50,3.54,2.00" )
6558 << QStringLiteral( "2,1500.00,2600.00,1100.00,2.50,3.33,2.00" )
6559 << QStringLiteral( "2,1500.00,2700.00,1200.00,2.50,3.14,2.00" )
6560 << QStringLiteral( "2,1500.00,2800.00,1300.00,2.50,2.97,2.00" )
6561 << QStringLiteral( "2,1500.00,2900.00,1400.00,2.50,2.82,2.00" )
6562 << QStringLiteral( "2,1500.00,3000.00,1500.00,2.50,2.69,2.00" )
6563 << QStringLiteral( "2,1500.00,3100.00,1600.00, , , " )
6564 << QStringLiteral( "2,1500.00,3200.00,1700.00, , , " );
6565 QString line = textStream.readLine();
6566 int i = 0;
6567 QVERIFY( !line.isEmpty() );
6568 while ( !line.isEmpty() )
6569 {
6570 QCOMPARE( line, expectedLines.at( i ) );
6571 ++i;
6572 line = textStream.readLine();
6573 }
6574
6575 QVERIFY( i == expectedLines.count() );
6576 }
6577
fileDownloader()6578 void TestQgsProcessingAlgs::fileDownloader()
6579 {
6580 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:filedownloader" ) ) );
6581 QVERIFY( alg != nullptr );
6582
6583 QVariantMap parameters;
6584 parameters.insert( QStringLiteral( "URL" ), QStringLiteral( "https://version.qgis.org/version.txt" ) );
6585 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
6586
6587 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6588 QgsProcessingFeedback feedback;
6589 QVariantMap results;
6590 bool ok = false;
6591
6592 results = alg->run( parameters, *context, &feedback, &ok );
6593 QVERIFY( ok );
6594 // verify that temporary outputs have the URL file extension appended
6595 QVERIFY( results.value( QStringLiteral( "OUTPUT" ) ).toString().endsWith( QLatin1String( ".txt" ) ) );
6596 }
6597
rasterize()6598 void TestQgsProcessingAlgs::rasterize()
6599 {
6600 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:rasterize" ) ) );
6601 QVERIFY( alg != nullptr );
6602
6603 const QString outputTif = QDir::tempPath() + "/rasterize_output.tif";
6604 if ( QFile::exists( outputTif ) )
6605 QFile::remove( outputTif );
6606
6607 QVariantMap parameters;
6608 parameters.insert( QStringLiteral( "EXTENT" ), QStringLiteral( "-120,-80,15,55" ) );
6609 parameters.insert( QStringLiteral( "TILE_SIZE" ), 320 );
6610 parameters.insert( QStringLiteral( "MAP_UNITS_PER_PIXEL" ), 0.125 );
6611 parameters.insert( QStringLiteral( "OUTPUT" ), outputTif );
6612
6613 // create a temporary project with three layers, but only two are visible
6614 // (to test that the algorithm in the default setup without defined LAYERS or MAP_THEME uses only vsisible
6615 // layers that and in the correct order)
6616 QgsProject project;
6617 const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
6618 QgsVectorLayer *pointsLayer = new QgsVectorLayer( dataDir + "/points.shp", QStringLiteral( "points" ), QStringLiteral( "ogr" ) );
6619 QgsVectorLayer *linesLayer = new QgsVectorLayer( dataDir + "/lines.shp", QStringLiteral( "lines" ), QStringLiteral( "ogr" ) );
6620 QgsVectorLayer *polygonLayer = new QgsVectorLayer( dataDir + "/polys.shp", QStringLiteral( "polygons" ), QStringLiteral( "ogr" ) );
6621 QVERIFY( pointsLayer->isValid() && linesLayer->isValid() && polygonLayer->isValid() );
6622 project.addMapLayers( QList<QgsMapLayer *>() << pointsLayer << linesLayer << polygonLayer );
6623 QgsLayerTreeLayer *nodePolygons = project.layerTreeRoot()->findLayer( polygonLayer );
6624 QVERIFY( nodePolygons );
6625 nodePolygons->setItemVisibilityChecked( false );
6626
6627 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6628 context->setProject( &project );
6629 QgsProcessingFeedback feedback;
6630 QVariantMap results;
6631 bool ok = false;
6632
6633 results = alg->run( parameters, *context, &feedback, &ok );
6634 QVERIFY( ok );
6635 QVERIFY( QFile::exists( outputTif ) );
6636
6637 QgsRenderChecker checker;
6638 checker.setControlPathPrefix( QStringLiteral( "processing_algorithm" ) );
6639 checker.setControlExtension( "tif" );
6640 checker.setControlName( "expected_rasterize" );
6641 checker.setRenderedImage( outputTif );
6642 QVERIFY( checker.compareImages( "rasterize", 500 ) );
6643 }
6644
convertGpxFeatureType()6645 void TestQgsProcessingAlgs::convertGpxFeatureType()
6646 {
6647 // test generation of babel argument lists
6648 QStringList processArgs;
6649 QStringList logArgs;
6650
6651 QgsConvertGpxFeatureTypeAlgorithm::createArgumentLists( QStringLiteral( "/home/me/my input file.gpx" ),
6652 QStringLiteral( "/home/me/my output file.gpx" ),
6653 QgsConvertGpxFeatureTypeAlgorithm::WaypointsFromRoute,
6654 processArgs, logArgs );
6655 QCOMPARE( processArgs, QStringList(
6656 {
6657 QStringLiteral( "-i" ),
6658 QStringLiteral( "gpx" ),
6659 QStringLiteral( "-f" ),
6660 QStringLiteral( "/home/me/my input file.gpx" ),
6661 QStringLiteral( "-x" ),
6662 QStringLiteral( "transform,wpt=rte,del" ),
6663 QStringLiteral( "-o" ),
6664 QStringLiteral( "gpx" ),
6665 QStringLiteral( "-F" ),
6666 QStringLiteral( "/home/me/my output file.gpx" )
6667 } ) );
6668 // when showing the babel command, filenames should be wrapped in "", which is what QProcess does internally (hence the processArgs don't have these)
6669 QCOMPARE( logArgs, QStringList(
6670 {
6671 QStringLiteral( "-i" ),
6672 QStringLiteral( "gpx" ),
6673 QStringLiteral( "-f" ),
6674 QStringLiteral( "\"/home/me/my input file.gpx\"" ),
6675 QStringLiteral( "-x" ),
6676 QStringLiteral( "transform,wpt=rte,del" ),
6677 QStringLiteral( "-o" ),
6678 QStringLiteral( "gpx" ),
6679 QStringLiteral( "-F" ),
6680 QStringLiteral( "\"/home/me/my output file.gpx\"" )
6681 } ) );
6682
6683 logArgs.clear();
6684 processArgs.clear();
6685 QgsConvertGpxFeatureTypeAlgorithm::createArgumentLists( QStringLiteral( "/home/me/my input file.gpx" ),
6686 QStringLiteral( "/home/me/my output file.gpx" ),
6687 QgsConvertGpxFeatureTypeAlgorithm::WaypointsFromTrack,
6688 processArgs, logArgs );
6689 QCOMPARE( processArgs, QStringList(
6690 {
6691 QStringLiteral( "-i" ),
6692 QStringLiteral( "gpx" ),
6693 QStringLiteral( "-f" ),
6694 QStringLiteral( "/home/me/my input file.gpx" ),
6695 QStringLiteral( "-x" ),
6696 QStringLiteral( "transform,wpt=trk,del" ),
6697 QStringLiteral( "-o" ),
6698 QStringLiteral( "gpx" ),
6699 QStringLiteral( "-F" ),
6700 QStringLiteral( "/home/me/my output file.gpx" )
6701 } ) );
6702 // when showing the babel command, filenames should be wrapped in "", which is what QProcess does internally (hence the processArgs don't have these)
6703 QCOMPARE( logArgs, QStringList(
6704 {
6705 QStringLiteral( "-i" ),
6706 QStringLiteral( "gpx" ),
6707 QStringLiteral( "-f" ),
6708 QStringLiteral( "\"/home/me/my input file.gpx\"" ),
6709 QStringLiteral( "-x" ),
6710 QStringLiteral( "transform,wpt=trk,del" ),
6711 QStringLiteral( "-o" ),
6712 QStringLiteral( "gpx" ),
6713 QStringLiteral( "-F" ),
6714 QStringLiteral( "\"/home/me/my output file.gpx\"" )
6715 } ) );
6716
6717 logArgs.clear();
6718 processArgs.clear();
6719
6720 QgsConvertGpxFeatureTypeAlgorithm::createArgumentLists( QStringLiteral( "/home/me/my input file.gpx" ),
6721 QStringLiteral( "/home/me/my output file.gpx" ),
6722 QgsConvertGpxFeatureTypeAlgorithm::RouteFromWaypoints,
6723 processArgs, logArgs );
6724 QCOMPARE( processArgs, QStringList(
6725 {
6726 QStringLiteral( "-i" ),
6727 QStringLiteral( "gpx" ),
6728 QStringLiteral( "-f" ),
6729 QStringLiteral( "/home/me/my input file.gpx" ),
6730 QStringLiteral( "-x" ),
6731 QStringLiteral( "transform,rte=wpt,del" ),
6732 QStringLiteral( "-o" ),
6733 QStringLiteral( "gpx" ),
6734 QStringLiteral( "-F" ),
6735 QStringLiteral( "/home/me/my output file.gpx" )
6736 } ) );
6737 // when showing the babel command, filenames should be wrapped in "", which is what QProcess does internally (hence the processArgs don't have these)
6738 QCOMPARE( logArgs, QStringList(
6739 {
6740 QStringLiteral( "-i" ),
6741 QStringLiteral( "gpx" ),
6742 QStringLiteral( "-f" ),
6743 QStringLiteral( "\"/home/me/my input file.gpx\"" ),
6744 QStringLiteral( "-x" ),
6745 QStringLiteral( "transform,rte=wpt,del" ),
6746 QStringLiteral( "-o" ),
6747 QStringLiteral( "gpx" ),
6748 QStringLiteral( "-F" ),
6749 QStringLiteral( "\"/home/me/my output file.gpx\"" )
6750 } ) );
6751
6752
6753 logArgs.clear();
6754 processArgs.clear();
6755
6756 QgsConvertGpxFeatureTypeAlgorithm::createArgumentLists( QStringLiteral( "/home/me/my input file.gpx" ),
6757 QStringLiteral( "/home/me/my output file.gpx" ),
6758 QgsConvertGpxFeatureTypeAlgorithm::TrackFromWaypoints,
6759 processArgs, logArgs );
6760 QCOMPARE( processArgs, QStringList(
6761 {
6762 QStringLiteral( "-i" ),
6763 QStringLiteral( "gpx" ),
6764 QStringLiteral( "-f" ),
6765 QStringLiteral( "/home/me/my input file.gpx" ),
6766 QStringLiteral( "-x" ),
6767 QStringLiteral( "transform,trk=wpt,del" ),
6768 QStringLiteral( "-o" ),
6769 QStringLiteral( "gpx" ),
6770 QStringLiteral( "-F" ),
6771 QStringLiteral( "/home/me/my output file.gpx" )
6772 } ) );
6773 // when showing the babel command, filenames should be wrapped in "", which is what QProcess does internally (hence the processArgs don't have these)
6774 QCOMPARE( logArgs, QStringList(
6775 {
6776 QStringLiteral( "-i" ),
6777 QStringLiteral( "gpx" ),
6778 QStringLiteral( "-f" ),
6779 QStringLiteral( "\"/home/me/my input file.gpx\"" ),
6780 QStringLiteral( "-x" ),
6781 QStringLiteral( "transform,trk=wpt,del" ),
6782 QStringLiteral( "-o" ),
6783 QStringLiteral( "gpx" ),
6784 QStringLiteral( "-F" ),
6785 QStringLiteral( "\"/home/me/my output file.gpx\"" )
6786 } ) );
6787 }
6788
convertGpsData()6789 void TestQgsProcessingAlgs::convertGpsData()
6790 {
6791 TestProcessingFeedback feedback;
6792
6793 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:convertgpsdata" ) ) );
6794 QVERIFY( alg != nullptr );
6795
6796 QVariantMap parameters;
6797 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "%1/GARMIN_ATRK.NVM" ).arg( TEST_DATA_DIR ) );
6798 parameters.insert( QStringLiteral( "FORMAT" ), QStringLiteral( "garmin_xt" ) );
6799 parameters.insert( QStringLiteral( "FEATURE_TYPE" ), 0 ); // waypoints
6800 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
6801
6802 bool ok = false;
6803 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6804
6805 QVariantMap results;
6806 results = alg->run( parameters, *context, &feedback, &ok );
6807 // garmin_xt format does not support waypoints, exception should have been raised
6808 QVERIFY( !ok );
6809
6810 QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "The GPSBabel format \u201Cgarmin_xt\u201D does not support converting waypoints." ) );
6811 feedback.errors.clear();
6812
6813 parameters.insert( QStringLiteral( "FEATURE_TYPE" ), 1 ); // routes
6814 ok = false;
6815 results = alg->run( parameters, *context, &feedback, &ok );
6816 // garmin_xt format does not support routes, exception should have been raised
6817 QVERIFY( !ok );
6818 QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "The GPSBabel format \u201Cgarmin_xt\u201D does not support converting routes." ) );
6819 feedback.errors.clear();
6820
6821 parameters.insert( QStringLiteral( "FEATURE_TYPE" ), 2 ); // tracks
6822 ok = false;
6823 results = alg->run( parameters, *context, &feedback, &ok );
6824 // garmin_xt format does support tracks!
6825 QVERIFY( ok );
6826
6827 QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT_LAYER" ) ).toString() ) );
6828 QVERIFY( resultLayer );
6829 QCOMPARE( resultLayer->providerType(), QStringLiteral( "gpx" ) );
6830 QCOMPARE( resultLayer->wkbType(), QgsWkbTypes::LineString );
6831 QCOMPARE( resultLayer->featureCount(), 1LL );
6832
6833 // algorithm should also run when given the description for a format, not the format name
6834 parameters.insert( QStringLiteral( "FORMAT" ), QStringLiteral( "Mobile Garmin XT Track files" ) );
6835 ok = false;
6836 results = alg->run( parameters, *context, &feedback, &ok );
6837 QVERIFY( ok );
6838 resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT_LAYER" ) ).toString() ) );
6839 QVERIFY( resultLayer );
6840 QCOMPARE( resultLayer->providerType(), QStringLiteral( "gpx" ) );
6841 QCOMPARE( resultLayer->wkbType(), QgsWkbTypes::LineString );
6842 QCOMPARE( resultLayer->featureCount(), 1LL );
6843
6844 // try with a format which doesn't exist
6845 feedback.errors.clear();
6846 parameters.insert( QStringLiteral( "FORMAT" ), QStringLiteral( "not a format" ) );
6847 ok = false;
6848 results = alg->run( parameters, *context, &feedback, &ok );
6849 QVERIFY( !ok );
6850 QVERIFY( feedback.errors.value( 0 ).startsWith( QStringLiteral( "Unknown GPSBabel format \u201Cnot a format\u201D." ) ) );
6851 }
6852
downloadGpsData()6853 void TestQgsProcessingAlgs::downloadGpsData()
6854 {
6855 TestProcessingFeedback feedback;
6856
6857 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:downloadgpsdata" ) ) );
6858 QVERIFY( alg != nullptr );
6859
6860 QVariantMap parameters;
6861 parameters.insert( QStringLiteral( "DEVICE" ), QStringLiteral( "xxx" ) );
6862 parameters.insert( QStringLiteral( "PORT" ), QStringLiteral( "usb:" ) );
6863 parameters.insert( QStringLiteral( "FEATURE_TYPE" ), 0 ); // waypoints
6864 parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
6865
6866 bool ok = false;
6867 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6868
6869 QVariantMap results;
6870 results = alg->run( parameters, *context, &feedback, &ok );
6871 // invalid device
6872 QVERIFY( !ok );
6873
6874 QVERIFY( feedback.errors.value( 0 ).startsWith( QStringLiteral( "Unknown GPSBabel device \u201Cxxx\u201D. Valid devices are:" ) ) );
6875 feedback.errors.clear();
6876
6877 parameters.insert( QStringLiteral( "DEVICE" ), QStringLiteral( "Garmin serial" ) );
6878 parameters.insert( QStringLiteral( "PORT" ), QStringLiteral( "not a port" ) );
6879 ok = false;
6880 results = alg->run( parameters, *context, &feedback, &ok );
6881 // invalid port
6882 QVERIFY( !ok );
6883 QVERIFY( feedback.errors.value( 0 ).startsWith( QStringLiteral( "Unknown port \u201Cnot a port\u201D. Valid ports are:" ) ) );
6884 feedback.errors.clear();
6885 }
6886
uploadGpsData()6887 void TestQgsProcessingAlgs::uploadGpsData()
6888 {
6889 TestProcessingFeedback feedback;
6890
6891 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:downloadgpsdata" ) ) );
6892 QVERIFY( alg != nullptr );
6893
6894 QVariantMap parameters;
6895 parameters.insert( QStringLiteral( "DEVICE" ), QStringLiteral( "xxx" ) );
6896 parameters.insert( QStringLiteral( "PORT" ), QStringLiteral( "usb:" ) );
6897 parameters.insert( QStringLiteral( "FEATURE_TYPE" ), 0 ); // waypoints
6898 parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "%1/layers.gpx" ).arg( TEST_DATA_DIR ) );
6899
6900 bool ok = false;
6901 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6902
6903 QVariantMap results;
6904 results = alg->run( parameters, *context, &feedback, &ok );
6905 // invalid device
6906 QVERIFY( !ok );
6907
6908 QVERIFY( feedback.errors.value( 0 ).startsWith( QStringLiteral( "Unknown GPSBabel device \u201Cxxx\u201D. Valid devices are:" ) ) );
6909 feedback.errors.clear();
6910
6911 parameters.insert( QStringLiteral( "DEVICE" ), QStringLiteral( "Garmin serial" ) );
6912 parameters.insert( QStringLiteral( "PORT" ), QStringLiteral( "not a port" ) );
6913 ok = false;
6914 results = alg->run( parameters, *context, &feedback, &ok );
6915 // invalid port
6916 QVERIFY( !ok );
6917 QVERIFY( feedback.errors.value( 0 ).startsWith( QStringLiteral( "Unknown port \u201Cnot a port\u201D. Valid ports are:" ) ) );
6918 feedback.errors.clear();
6919 }
6920
transferMainAnnotationLayer()6921 void TestQgsProcessingAlgs::transferMainAnnotationLayer()
6922 {
6923 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:transferannotationsfrommain" ) ) );
6924 QVERIFY( alg != nullptr );
6925
6926 QgsProject p;
6927 p.mainAnnotationLayer()->addItem( new QgsAnnotationMarkerItem( QgsPoint( 1, 2 ) ) );
6928
6929 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6930 context->setProject( &p );
6931 QgsProcessingFeedback feedback;
6932 QVariantMap results;
6933 bool ok = false;
6934
6935 QVariantMap parameters;
6936 parameters.insert( QStringLiteral( "LAYER_NAME" ), QStringLiteral( "my annotations" ) );
6937 results = alg->run( parameters, *context, &feedback, &ok );
6938 QVERIFY( ok );
6939
6940 QCOMPARE( p.mainAnnotationLayer()->items().size(), 0 );
6941 QgsAnnotationLayer *newLayer = qobject_cast< QgsAnnotationLayer * >( p.mapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
6942 QCOMPARE( newLayer->name(), QStringLiteral( "my annotations" ) );
6943 QCOMPARE( newLayer->items().size(), 1 );
6944 }
6945
exportMeshTimeSeries()6946 void TestQgsProcessingAlgs::exportMeshTimeSeries()
6947 {
6948 std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshexporttimeseries" ) ) );
6949 QVERIFY( alg != nullptr );
6950
6951 QVariantMap parameters;
6952 parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
6953
6954 QVariantList datasetGroup;
6955 datasetGroup << 1 << 2 << 3;
6956 parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
6957
6958 QVariantMap datasetStartTime;
6959 datasetStartTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6960 QVariantList datasetIndexStart;
6961 datasetIndexStart << 1 << 0;
6962 datasetStartTime[QStringLiteral( "value" )] = datasetIndexStart;
6963 parameters.insert( QStringLiteral( "STARTING_TIME" ), datasetStartTime );
6964
6965 QVariantMap datasetEndTime;
6966 datasetEndTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
6967 QVariantList datasetIndexEnd;
6968 datasetIndexEnd << 1 << 1;
6969 datasetEndTime[QStringLiteral( "value" )] = datasetIndexEnd;
6970 parameters.insert( QStringLiteral( "FINISHING_TIME" ), datasetEndTime );
6971
6972 const QString outputPath = QDir::tempPath() + "/test_mesh_ts.csv";
6973 parameters.insert( QStringLiteral( "OUTPUT" ), outputPath );
6974
6975 QgsVectorLayer *layerPoints = new QgsVectorLayer( QStringLiteral( "Point" ),
6976 QStringLiteral( "points_for_ts" ),
6977 QStringLiteral( "memory" ) );
6978
6979 std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >();
6980 context->setProject( QgsProject::instance() );
6981 QgsProcessingFeedback feedback;
6982 QVariantMap results;
6983 bool ok = false;
6984
6985 results = alg->run( parameters, *context, &feedback, &ok );
6986 QVERIFY( !ok );
6987
6988 QStringList wktPoints;
6989 wktPoints << QStringLiteral( "Point (1500 2200)" );
6990 wktPoints << QStringLiteral( "Point (1500 1500)" );
6991 wktPoints << QStringLiteral( "Point (2500 2100)" );
6992
6993 QgsFeatureList flist;
6994 for ( const QString &wkt : wktPoints )
6995 {
6996 QgsFeature feat;
6997 feat.setGeometry( QgsGeometry::fromWkt( wkt ) );
6998 flist << feat;
6999 }
7000 layerPoints->dataProvider()->addFeatures( flist );
7001 QgsProject::instance()->addMapLayer( layerPoints ); QgsProject::instance()->addMapLayer( layerPoints );
7002 parameters.insert( QStringLiteral( "INPUT_POINTS" ), layerPoints->name() );
7003
7004 results = alg->run( parameters, *context, &feedback, &ok );
7005 QVERIFY( ok );
7006
7007 QFile outputFile( outputPath );
7008 QVERIFY( outputFile.open( QIODevice::ReadOnly ) );
7009 QTextStream textStream( &outputFile );
7010 QString header = textStream.readLine();
7011 QCOMPARE( header, QStringLiteral( "fid,x,y,time,VertexScalarDataset,VertexVectorDataset,FaceScalarDataset" ) );
7012
7013 QStringList expectedLines;
7014 expectedLines << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:00:00,1.50,1.92,1.00" )
7015 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 01:00:00,2.50,3.33,2.00" )
7016 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:00:00,2.50,2.97,2.00" )
7017 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 01:00:00,3.50,4.36,3.00" );
7018
7019 QString line = textStream.readLine();
7020 int i = 0;
7021 QVERIFY( !line.isEmpty() );
7022 while ( !line.isEmpty() )
7023 {
7024 QCOMPARE( line, expectedLines.at( i ) );
7025 ++i;
7026 line = textStream.readLine();
7027 }
7028 QVERIFY( i == expectedLines.count() );
7029 outputFile.close();
7030
7031 parameters.insert( QStringLiteral( "TIME_STEP" ), 0.1 );
7032
7033 results = alg->run( parameters, *context, &feedback, &ok );
7034 QVERIFY( ok );
7035
7036 QVERIFY( outputFile.open( QIODevice::ReadOnly ) );
7037 header = textStream.readLine();
7038 QCOMPARE( header, QStringLiteral( "fid,x,y,time,VertexScalarDataset,VertexVectorDataset,FaceScalarDataset" ) );
7039
7040 expectedLines.clear();
7041 expectedLines << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:00:00,1.50,1.92,1.00" )
7042 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:06:00,1.50,1.92,1.00" )
7043 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:12:00,1.50,1.92,1.00" )
7044 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:18:00,1.50,1.92,1.00" )
7045 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:24:00,1.50,1.92,1.00" )
7046 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:30:00,1.50,1.92,1.00" )
7047 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:36:00,1.50,1.92,1.00" )
7048 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:42:00,1.50,1.92,1.00" )
7049 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:48:00,1.50,1.92,1.00" )
7050 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:54:00,1.50,1.92,1.00" )
7051 << QStringLiteral( "1,1500.00,2200.00,1950-01-01 01:00:00,2.50,3.33,2.00" )
7052 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:00:00,2.50,2.97,2.00" )
7053 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:06:00,2.50,2.97,2.00" )
7054 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:12:00,2.50,2.97,2.00" )
7055 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:18:00,2.50,2.97,2.00" )
7056 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:24:00,2.50,2.97,2.00" )
7057 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:30:00,2.50,2.97,2.00" )
7058 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:36:00,2.50,2.97,2.00" )
7059 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:42:00,2.50,2.97,2.00" )
7060 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:48:00,2.50,2.97,2.00" )
7061 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:54:00,2.50,2.97,2.00" )
7062 << QStringLiteral( "3,2500.00,2100.00,1950-01-01 01:00:00,3.50,4.36,3.00" );
7063
7064 line = textStream.readLine();
7065 i = 0;
7066 QVERIFY( !line.isEmpty() );
7067 while ( !line.isEmpty() )
7068 {
7069 QCOMPARE( line, expectedLines.at( i ) );
7070 ++i;
7071 line = textStream.readLine();
7072 }
7073 QVERIFY( i == expectedLines.count() );
7074 outputFile.close();
7075 }
7076
7077
imageCheck(const QString & testName,const QString & renderedImage)7078 bool TestQgsProcessingAlgs::imageCheck( const QString &testName, const QString &renderedImage )
7079 {
7080 QgsRenderChecker checker;
7081 checker.setControlPathPrefix( QStringLiteral( "processing_algorithm" ) );
7082 checker.setControlName( "expected_" + testName );
7083 checker.setRenderedImage( renderedImage );
7084 checker.setSizeTolerance( 3, 3 );
7085 const bool equal = checker.compareImages( testName, 500 );
7086 return equal;
7087 }
7088
7089 QGSTEST_MAIN( TestQgsProcessingAlgs )
7090 #include "testqgsprocessingalgs.moc"
7091