QGIS/tests/src/analysis/testqgsprocessingalgs.cpp
2020-11-18 07:10:49 +10:00

5619 lines
236 KiB
C++

/***************************************************************************
testqgsprocessingalgs.cpp
---------------------
begin : November 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "limits"
#include "qgstest.h"
#include "qgsprocessingregistry.h"
#include "qgsprocessingprovider.h"
#include "qgsprocessingutils.h"
#include "qgsprocessingalgorithm.h"
#include "qgsprocessingcontext.h"
#include "qgsprocessingmodelalgorithm.h"
#include "qgsnativealgorithms.h"
#include "qgsalgorithmfillnodata.h"
#include "qgsalgorithmlinedensity.h"
#include "qgsalgorithmimportphotos.h"
#include "qgsalgorithmtransform.h"
#include "qgsalgorithmkmeansclustering.h"
#include "qgsvectorlayer.h"
#include "qgscategorizedsymbolrenderer.h"
#include "qgssinglesymbolrenderer.h"
#include "qgsmultipolygon.h"
#include "qgsrasteranalysisutils.h"
#include "qgsrasterfilewriter.h"
#include "qgsreclassifyutils.h"
#include "qgsalgorithmrasterlogicalop.h"
#include "qgsprintlayout.h"
#include "qgslayoutmanager.h"
#include "qgslayoutitemmap.h"
#include "qgsmarkersymbollayer.h"
#include "qgsrulebasedrenderer.h"
#include "qgspallabeling.h"
#include "qgsrastershader.h"
#include "qgssinglebandpseudocolorrenderer.h"
#include "qgslayoutitemscalebar.h"
#include "annotations/qgstextannotation.h"
#include "qgsfontutils.h"
#include "annotations/qgsannotationmanager.h"
#include "qgsvectorlayerlabeling.h"
#include "qgsstyle.h"
#include "qgsbookmarkmanager.h"
#include "qgsexpressioncontextutils.h"
#include "qgsrenderchecker.h"
#include "qgsrelationmanager.h"
#include "qgsmeshlayer.h"
class TestQgsProcessingAlgs: public QObject
{
Q_OBJECT
private:
/**
* Helper function to get a feature based algorithm.
*/
std::unique_ptr<QgsProcessingFeatureBasedAlgorithm> featureBasedAlg( const QString &id );
QgsFeature runForFeature( const std::unique_ptr<QgsProcessingFeatureBasedAlgorithm> &alg, QgsFeature feature, const QString &layerType, QVariantMap parameters = QVariantMap() );
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase(); // will be called after the last testfunction was executed.
void init() {} // will be called before each testfunction is executed.
void cleanup() {} // will be called after every testfunction.
void saveFeaturesAlg();
void packageAlg();
void renameLayerAlg();
void loadLayerAlg();
void parseGeoTags();
void featureFilterAlg();
void transformAlg();
void kmeansCluster();
void categorizeByStyle();
void extractBinary();
void createDirectory();
void flattenRelations();
void polygonsToLines_data();
void polygonsToLines();
void createConstantRaster_data();
void createConstantRaster();
void densifyGeometries_data();
void densifyGeometries();
void fillNoData_data();
void fillNoData();
void lineDensity_data();
void lineDensity();
void rasterLogicOp_data();
void rasterLogicOp();
void cellStatistics_data();
void cellStatistics();
void rasterFrequencyByComparisonOperator_data();
void rasterFrequencyByComparisonOperator();
void rasterLocalPosition_data();
void rasterLocalPosition();
void roundRasterValues_data();
void roundRasterValues();
void layoutMapExtent();
void styleFromProject();
void combineStyles();
void bookmarksToLayer();
void layerToBookmarks();
void repairShapefile();
void renameField();
void compareDatasets();
void shapefileEncoding();
void setLayerEncoding();
void raiseException();
void raiseWarning();
void randomFloatingPointDistributionRaster_data();
void randomFloatingPointDistributionRaster();
void randomIntegerDistributionRaster_data();
void randomIntegerDistributionRaster();
void randomRaster_data();
void randomRaster();
void filterByLayerType();
void conditionalBranch();
void saveLog();
void setProjectVariable();
#ifndef QT_NO_PRINTER
void exportLayoutPdf();
void exportLayoutPng();
void exportAtlasLayoutPdf();
void exportAtlasLayoutPng();
#endif
void tinMeshCreation();
void exportMeshVertices();
void exportMeshFaces();
void exportMeshEdges();
void exportMeshOnGrid();
void rasterizeMesh();
void exportMeshContours();
void exportMeshCrossSection();
void exportMeshTimeSeries();
private:
bool imageCheck( const QString &testName, const QString &renderedImage );
QString mPointLayerPath;
QgsVectorLayer *mPointsLayer = nullptr;
QgsVectorLayer *mPolygonLayer = nullptr;
};
std::unique_ptr<QgsProcessingFeatureBasedAlgorithm> TestQgsProcessingAlgs::featureBasedAlg( const QString &id )
{
return std::unique_ptr<QgsProcessingFeatureBasedAlgorithm>( static_cast<QgsProcessingFeatureBasedAlgorithm *>( QgsApplication::processingRegistry()->createAlgorithmById( id ) ) );
}
QgsFeature TestQgsProcessingAlgs::runForFeature( const std::unique_ptr< QgsProcessingFeatureBasedAlgorithm > &alg, QgsFeature feature, const QString &layerType, QVariantMap parameters )
{
Q_ASSERT( alg.get() );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QgsProcessingFeedback feedback;
context->setFeedback( &feedback );
std::unique_ptr<QgsVectorLayer> inputLayer( qgis::make_unique<QgsVectorLayer>( layerType, QStringLiteral( "layer" ), QStringLiteral( "memory" ) ) );
inputLayer->dataProvider()->addFeature( feature );
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue<QgsMapLayer *>( inputLayer.get() ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QStringLiteral( "memory:" ) );
bool ok = false;
auto res = alg->run( parameters, *context, &feedback, &ok );
QgsFeature result;
std::unique_ptr<QgsVectorLayer> outputLayer( qobject_cast< QgsVectorLayer * >( context->getMapLayer( res.value( QStringLiteral( "OUTPUT" ) ).toString() ) ) );
outputLayer->getFeatures().nextFeature( result );
return result;
}
void TestQgsProcessingAlgs::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();
// Set up the QgsSettings environment
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
QgsApplication::processingRegistry()->addProvider( new QgsNativeAlgorithms( QgsApplication::processingRegistry() ) );
QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
QString pointsFileName = dataDir + "/points.shp";
QFileInfo pointFileInfo( pointsFileName );
mPointLayerPath = pointFileInfo.filePath();
mPointsLayer = new QgsVectorLayer( mPointLayerPath,
QStringLiteral( "points" ), QStringLiteral( "ogr" ) );
QVERIFY( mPointsLayer->isValid() );
// Register the layer with the registry
QgsProject::instance()->addMapLayers(
QList<QgsMapLayer *>() << mPointsLayer );
//
//create a poly layer that will be used in all tests...
//
QString polysFileName = dataDir + "/polys.shp";
QFileInfo polyFileInfo( polysFileName );
mPolygonLayer = new QgsVectorLayer( polyFileInfo.filePath(),
QStringLiteral( "polygons" ), QStringLiteral( "ogr" ) );
// Register the layer with the registry
QgsProject::instance()->addMapLayers(
QList<QgsMapLayer *>() << mPolygonLayer );
QVERIFY( mPolygonLayer->isValid() );
//add a mesh layer
QString uri( dataDir + "/mesh/quad_and_triangle.2dm" );
QString meshLayerName = QStringLiteral( "mesh layer" );
QgsMeshLayer *meshLayer = new QgsMeshLayer( uri, meshLayerName, QStringLiteral( "mdal" ) );
// Register the layer with the registry
QgsProject::instance()->addMapLayer( meshLayer );
QVERIFY( meshLayer->isValid() );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_vertex_scalar.dat" );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_vertex_vector.dat" );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_els_face_scalar.dat" );
meshLayer->addDatasets( dataDir + "/mesh/quad_and_triangle_els_face_vector.dat" );
QCOMPARE( meshLayer->datasetGroupCount(), 5 );
//add a 1D mesh layer
QString uri1d( dataDir + "/mesh/lines.2dm" );
QString meshLayer1dName = QStringLiteral( "mesh layer 1D" );
QgsMeshLayer *meshLayer1d = new QgsMeshLayer( uri1d, meshLayer1dName, QStringLiteral( "mdal" ) );
// Register the layer with the registry
QgsProject::instance()->addMapLayer( meshLayer1d );
QVERIFY( meshLayer1d->isValid() );
meshLayer1d->addDatasets( dataDir + "/mesh/lines_els_scalar.dat" );
meshLayer1d->addDatasets( dataDir + "/mesh/lines_els_vector.dat" );
QCOMPARE( meshLayer1d->datasetGroupCount(), 3 );
}
void TestQgsProcessingAlgs::cleanupTestCase()
{
QgsApplication::exitQgis();
}
QVariantMap pkgAlg( const QStringList &layers, const QString &outputGpkg, bool overwrite, bool *ok )
{
const QgsProcessingAlgorithm *package( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:package" ) ) );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap parameters;
parameters.insert( QStringLiteral( "LAYERS" ), layers );
parameters.insert( QStringLiteral( "OUTPUT" ), outputGpkg );
parameters.insert( QStringLiteral( "OVERWRITE" ), overwrite );
return package->run( parameters, *context, &feedback, ok );
}
void TestQgsProcessingAlgs::saveFeaturesAlg()
{
QString outputGeoJson = QDir::tempPath() + "/savefeatures_alg.geojson";
QString layerName = QStringLiteral( "custom_layer" );
if ( QFile::exists( outputGeoJson ) )
QFile::remove( outputGeoJson );
QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QString( dataDir + "/points.shp" ) );
parameters.insert( QStringLiteral( "LAYER_NAME" ), layerName );
parameters.insert( QStringLiteral( "LAYER_OPTIONS" ), QStringLiteral( "COORDINATE_PRECISION=1" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), outputGeoJson );
const QgsProcessingAlgorithm *saveFeatures( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:savefeatures" ) ) );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
bool ok = false;
QVariantMap outputs = saveFeatures->run( parameters, *context, &feedback, &ok );
QCOMPARE( ok, true );
QCOMPARE( outputs.value( QStringLiteral( "OUTPUT" ) ).toString(), QStringLiteral( "%1|layername=%2" ).arg( outputGeoJson, layerName ) );
QCOMPARE( outputs.value( QStringLiteral( "FILE_PATH" ) ).toString(), outputGeoJson );
QCOMPARE( outputs.value( QStringLiteral( "LAYER_NAME" ) ).toString(), layerName );
std::unique_ptr< QgsVectorLayer > savedLayer = qgis::make_unique< QgsVectorLayer >( outputs.value( QStringLiteral( "OUTPUT" ) ).toString(), "points", "ogr" );
QVERIFY( savedLayer->isValid() );
QCOMPARE( savedLayer->getFeature( 1 ).geometry().asPoint().x(), -83.3 );
}
void TestQgsProcessingAlgs::packageAlg()
{
QString outputGpkg = QDir::tempPath() + "/package_alg.gpkg";
if ( QFile::exists( outputGpkg ) )
QFile::remove( outputGpkg );
QVariantMap parameters;
QStringList layers = QStringList() << mPointsLayer->id() << mPolygonLayer->id();
bool ok = false;
QVariantMap results = pkgAlg( layers, outputGpkg, true, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
std::unique_ptr< QgsVectorLayer > pointLayer = qgis::make_unique< QgsVectorLayer >( outputGpkg + "|layername=points", "points", "ogr" );
QVERIFY( pointLayer->isValid() );
QCOMPARE( pointLayer->wkbType(), mPointsLayer->wkbType() );
QCOMPARE( pointLayer->featureCount(), mPointsLayer->featureCount() );
pointLayer.reset();
std::unique_ptr< QgsVectorLayer > polygonLayer = qgis::make_unique< QgsVectorLayer >( outputGpkg + "|layername=polygons", "polygons", "ogr" );
QVERIFY( polygonLayer->isValid() );
QCOMPARE( polygonLayer->wkbType(), mPolygonLayer->wkbType() );
QCOMPARE( polygonLayer->featureCount(), mPolygonLayer->featureCount() );
polygonLayer.reset();
std::unique_ptr<QgsVectorLayer> rectangles = qgis::make_unique<QgsVectorLayer>( QStringLiteral( TEST_DATA_DIR ) + "/rectangles.shp",
QStringLiteral( "rectangles" ), QStringLiteral( "ogr" ) );
QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << rectangles.get() );
// Test adding an additional layer (overwrite disabled)
QVariantMap results2 = pkgAlg( QStringList() << rectangles->id(), outputGpkg, false, &ok );
QVERIFY( ok );
QVERIFY( !results2.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
std::unique_ptr< QgsVectorLayer > rectanglesPackagedLayer = qgis::make_unique< QgsVectorLayer >( outputGpkg + "|layername=rectangles", "points", "ogr" );
QVERIFY( rectanglesPackagedLayer->isValid() );
QCOMPARE( rectanglesPackagedLayer->wkbType(), rectanglesPackagedLayer->wkbType() );
QCOMPARE( rectanglesPackagedLayer->featureCount(), rectangles->featureCount() );
rectanglesPackagedLayer.reset();
pointLayer = qgis::make_unique< QgsVectorLayer >( outputGpkg + "|layername=points", "points", "ogr" );
QVERIFY( pointLayer->isValid() );
pointLayer.reset();
// And finally, test with overwrite enabled
QVariantMap results3 = pkgAlg( QStringList() << rectangles->id(), outputGpkg, true, &ok );
QVERIFY( ok );
QVERIFY( !results2.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
rectanglesPackagedLayer = qgis::make_unique< QgsVectorLayer >( outputGpkg + "|layername=rectangles", "points", "ogr" );
QVERIFY( rectanglesPackagedLayer->isValid() );
QCOMPARE( rectanglesPackagedLayer->wkbType(), rectanglesPackagedLayer->wkbType() );
QCOMPARE( rectanglesPackagedLayer->featureCount(), rectangles->featureCount() );
pointLayer = qgis::make_unique< QgsVectorLayer >( outputGpkg + "|layername=points", "points", "ogr" );
QVERIFY( !pointLayer->isValid() ); // It's gone -- the gpkg was recreated with a single layer
}
void TestQgsProcessingAlgs::renameLayerAlg()
{
const QgsProcessingAlgorithm *package( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:renamelayer" ) ) );
QVERIFY( package );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:real" ), QStringLiteral( "layer" ), QStringLiteral( "memory" ) );
QVERIFY( layer->isValid() );
QgsProject::instance()->addMapLayer( layer );
QgsProcessingFeedback feedback;
QVariantMap parameters;
// bad layer
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "bad layer" ) );
parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name" ) );
bool ok = false;
( void )package->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QCOMPARE( layer->name(), QStringLiteral( "layer" ) );
//invalid name
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "layer" ) );
parameters.insert( QStringLiteral( "NAME" ), QString() );
ok = false;
( void )package->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QCOMPARE( layer->name(), QStringLiteral( "layer" ) );
//good params
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( layer ) );
parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name" ) );
ok = false;
QVariantMap results = package->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( layer->name(), QStringLiteral( "new name" ) );
QCOMPARE( results.value( "OUTPUT" ), QVariant::fromValue( layer ) );
// with input layer name as parameter
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "new name" ) );
parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name2" ) );
ok = false;
results = package->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( layer->name(), QStringLiteral( "new name2" ) );
// result should use new name as value
QCOMPARE( results.value( "OUTPUT" ).toString(), QStringLiteral( "new name2" ) );
}
void TestQgsProcessingAlgs::loadLayerAlg()
{
const QgsProcessingAlgorithm *package( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:loadlayer" ) ) );
QVERIFY( package );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QgsProcessingFeedback feedback;
QVariantMap parameters;
// bad layer
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "bad layer" ) );
parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "new name" ) );
bool ok = false;
( void )package->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( context->layersToLoadOnCompletion().empty() );
//invalid name
parameters.insert( QStringLiteral( "INPUT" ), mPointLayerPath );
parameters.insert( QStringLiteral( "NAME" ), QString() );
ok = false;
( void )package->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QVERIFY( context->layersToLoadOnCompletion().empty() );
//good params
parameters.insert( QStringLiteral( "INPUT" ), mPointLayerPath );
parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "my layer" ) );
ok = false;
QVariantMap results = package->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !context->layersToLoadOnCompletion().empty() );
QString layerId = context->layersToLoadOnCompletion().keys().at( 0 );
QCOMPARE( results.value( QStringLiteral( "OUTPUT" ) ).toString(), layerId );
QVERIFY( !layerId.isEmpty() );
QVERIFY( context->temporaryLayerStore()->mapLayer( layerId ) );
QCOMPARE( context->layersToLoadOnCompletion().value( layerId, QgsProcessingContext::LayerDetails( QString(), nullptr, QString() ) ).name, QStringLiteral( "my layer" ) );
QCOMPARE( context->layersToLoadOnCompletion().value( layerId, QgsProcessingContext::LayerDetails( QString(), nullptr, QString() ) ).project, &p );
QCOMPARE( context->layersToLoadOnCompletion().value( layerId, QgsProcessingContext::LayerDetails( QString(), nullptr, QString() ) ).outputName, QStringLiteral( "my layer" ) );
}
void TestQgsProcessingAlgs::parseGeoTags()
{
// parseCoord
QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "" ).isValid() );
QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "x" ).isValid() );
QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "1 2 3" ).isValid() );
QGSCOMPARENEAR( QgsImportPhotosAlgorithm::parseCoord( "(36) (13) (15.21)" ).toDouble(), 36.220892, 0.000001 );
QGSCOMPARENEAR( QgsImportPhotosAlgorithm::parseCoord( "(3) (1) (5.21)" ).toDouble(), 3.018114, 0.000001 );
QGSCOMPARENEAR( QgsImportPhotosAlgorithm::parseCoord( "(149) (7) (54.76)" ).toDouble(), 149.131878, 0.000001 );
QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "(149) (7) (c)" ).isValid() );
QVERIFY( !QgsImportPhotosAlgorithm::parseCoord( "(149) (7) ()" ).isValid() );
// parseMetadataValue
QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "abc" ).toString(), QStringLiteral( "abc" ) );
QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "(abc)" ).toString(), QStringLiteral( "(abc)" ) );
QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "abc (123)" ).toString(), QStringLiteral( "abc (123)" ) );
QCOMPARE( QgsImportPhotosAlgorithm::parseMetadataValue( "(123)" ).toDouble(), 123.0 );
// parseMetadataList
QVariantMap md = QgsImportPhotosAlgorithm::parseMetadataList( QStringList() << "EXIF_Contrast=(1)"
<< "EXIF_ExposureTime=(0.008339)"
<< "EXIF_Model=Pixel"
<< "EXIF_GPSLatitude=(36) (13) (15.21)"
<< "EXIF_GPSLongitude=(149) (7) (54.76)" );
QCOMPARE( md.count(), 5 );
QCOMPARE( md.value( "EXIF_Contrast" ).toInt(), 1 );
QCOMPARE( md.value( "EXIF_ExposureTime" ).toDouble(), 0.008339 );
QCOMPARE( md.value( "EXIF_Model" ).toString(), QStringLiteral( "Pixel" ) );
QGSCOMPARENEAR( md.value( "EXIF_GPSLatitude" ).toDouble(), 36.220892, 0.000001 );
QGSCOMPARENEAR( md.value( "EXIF_GPSLongitude" ).toDouble(), 149.131878, 0.000001 );
// test extractGeoTagFromMetadata
md = QVariantMap();
QgsPointXY point;
QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
md.insert( QStringLiteral( "EXIF_GPSLongitude" ), 142.0 );
QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
md.insert( QStringLiteral( "EXIF_GPSLatitude" ), 37.0 );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitude" ), QStringLiteral( "x" ) );
QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
md.insert( QStringLiteral( "EXIF_GPSLongitude" ), 142.0 );
md.insert( QStringLiteral( "EXIF_GPSLatitude" ), QStringLiteral( "x" ) );
QVERIFY( !QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
md.insert( QStringLiteral( "EXIF_GPSLatitude" ), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "E" ) );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "W" ) );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), -142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "w" ) );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), -142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QStringLiteral( "...W" ) ); // apparently any string ENDING in W is acceptable
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), -142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QString() );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), -1 );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), -142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLongitudeRef" ), QString() );
md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "N" ) );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "S" ) );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), -37.0 );
md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "s" ) );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), -37.0 );
md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QStringLiteral( "...S" ) ); // any string ending in s is acceptable
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), -37.0 );
md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), QString() );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), 37.0 );
md.insert( QStringLiteral( "EXIF_GPSLatitudeRef" ), -1 );
QVERIFY( QgsImportPhotosAlgorithm::extractGeoTagFromMetadata( md, point ) );
QCOMPARE( point.x(), 142.0 );
QCOMPARE( point.y(), -37.0 );
// extractAltitudeFromMetadata
QVERIFY( !QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).isValid() );
md.insert( QStringLiteral( "EXIF_GPSAltitude" ), 10.5 );
QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "0" ) );
QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "00" ) );
QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "1" ) );
QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), -10.5 );
md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), QStringLiteral( "01" ) );
QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), -10.5 );
md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), 1 );
QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), 10.5 );
md.insert( QStringLiteral( "EXIF_GPSAltitudeRef" ), -1 );
QCOMPARE( QgsImportPhotosAlgorithm::extractAltitudeFromMetadata( md ).toDouble(), -10.5 );
// extractDirectionFromMetadata
QVERIFY( !QgsImportPhotosAlgorithm::extractDirectionFromMetadata( md ).isValid() );
md.insert( QStringLiteral( "EXIF_GPSImgDirection" ), 15 );
QCOMPARE( QgsImportPhotosAlgorithm::extractDirectionFromMetadata( md ).toDouble(), 15.0 );
// extractOrientationFromMetadata
QVERIFY( !QgsImportPhotosAlgorithm::extractOrientationFromMetadata( md ).isValid() );
md.insert( QStringLiteral( "EXIF_Orientation" ), 3 );
QCOMPARE( QgsImportPhotosAlgorithm::extractOrientationFromMetadata( md ).toInt(), 180 );
// extractTimestampFromMetadata
QVERIFY( !QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).isValid() );
md.insert( QStringLiteral( "EXIF_DateTimeOriginal" ), QStringLiteral( "xx" ) );
QVERIFY( !QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).isValid() );
md.insert( QStringLiteral( "EXIF_DateTimeOriginal" ), QStringLiteral( "2017:12:27 19:20:52" ) );
QCOMPARE( QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).toDateTime(), QDateTime( QDate( 2017, 12, 27 ), QTime( 19, 20, 52 ) ) );
md.remove( QStringLiteral( "EXIF_DateTimeOriginal" ) );
md.insert( QStringLiteral( "EXIF_DateTimeDigitized" ), QStringLiteral( "2017:12:27 19:20:52" ) );
QCOMPARE( QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).toDateTime(), QDateTime( QDate( 2017, 12, 27 ), QTime( 19, 20, 52 ) ) );
md.remove( QStringLiteral( "EXIF_DateTimeDigitized" ) );
md.insert( QStringLiteral( "EXIF_DateTime" ), QStringLiteral( "2017:12:27 19:20:52" ) );
QCOMPARE( QgsImportPhotosAlgorithm::extractTimestampFromMetadata( md ).toDateTime(), QDateTime( QDate( 2017, 12, 27 ), QTime( 19, 20, 52 ) ) );
}
void TestQgsProcessingAlgs::featureFilterAlg()
{
const QgsProcessingAlgorithm *filterAlgTemplate = QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:filter" ) );
Q_ASSERT( filterAlgTemplate->outputDefinitions().isEmpty() );
QVariantList outputs;
QVariantMap output1;
output1.insert( QStringLiteral( "name" ), QStringLiteral( "test" ) );
output1.insert( QStringLiteral( "expression" ), QStringLiteral( "TRUE" ) );
output1.insert( QStringLiteral( "isModelOutput" ), true );
outputs.append( output1 );
QVariantMap config1;
config1.insert( QStringLiteral( "outputs" ), outputs );
std::unique_ptr<QgsProcessingAlgorithm> filterAlg( filterAlgTemplate->create( config1 ) );
QCOMPARE( filterAlg->outputDefinitions().size(), 1 );
auto outputDef = filterAlg->outputDefinition( QStringLiteral( "OUTPUT_test" ) );
QCOMPARE( outputDef->type(), QStringLiteral( "outputVector" ) );
auto outputParamDef = filterAlg->parameterDefinition( "OUTPUT_test" );
Q_ASSERT( outputParamDef->flags() & QgsProcessingParameterDefinition::FlagIsModelOutput );
Q_ASSERT( outputParamDef->flags() & QgsProcessingParameterDefinition::FlagHidden );
QVariantMap output2;
output2.insert( QStringLiteral( "name" ), QStringLiteral( "nonmodeloutput" ) );
output2.insert( QStringLiteral( "expression" ), QStringLiteral( "TRUE" ) );
output2.insert( QStringLiteral( "isModelOutput" ), false );
outputs.append( output2 );
QVariantMap config2;
config2.insert( QStringLiteral( "outputs" ), outputs );
std::unique_ptr<QgsProcessingAlgorithm> filterAlg2( filterAlgTemplate->create( config2 ) );
QCOMPARE( filterAlg2->outputDefinitions().size(), 2 );
auto outputDef2 = filterAlg2->outputDefinition( QStringLiteral( "OUTPUT_nonmodeloutput" ) );
QCOMPARE( outputDef2->type(), QStringLiteral( "outputVector" ) );
auto outputParamDef2 = filterAlg2->parameterDefinition( "OUTPUT_nonmodeloutput" );
Q_ASSERT( !outputParamDef2->flags().testFlag( QgsProcessingParameterDefinition::FlagIsModelOutput ) );
Q_ASSERT( outputParamDef2->flags() & QgsProcessingParameterDefinition::FlagHidden );
}
void TestQgsProcessingAlgs::transformAlg()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:reprojectlayer" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QgsProcessingFeedback feedback;
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326field=col1:integer" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
QVERIFY( layer->isValid() );
QgsFeature f;
// add a point with a bad geometry - this should result in a transform exception!
f.setGeometry( QgsGeometry::fromPointXY( QgsPointXY( -96215069, 41.673559 ) ) );
QVERIFY( layer->dataProvider()->addFeature( f ) );
p.addMapLayer( layer );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "test" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QStringLiteral( "memory:" ) );
parameters.insert( QStringLiteral( "TARGET_CRS" ), QStringLiteral( "EPSG:2163" ) );
bool ok = false;
QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
Q_UNUSED( results )
QVERIFY( ok );
}
void TestQgsProcessingAlgs::kmeansCluster()
{
// make some features
std::vector< QgsKMeansClusteringAlgorithm::Feature > features;
std::vector< QgsPointXY > centers( 2 );
// no features, no crash
int k = 2;
QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
// features < clusters
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
QCOMPARE( features[ 0 ].cluster, 0 );
// features == clusters
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 11, 5 ) ) );
QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
QCOMPARE( features[ 0 ].cluster, 1 );
QCOMPARE( features[ 1 ].cluster, 0 );
// features > clusters
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 13, 3 ) ) );
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 13, 13 ) ) );
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 23, 6 ) ) );
k = 2;
QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
QCOMPARE( features[ 0 ].cluster, 1 );
QCOMPARE( features[ 1 ].cluster, 1 );
QCOMPARE( features[ 2 ].cluster, 0 );
QCOMPARE( features[ 3 ].cluster, 0 );
QCOMPARE( features[ 4 ].cluster, 0 );
// repeat above, with 3 clusters
k = 3;
centers.resize( 3 );
QgsKMeansClusteringAlgorithm::initClusters( features, centers, k, nullptr );
QgsKMeansClusteringAlgorithm::calculateKMeans( features, centers, k, nullptr );
QCOMPARE( features[ 0 ].cluster, 1 );
QCOMPARE( features[ 1 ].cluster, 2 );
QCOMPARE( features[ 2 ].cluster, 2 );
QCOMPARE( features[ 3 ].cluster, 2 );
QCOMPARE( features[ 4 ].cluster, 0 );
// with identical points
features.clear();
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
features.emplace_back( QgsKMeansClusteringAlgorithm::Feature( QgsPointXY( 1, 5 ) ) );
QCOMPARE( features[ 0 ].cluster, -1 );
QCOMPARE( features[ 1 ].cluster, -1 );
QCOMPARE( features[ 2 ].cluster, -1 );
}
void TestQgsProcessingAlgs::categorizeByStyle()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:categorizeusingstyle" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
QString styleFileName = dataDir + "/categorized.xml";
QgsProcessingFeedback feedback;
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?crs=EPSG:4326&field=col1:string" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
QVERIFY( layer->isValid() );
QgsFeature f, f2, f3;
f.setAttributes( QgsAttributes() << "a" );
QVERIFY( layer->dataProvider()->addFeature( f ) );
f2.setAttributes( QgsAttributes() << "b" );
QVERIFY( layer->dataProvider()->addFeature( f2 ) );
f3.setAttributes( QgsAttributes() << "c " );
QVERIFY( layer->dataProvider()->addFeature( f3 ) );
p.addMapLayer( layer );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "test" ) );
parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "col1" ) );
parameters.insert( QStringLiteral( "STYLE" ), styleFileName );
parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), true );
parameters.insert( QStringLiteral( "TOLERANT" ), false );
parameters.insert( QStringLiteral( "NON_MATCHING_CATEGORIES" ), QStringLiteral( "memory:" ) );
parameters.insert( QStringLiteral( "NON_MATCHING_SYMBOLS" ), QStringLiteral( "memory:" ) );
bool ok = false;
QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
QgsCategorizedSymbolRenderer *catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
QVERIFY( catRenderer );
auto allValues = []( QgsVectorLayer * layer )->QStringList
{
QStringList all;
QgsFeature f;
QgsFeatureIterator it = layer->getFeatures();
while ( it.nextFeature( f ) )
{
all.append( f.attribute( 0 ).toString() );
}
return all;
};
QgsVectorLayer *nonMatchingCats = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
QCOMPARE( allValues( nonMatchingCats ), QStringList() << "b" << "c " );
QgsVectorLayer *nonMatchingSymbols = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
QCOMPARE( allValues( nonMatchingSymbols ), QStringList() << " ----c/- " << "B " );
QCOMPARE( catRenderer->categories().count(), 3 );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name() != QLatin1String( "#00ff00" ) );
QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name() != QLatin1String( "#0000ff" ) );
// reset renderer
layer->setRenderer( new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) ) );
// case insensitive
parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), false );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
QVERIFY( catRenderer );
nonMatchingCats = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
QCOMPARE( allValues( nonMatchingCats ), QStringList() << "c " );
nonMatchingSymbols = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
QCOMPARE( allValues( nonMatchingSymbols ), QStringList() << " ----c/- " );
QCOMPARE( catRenderer->categories().count(), 3 );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name(), QStringLiteral( "#00ff00" ) );
QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name() != QLatin1String( "#0000ff" ) );
// reset renderer
layer->setRenderer( new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) ) );
// tolerant
parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), true );
parameters.insert( QStringLiteral( "TOLERANT" ), true );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
QVERIFY( catRenderer );
nonMatchingCats = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
QCOMPARE( allValues( nonMatchingCats ), QStringList() << "b" );
nonMatchingSymbols = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
QCOMPARE( allValues( nonMatchingSymbols ), QStringList() << "B " );
QCOMPARE( catRenderer->categories().count(), 3 );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
QVERIFY( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name() != QLatin1String( "#00ff00" ) );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name(), QStringLiteral( "#0000ff" ) );
// reset renderer
layer->setRenderer( new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) ) );
// no optional sinks
parameters.insert( QStringLiteral( "CASE_SENSITIVE" ), false );
parameters.remove( QStringLiteral( "NON_MATCHING_CATEGORIES" ) );
parameters.remove( QStringLiteral( "NON_MATCHING_SYMBOLS" ) );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
context->layerToLoadOnCompletionDetails( layer->id() ).postProcessor()->postProcessLayer( layer, *context, &feedback );
catRenderer = dynamic_cast< QgsCategorizedSymbolRenderer * >( layer->renderer() );
QVERIFY( catRenderer );
QVERIFY( !context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_CATEGORIES" ) ).toString() ) );
QVERIFY( !context->getMapLayer( results.value( QStringLiteral( "NON_MATCHING_SYMBOLS" ) ).toString() ) );
QCOMPARE( catRenderer->categories().count(), 3 );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "a" ) ) ).symbol()->color().name(), QStringLiteral( "#ff0000" ) );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "b" ) ) ).symbol()->color().name(), QStringLiteral( "#00ff00" ) );
QCOMPARE( catRenderer->categories().at( catRenderer->categoryIndexForValue( QStringLiteral( "c " ) ) ).symbol()->color().name(), QStringLiteral( "#0000ff" ) );
}
void TestQgsProcessingAlgs::extractBinary()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:extractbinary" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
const QString source = dataDir + QStringLiteral( "/attachments.gdb|layername=points__ATTACH" );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), source );
parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "DATA" ) );
parameters.insert( QStringLiteral( "FILENAME" ), QStringLiteral( "'test' || \"ATTACHMENTID\" || '.jpg'" ) );
const QString folder = QDir::tempPath();
parameters.insert( QStringLiteral( "FOLDER" ), folder );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.count(), 1 );
QCOMPARE( results.value( QStringLiteral( "FOLDER" ) ).toString(), folder );
QFile file( folder + "/test1.jpg" );
QVERIFY( file.open( QIODevice::ReadOnly ) );
QByteArray d = file.readAll();
QCOMPARE( QString( QCryptographicHash::hash( d, QCryptographicHash::Md5 ).toHex() ), QStringLiteral( "ef3dbc530cc39a545832a6c82aac57b6" ) );
QFile file2( folder + "/test2.jpg" );
QVERIFY( file2.open( QIODevice::ReadOnly ) );
d = file2.readAll();
QCOMPARE( QString( QCryptographicHash::hash( d, QCryptographicHash::Md5 ).toHex() ), QStringLiteral( "4b952b80e4288ca5111be2f6dd5d6809" ) );
}
void TestQgsProcessingAlgs::createDirectory()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createdirectory" ) ) );
QVERIFY( alg != nullptr );
// first make a path to an existing file
QString outputPath = QDir::tempPath() + "/test.txt";
if ( QFile::exists( outputPath ) )
QFile::remove( outputPath );
QFile file( outputPath );
file.open( QIODevice::ReadWrite );
file.close();
QVariantMap parameters;
parameters.insert( QStringLiteral( "PATH" ), outputPath );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
// path is an existing file
QVERIFY( !ok );
outputPath = QDir::tempPath() + "/createdir/test/test2";
QDir().rmdir( QDir::tempPath() + "/createdir/test/test2" );
QDir().rmdir( QDir::tempPath() + "/createdir/test" );
QDir().rmdir( QDir::tempPath() + "/createdir" );
parameters.insert( QStringLiteral( "PATH" ), outputPath );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( outputPath ) );
QVERIFY( QFileInfo( outputPath ).isDir() );
// run a second time -- should be OK, no exception raised
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( outputPath ) );
QVERIFY( QFileInfo( outputPath ).isDir() );
}
void TestQgsProcessingAlgs::flattenRelations()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:flattenrelationships" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
QgsProcessingFeedback feedback;
QgsVectorLayer *parent = new QgsVectorLayer( dataDir + QStringLiteral( "/points_relations.shp" ), QStringLiteral( "parent" ) );
QgsVectorLayer *child = new QgsVectorLayer( dataDir + QStringLiteral( "/points.shp" ), QStringLiteral( "child" ) );
QVERIFY( parent->isValid() );
QVERIFY( child->isValid() );
p.addMapLayer( parent );
p.addMapLayer( child );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), parent->id() );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QVariantMap results = alg->run( parameters, *context, &feedback, &ok );
// no relations!
QVERIFY( !ok );
// create relationship
QgsRelationContext relationContext( &p );
QgsRelation relation( relationContext );
relation.setId( QStringLiteral( "rel" ) );
relation.setName( QStringLiteral( "my relation" ) );
relation.setReferencedLayer( parent->id() );
relation.setReferencingLayer( child->id() );
relation.addFieldPair( QStringLiteral( "class" ), QStringLiteral( "class" ) );
QVERIFY( relation.isValid() );
p.relationManager()->addRelation( relation );
results = alg->run( parameters, *context, &feedback, &ok );
// one relation - should be ok!
QVERIFY( ok );
QgsVectorLayer *outputLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( outputLayer );
QCOMPARE( outputLayer->fields().count(), 8 );
QCOMPARE( outputLayer->fields().at( 0 ).name(), QStringLiteral( "Class" ) );
QCOMPARE( outputLayer->fields().at( 1 ).name(), QStringLiteral( "id" ) );
QCOMPARE( outputLayer->fields().at( 2 ).name(), QStringLiteral( "Class_2" ) );
QCOMPARE( outputLayer->fields().at( 3 ).name(), QStringLiteral( "Heading" ) );
QCOMPARE( outputLayer->fields().at( 4 ).name(), QStringLiteral( "Importance" ) );
QCOMPARE( outputLayer->fields().at( 5 ).name(), QStringLiteral( "Pilots" ) );
QCOMPARE( outputLayer->fields().at( 6 ).name(), QStringLiteral( "Cabin Crew" ) );
QCOMPARE( outputLayer->fields().at( 7 ).name(), QStringLiteral( "Staff" ) );
QCOMPARE( outputLayer->featureCount(), 17L );
QSet< QgsAttributes > res;
QgsFeature f;
QgsFeatureIterator it = outputLayer->getFeatures();
while ( it.nextFeature( f ) )
{
QgsAttributes created = f.attributes();
created << f.geometry().asWkt( 0 );
res.insert( created );
}
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 0 << 10.0 << 2 << 1 << 3 << QStringLiteral( "Point (-103 23)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 12 << 10.0 << 1 << 1 << 2 << QStringLiteral( "Point (-103 23)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 34 << 10.0 << 2 << 1 << 3 << QStringLiteral( "Point (-103 23)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "B52" ) << 3 << QStringLiteral( "B52" ) << 80 << 10.0 << 2 << 1 << 3 << QStringLiteral( "Point (-103 23)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 90 << 3.0 << 2 << 0 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 85 << 3.0 << 1 << 1 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 95 << 3.0 << 1 << 1 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 90 << 3.0 << 1 << 0 << 1 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 160 << 4.0 << 2 << 0 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 180 << 3.0 << 1 << 0 << 1 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 140 << 10.0 << 1 << 1 << 2 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Jet" ) << 1 << QStringLiteral( "Jet" ) << 100 << 20.0 << 3 << 0 << 3 << QStringLiteral( "Point (-117 37)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 0 << 1.0 << 3 << 3 << 6 << QStringLiteral( "Point (-83 34)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 340 << 1.0 << 3 << 3 << 6 << QStringLiteral( "Point (-83 34)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 300 << 1.0 << 3 << 2 << 5 << QStringLiteral( "Point (-83 34)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 270 << 1.0 << 3 << 4 << 7 << QStringLiteral( "Point (-83 34)" ) ) );
QVERIFY( res.contains( QgsAttributes() << QStringLiteral( "Biplane" ) << 2 << QStringLiteral( "Biplane" ) << 240 << 1.0 << 3 << 2 << 5 << QStringLiteral( "Point (-83 34)" ) ) );
QgsRelation relation2( relationContext );
relation2.setId( QStringLiteral( "rel2" ) );
relation2.setName( QStringLiteral( "my relation2" ) );
relation2.setReferencedLayer( parent->id() );
relation2.setReferencingLayer( child->id() );
relation2.addFieldPair( QStringLiteral( "class" ), QStringLiteral( "class" ) );
QVERIFY( relation2.isValid() );
p.relationManager()->addRelation( relation2 );
results = alg->run( parameters, *context, &feedback, &ok );
// two relations - should not run
QVERIFY( !ok );
}
void TestQgsProcessingAlgs::polygonsToLines_data()
{
QTest::addColumn<QgsGeometry>( "sourceGeometry" );
QTest::addColumn<QgsGeometry>( "expectedGeometry" );
QTest::newRow( "Simple Polygon" )
<< QgsGeometry::fromWkt( "Polygon((1 1, 2 2, 1 3, 1 1))" )
<< QgsGeometry::fromWkt( "MultiLineString ((1 1, 2 2, 1 3, 1 1))" );
QgsGeometry geomNoRing( qgis::make_unique<QgsMultiPolygon>() );
QTest::newRow( "Polygon without exterior ring" )
<< geomNoRing
<< QgsGeometry::fromWkt( "MultiLineString ()" );
QTest::newRow( "MultiPolygon" )
<< 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)))" )
<< 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))" );
QTest::newRow( "Polygon with inner ring" )
<< QgsGeometry::fromWkt( "Polygon((0 0, 0 10, 10 10, 10 0, 0 0), (3 3, 3 6, 6 6, 6 3, 3 3))" )
<< QgsGeometry::fromWkt( "MultiLineString ((0 0, 0 10, 10 10, 10 0, 0 0),(3 3, 3 6, 6 6, 6 3, 3 3))" );
}
void TestQgsProcessingAlgs::polygonsToLines()
{
QFETCH( QgsGeometry, sourceGeometry );
QFETCH( QgsGeometry, expectedGeometry );
std::unique_ptr< QgsProcessingFeatureBasedAlgorithm > alg( featureBasedAlg( "native:polygonstolines" ) );
QgsFeature feature;
feature.setGeometry( sourceGeometry );
QgsFeature result = runForFeature( alg, feature, QStringLiteral( "Polygon" ) );
QVERIFY2( result.geometry().equals( expectedGeometry ), QStringLiteral( "Result: %1, Expected: %2" ).arg( result.geometry().asWkt(), expectedGeometry.asWkt() ).toUtf8().constData() );
}
Q_DECLARE_METATYPE( Qgis::DataType )
void TestQgsProcessingAlgs::createConstantRaster_data()
{
QTest::addColumn<QString>( "inputExtent" );
QTest::addColumn<QString>( "expectedRaster" );
QTest::addColumn<Qgis::DataType>( "expectedDataType" );
QTest::addColumn<QString>( "crs" );
QTest::addColumn<double>( "pixelSize" );
QTest::addColumn<double>( "constantValue" );
QTest::addColumn<int>( "typeId" );
/*
* Testcase 1
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* Byte Raster Layer
*
*/
QTest::newRow( "testcase 1" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase1.tif" )
<< Qgis::Byte
<< "EPSG:4326"
<< 1.0
<< 12.0
<< 0;
/*
* Testcase 2
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = -1
* Byte Raster Layer
*
*/
QTest::newRow( "testcase 2" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::Byte
<< "EPSG:4326"
<< 1.0
<< -1.0 //fails --> value too small for byte
<< 0;
/*
* Testcase 3
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = -1
* Byte Raster Layer
*
*/
QTest::newRow( "testcase 3" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::Byte
<< "EPSG:4326"
<< 1.0
<< 256.0 //fails --> value too big for byte
<< 0;
/*
* Testcase 4
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* Int16 Raster Layer
*
*/
QTest::newRow( "testcase 4" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase4.tif" )
<< Qgis::Int16
<< "EPSG:4326"
<< 1.0
<< 12.0
<< 1;
/*
* Testcase 5
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* Int16 Raster Layer
*
*/
QTest::newRow( "testcase 5" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::Int16
<< "EPSG:4326"
<< 1.0
<< -32769.0
<< 1;
/*
* Testcase 6
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* Int16 Raster Layer
*
*/
QTest::newRow( "testcase 6" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::Int16
<< "EPSG:4326"
<< 1.0
<< 32769.0
<< 1;
/*
* Testcase 7
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* UInt16 Raster Layer
*
*/
QTest::newRow( "testcase 7" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase7.tif" )
<< Qgis::UInt16
<< "EPSG:4326"
<< 1.0
<< 12.0
<< 2;
/*
* Testcase 8
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* UInt16 Raster Layer
*
*/
QTest::newRow( "testcase 8" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::UInt16
<< "EPSG:4326"
<< 1.0
<< -1.0
<< 2;
/*
* Testcase 9
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* UInt16 Raster Layer
*
*/
QTest::newRow( "testcase 9" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::UInt16
<< "EPSG:4326"
<< 1.0
<< 65536.0
<< 2;
/*
* Testcase 10
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* UInt16 Raster Layer
*
*/
QTest::newRow( "testcase 10" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase10.tif" )
<< Qgis::Int32
<< "EPSG:4326"
<< 1.0
<< 12.0
<< 3;
/*
* Testcase 10
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12
* Int32 Raster Layer
*
*/
QTest::newRow( "testcase 10" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase10.tif" )
<< Qgis::Int32
<< "EPSG:4326"
<< 1.0
<< 12.0
<< 3;
/*
* Testcase 11
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = -2147483649.0
* Int32 Raster Layer
*
*/
QTest::newRow( "testcase 11" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::Int32
<< "EPSG:4326"
<< 1.0
<< -2147483649.0
<< 3;
/*
* Testcase 12
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 2147483649.0
* Int32 Raster Layer
*
*/
QTest::newRow( "testcase 12" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::Int32
<< "EPSG:4326"
<< 1.0
<< 2147483649.0
<< 3;
/*
* Testcase 13
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12.0
* UInt32 Raster Layer
*
*/
QTest::newRow( "testcase 13" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase13.tif" )
<< Qgis::UInt32
<< "EPSG:4326"
<< 1.0
<< 12.0
<< 4;
/*
* Testcase 14
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 4294967296.0
* UInt32 Raster Layer
*
*/
QTest::newRow( "testcase 14" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::UInt32
<< "EPSG:4326"
<< 1.0
<< 4294967296.0
<< 4;
/*
* Testcase 15
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = -1.0
* UInt32 Raster Layer
*
*/
QTest::newRow( "testcase 14" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "" )
<< Qgis::UInt32
<< "EPSG:4326"
<< 1.0
<< -1.0
<< 4;
/*
* Testcase 16
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12.0
* Float32 Raster Layer
*
*/
QTest::newRow( "testcase 16" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase16.tif" )
<< Qgis::Float32
<< "EPSG:4326"
<< 1.0
<< 12.12
<< 5;
/*
* Testcase 17
*
* inputExtent = from "/raster/band1_int16_noct_epsg4326.tif"
* crs = EPSG:4326
* pixelSize = 1.0
* constantValue = 12.0
* Float64 Raster Layer
*
*/
QTest::newRow( "testcase 17" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< QStringLiteral( "/createConstantRaster_testcase17.tif" )
<< Qgis::Float64
<< "EPSG:4326"
<< 1.0
<< 12.125789212532487
<< 6;
}
void TestQgsProcessingAlgs::createConstantRaster()
{
QFETCH( QString, inputExtent );
QFETCH( QString, expectedRaster );
QFETCH( Qgis::DataType, expectedDataType );
QFETCH( QString, crs );
QFETCH( double, pixelSize );
QFETCH( double, constantValue );
QFETCH( int, typeId );
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createconstantrasterlayer" ) ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
//set project crs and ellipsoid from input layer
p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
parameters.insert( QStringLiteral( "NUMBER" ), constantValue );
parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
if ( expectedRaster.isEmpty() )
{
//verify if user feedback for unacceptable values are thrown
alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
}
else
{
//prepare expectedRaster
std::unique_ptr<QgsRasterLayer> expectedRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_constantRaster" + expectedRaster, "expectedDataset", "gdal" );
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
QgsRasterIterator expectedIter( expectedInterface.get() );
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
//run alg...
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
int expectedIterLeft = 0;
int expectedIterTop = 0;
int expectedIterCols = 0;
int expectedIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
{
for ( int row = 0; row < expectedIterRows; row++ )
{
for ( int column = 0; column < expectedIterCols; column++ )
{
double expectedValue = expectedRasterBlock->value( row, column );
double outputValue = outputRasterBlock->value( row, column );
QCOMPARE( outputValue, expectedValue );
}
}
}
}
}
void TestQgsProcessingAlgs::densifyGeometries_data()
{
QTest::addColumn<QgsGeometry>( "sourceGeometry" );
QTest::addColumn<QgsGeometry>( "expectedGeometry" );
QTest::addColumn<double>( "interval" );
QTest::addColumn<QString>( "geometryType" );
QTest::newRow( "Null geometry" )
<< QgsGeometry()
<< QgsGeometry()
<< 0.1
<< "Point";
QTest::newRow( "PointZ" )
<< QgsGeometry::fromWkt( "PointZ( 1 2 3 )" )
<< QgsGeometry::fromWkt( "PointZ( 1 2 3 )" )
<< 0.1
<< "Point";
QTest::newRow( "MultiPoint" )
<< QgsGeometry::fromWkt( "MULTIPOINT ((155 271), (150 360), (260 360), (271 265), (280 260), (270 370), (154 354), (150 260))" )
<< QgsGeometry::fromWkt( "MULTIPOINT ((155 271), (150 360), (260 360), (271 265), (280 260), (270 370), (154 354), (150 260))" )
<< 0.1
<< "Point";
QTest::newRow( "LineString big distance" )
<< QgsGeometry::fromWkt( "LineString( 0 0, 10 0, 10 10 )" )
<< QgsGeometry::fromWkt( "LineString( 0 0, 10 0, 10 10 )" )
<< 100.
<< "LineString";
QTest::newRow( "LineString small distance" )
<< QgsGeometry::fromWkt( "LineString( 0 0, 10 0, 10 10 )" )
<< 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)" )
<< 3.
<< "LineString";
QTest::newRow( "LineStringZ" )
<< QgsGeometry::fromWkt( "LineStringZ( 0 0 1, 10 0 2, 10 10 0)" )
<< QgsGeometry::fromWkt( "LineStringZ (0 0 1, 5 0 1.5, 10 0 2, 10 5 1, 10 10 0)" )
<< 6.
<< "LineString";
QTest::newRow( "LineStringM" )
<< QgsGeometry::fromWkt( "LineStringM( 0 0 0, 10 0 2, 10 10 0)" )
<< 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)" )
<< 3.
<< "LineString";
QTest::newRow( "LineStringZM" )
<< QgsGeometry::fromWkt( "LineStringZM( 0 0 1 10, 10 0 2 8, 10 10 0 4)" )
<< QgsGeometry::fromWkt( "LineStringZM (0 0 1 10, 5 0 1.5 9, 10 0 2 8, 10 5 1 6, 10 10 0 4)" )
<< 6.
<< "LineString";
QTest::newRow( "Polygon" )
<< QgsGeometry::fromWkt( "Polygon(( 0 0, 20 0, 20 20, 0 0 ))" )
<< 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))" )
<< 6.
<< "Polygon";
}
void TestQgsProcessingAlgs::densifyGeometries()
{
QFETCH( QgsGeometry, sourceGeometry );
QFETCH( QgsGeometry, expectedGeometry );
QFETCH( double, interval );
QFETCH( QString, geometryType );
std::unique_ptr< QgsProcessingFeatureBasedAlgorithm > alg( featureBasedAlg( "native:densifygeometriesgivenaninterval" ) );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INTERVAL" ), interval );
QgsFeature feature;
feature.setGeometry( sourceGeometry );
QgsFeature result = runForFeature( alg, feature, geometryType, parameters );
if ( expectedGeometry.isNull() )
QVERIFY( result.geometry().isNull() );
else
QVERIFY2( result.geometry().equals( expectedGeometry ), QStringLiteral( "Result: %1, Expected: %2" ).arg( result.geometry().asWkt(), expectedGeometry.asWkt() ).toUtf8().constData() );
}
void TestQgsProcessingAlgs::fillNoData_data()
{
QTest::addColumn<QString>( "inputRaster" );
QTest::addColumn<QString>( "expectedRaster" );
QTest::addColumn<int>( "inputBand" );
QTest::addColumn<double>( "fillValue" );
/*
* Testcase 1
*
* NoData raster layer
* band = 1
* fillValue = 2.0
*/
QTest::newRow( "testcase 1" )
<< "/raster/band1_int16_noct_epsg4326.tif"
<< QStringLiteral( "/fillnodata_testcase1.tif" )
<< 1
<< 2.0;
/*
* Testcase 2
*
* WGS84 data without weights
* searchRadius = 3
* pixelSize = 1.8
*/
QTest::newRow( "testcase 2" )
<< "/raster/band1_int16_noct_epsg4326.tif"
<< QStringLiteral( "/fillnodata_testcase2.tif" )
<< 1
<< 1.8;
/*
* Testcase 3
*
* WGS84 data without weights
* searchRadius = 3
* pixelSize = 1.8
*/
QTest::newRow( "testcase 2" )
<< "/raster/band1_float32_noct_epsg4326.tif"
<< QStringLiteral( "/fillnodata_testcase3.tif" )
<< 1
<< 1.8;
}
void TestQgsProcessingAlgs::fillNoData()
{
QFETCH( QString, inputRaster );
QFETCH( QString, expectedRaster );
QFETCH( int, inputBand );
QFETCH( double, fillValue );
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:fillnodata" ) ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
std::unique_ptr<QgsRasterLayer> inputRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + inputRaster, "inputDataset", "gdal" );
//set project crs and ellipsoid from input layer
p.setCrs( inputRasterLayer->crs(), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QString( myDataPath + inputRaster ) );
parameters.insert( QStringLiteral( "BAND" ), inputBand );
parameters.insert( QStringLiteral( "FILL_VALUE" ), fillValue );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
//prepare expectedRaster
std::unique_ptr<QgsRasterLayer> expectedRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images/fillNoData/" + expectedRaster, "expectedDataset", "gdal" );
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
QgsRasterIterator expectedIter( expectedInterface.get() );
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
//run alg...
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
int expectedIterLeft = 0;
int expectedIterTop = 0;
int expectedIterCols = 0;
int expectedIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
{
for ( int row = 0; row < expectedIterRows; row++ )
{
for ( int column = 0; column < expectedIterCols; column++ )
{
double expectedValue = expectedRasterBlock->value( row, column );
double outputValue = outputRasterBlock->value( row, column );
QCOMPARE( outputValue, expectedValue );
}
}
}
}
void TestQgsProcessingAlgs::lineDensity_data()
{
QTest::addColumn<QString>( "inputDataset" );
QTest::addColumn<QString>( "expectedDataset" );
QTest::addColumn<double>( "searchRadius" );
QTest::addColumn<double>( "pixelSize" );
QTest::addColumn<QString>( "weightField" );
/*
* Testcase 1
*
* WGS84 data with weights
* searchRadius = 3
* pixelSize = 2
*/
QTest::newRow( "testcase 1" )
<< "/linedensity.gml"
<< QStringLiteral( "/linedensity_testcase1.tif" )
<< 3.0
<< 2.0
<< QStringLiteral( "weight" );
/*
* Testcase 2
*
* WGS84 data without weights
* searchRadius = 3
* pixelSize = 2
*/
QTest::newRow( "testcase_2" )
<< "/linedensity.gml"
<< QStringLiteral( "/linedensity_testcase2.tif" )
<< 3.0
<< 2.0
<< QStringLiteral( "" );
}
void TestQgsProcessingAlgs::lineDensity()
{
QFETCH( QString, inputDataset );
QFETCH( QString, expectedDataset );
QFETCH( double, searchRadius );
QFETCH( double, pixelSize );
QFETCH( QString, weightField );
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:linedensity" ) ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
QgsVectorLayer *layer = new QgsVectorLayer( myDataPath + inputDataset + "|layername=linedensity", QStringLiteral( "layer" ), QStringLiteral( "ogr" ) );
p.addMapLayer( layer );
QVERIFY( layer->isValid() );
//set project crs and ellipsoid from input layer
p.setCrs( layer->crs(), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( layer ) );
parameters.insert( QStringLiteral( "WEIGHT" ), weightField );
parameters.insert( QStringLiteral( "RADIUS" ), searchRadius );
parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
//prepare expectedRaster
std::unique_ptr<QgsRasterLayer> expectedRaster = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_raster_linedensity" + expectedDataset, "expectedDataset", "gdal" );
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRaster->dataProvider()->clone() );
QgsRasterIterator expectedIter( expectedInterface.get() );
expectedIter.startRasterRead( 1, expectedRaster->width(), expectedRaster->height(), expectedInterface->extent() );
//run alg...
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputRaster->width(), expectedRaster->width() );
QCOMPARE( outputRaster->height(), expectedRaster->height() );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
int expectedIterLeft = 0;
int expectedIterTop = 0;
int expectedIterCols = 0;
int expectedIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
{
for ( int row = 0; row < expectedIterRows; row++ )
{
for ( int column = 0; column < expectedIterCols; column++ )
{
double expectedValue = expectedRasterBlock->value( row, column );
double outputValue = outputRasterBlock->value( row, column );
QGSCOMPARENEAR( outputValue, expectedValue, 0.000000000015 );
}
}
}
}
void TestQgsProcessingAlgs::rasterLogicOp_data()
{
QTest::addColumn<QVector< double >>( "input1" );
QTest::addColumn<QVector< double >>( "input2" );
QTest::addColumn<QVector< double >>( "input3" );
QTest::addColumn<bool>( "treatNodataAsFalse" );
QTest::addColumn<qgssize>( "expectedOrNoDataCount" );
QTest::addColumn<qgssize>( "expectedOrTrueCount" );
QTest::addColumn<qgssize>( "expectedOrFalseCount" );
QTest::addColumn<QVector< double >>( "expectedOr" );
QTest::addColumn<qgssize>( "expectedAndNoDataCount" );
QTest::addColumn<qgssize>( "expectedAndTrueCount" );
QTest::addColumn<qgssize>( "expectedAndFalseCount" );
QTest::addColumn<QVector< double >>( "expectedAnd" );
QTest::addColumn<int>( "nRows" );
QTest::addColumn<int>( "nCols" );
QTest::addColumn<double>( "destNoDataValue" );
QTest::addColumn<int>( "dataType" );
QTest::newRow( "no nodata" ) << QVector< double > { 1, 2, 0, 0, 0, 0 }
<< QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > { 1, 2, 0, 0, 0, -1 }
<< false
<< 0ULL << 5ULL << 1ULL
<< QVector< double > { 1, 1, 1, 1, 0, 1 }
<< 0ULL << 1ULL << 5ULL
<< QVector< double > { 1, 0, 0, 0, 0, 0 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "nodata" ) << QVector< double > { 1, -9999, 0, 0, 0, 0 }
<< QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< false
<< 2ULL << 3ULL << 1ULL
<< QVector< double > { 1, -9999, 1, -9999, 0, 1 }
<< 2ULL << 1ULL << 3ULL
<< QVector< double > { 1, -9999, 0, -9999, 0, 0 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "nodata as false" ) << QVector< double > { 1, -9999, 0, 0, 0, 0 }
<< QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< true
<< 0ULL << 5ULL << 1ULL
<< QVector< double > { 1, 1, 1, 1, 0, 1 }
<< 0ULL << 1ULL << 5ULL
<< QVector< double > { 1, 0, 0, 0, 0, 0 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "missing block 1" ) << QVector< double > {}
<< QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< false
<< 6ULL << 0ULL << 0ULL
<< QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
<< 6ULL << 0ULL << 0ULL
<< QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "missing block 1 nodata as false" ) << QVector< double > {}
<< QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< true
<< 0ULL << 5ULL << 1ULL
<< QVector< double > { 1, 1, 1, 1, 0, 1 }
<< 0ULL << 0ULL << 6ULL
<< QVector< double > { 0, 0, 0, 0, 0, 0 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "missing block 2" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > {}
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< false
<< 6ULL << 0ULL << 0ULL
<< QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
<< 6ULL << 0ULL << 0ULL
<< QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "missing block 2 nodata as false" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > {}
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< true
<< 0ULL << 5ULL << 1ULL
<< QVector< double > { 1, 1, 1, 1, 0, 1 }
<< 0ULL << 0ULL << 6ULL
<< QVector< double > { 0, 0, 0, 0, 0, 0 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "missing block 3" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< QVector< double > {}
<< false
<< 6ULL << 0ULL << 0ULL
<< QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
<< 6ULL << 0ULL << 0ULL
<< QVector< double > { -9999, -9999, -9999, -9999, -9999, -9999 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
QTest::newRow( "missing block 3 nodata as false" ) << QVector< double > { 1, 0, 1, 1, 0, 1 }
<< QVector< double > { 1, 2, 0, -9999, 0, -1 }
<< QVector< double > {}
<< true
<< 0ULL << 5ULL << 1ULL
<< QVector< double > { 1, 1, 1, 1, 0, 1 }
<< 0ULL << 0ULL << 6ULL
<< QVector< double > { 0, 0, 0, 0, 0, 0 }
<< 3 << 2
<< -9999.0 << static_cast< int >( Qgis::Float32 );
}
void TestQgsProcessingAlgs::rasterLogicOp()
{
QFETCH( QVector< double >, input1 );
QFETCH( QVector< double >, input2 );
QFETCH( QVector< double >, input3 );
QVector< QVector< double > > input;
input << input1 << input2 << input3;
QFETCH( bool, treatNodataAsFalse );
QFETCH( qgssize, expectedOrNoDataCount );
QFETCH( qgssize, expectedOrTrueCount );
QFETCH( qgssize, expectedOrFalseCount );
QFETCH( QVector< double >, expectedOr );
QFETCH( qgssize, expectedAndNoDataCount );
QFETCH( qgssize, expectedAndTrueCount );
QFETCH( qgssize, expectedAndFalseCount );
QFETCH( QVector< double >, expectedAnd );
QFETCH( int, nRows );
QFETCH( int, nCols );
QFETCH( double, destNoDataValue );
QFETCH( int, dataType );
QgsRasterLogicalOrAlgorithm orAlg;
QgsRasterLogicalAndAlgorithm andAlg;
QgsRectangle extent = QgsRectangle( 0, 0, nRows, nCols );
QgsRectangle badExtent = QgsRectangle( -100, -100, 90, 90 );
QgsCoordinateReferenceSystem crs( QStringLiteral( "EPSG:3857" ) );
double tform[] =
{
extent.xMinimum(), extent.width() / nCols, 0.0,
extent.yMaximum(), 0.0, -extent.height() / nRows
};
std::vector< QgsRasterAnalysisUtils::RasterLogicInput > inputs;
for ( int ii = 0; ii < 3; ++ii )
{
// generate unique filename (need to open the file first to generate it)
QTemporaryFile tmpFile;
tmpFile.open();
tmpFile.close();
// create a GeoTIFF - this will create data provider in editable mode
QString filename = tmpFile.fileName();
std::unique_ptr< QgsRasterFileWriter > writer = qgis::make_unique< QgsRasterFileWriter >( filename );
writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
writer->setOutputFormat( QStringLiteral( "GTiff" ) );
std::unique_ptr<QgsRasterDataProvider > dp( writer->createOneBandRaster( Qgis::Float32, nCols, nRows, input[ii].empty() ? badExtent : extent, crs ) );
QVERIFY( dp->isValid() );
dp->setNoDataValue( 1, -9999 );
std::unique_ptr< QgsRasterBlock > block( dp->block( 1, input[ii].empty() ? badExtent : extent, nCols, nRows ) );
if ( !dp->isEditable() )
{
QVERIFY( dp->setEditable( true ) );
}
int i = 0;
for ( int row = 0; row < nRows; row++ )
{
for ( int col = 0; col < nCols; col++ )
{
if ( !input[ii].empty() )
block->setValue( row, col, input[ii][i++] );
}
}
QVERIFY( dp->writeBlock( block.get(), 1 ) );
QVERIFY( dp->setEditable( false ) );
QgsRasterAnalysisUtils::RasterLogicInput input;
input.sourceDataProvider = std::move( dp );
input.hasNoDataValue = true;
input.interface = input.sourceDataProvider.get();
inputs.emplace_back( std::move( input ) );
}
// make destination OR raster
QTemporaryFile tmpFile2;
tmpFile2.open();
tmpFile2.close();
// create a GeoTIFF - this will create data provider in editable mode
QString filename = tmpFile2.fileName();
std::unique_ptr< QgsRasterDataProvider > dpOr( QgsRasterDataProvider::create( QStringLiteral( "gdal" ), filename, QStringLiteral( "GTiff" ), 1, static_cast< Qgis::DataType >( dataType ), 10, 10, tform, crs ) );
QVERIFY( dpOr->isValid() );
// make destination AND raster
QTemporaryFile tmpFile3;
tmpFile3.open();
tmpFile3.close();
// create a GeoTIFF - this will create data provider in editable mode
filename = tmpFile3.fileName();
std::unique_ptr< QgsRasterDataProvider > dpAnd( QgsRasterDataProvider::create( QStringLiteral( "gdal" ), filename, QStringLiteral( "GTiff" ), 1, static_cast< Qgis::DataType >( dataType ), 10, 10, tform, crs ) );
QVERIFY( dpAnd->isValid() );
QgsFeedback feedback;
qgssize noDataCount = 0;
qgssize trueCount = 0;
qgssize falseCount = 0;
QgsRasterAnalysisUtils::applyRasterLogicOperator( inputs, dpOr.get(), destNoDataValue, treatNodataAsFalse, nCols, nRows,
extent, &feedback, orAlg.mExtractValFunc, noDataCount, trueCount, falseCount );
QCOMPARE( noDataCount, expectedOrNoDataCount );
QCOMPARE( trueCount, expectedOrTrueCount );
QCOMPARE( falseCount, expectedOrFalseCount );
// read back in values
std::unique_ptr< QgsRasterBlock > block( dpOr->block( 1, extent, nCols, nRows ) );
QVector< double > res( nCols * nRows );
int i = 0;
for ( int row = 0; row < nRows; row++ )
{
for ( int col = 0; col < nCols; col++ )
{
res[i++] = block->value( row, col );
}
}
for ( int row = 0; row < nRows; row++ )
{
for ( int col = 0; col < nCols; col++ )
{
QCOMPARE( res[row * nCols + col], expectedOr[row * nCols + col] );
}
}
noDataCount = 0;
trueCount = 0;
falseCount = 0;
QgsRasterAnalysisUtils::applyRasterLogicOperator( inputs, dpAnd.get(), destNoDataValue, treatNodataAsFalse, nCols, nRows,
extent, &feedback, andAlg.mExtractValFunc, noDataCount, trueCount, falseCount );
QCOMPARE( noDataCount, expectedAndNoDataCount );
QCOMPARE( trueCount, expectedAndTrueCount );
QCOMPARE( falseCount, expectedAndFalseCount );
// read back in values
block.reset( dpAnd->block( 1, extent, nCols, nRows ) );
QVector< double > res2( nCols * nRows );
i = 0;
for ( int row = 0; row < nRows; row++ )
{
for ( int col = 0; col < nCols; col++ )
{
res2[i++] = block->value( row, col );
}
}
for ( int row = 0; row < nRows; row++ )
{
for ( int col = 0; col < nCols; col++ )
{
QCOMPARE( res2[row * nCols + col], expectedAnd[row * nCols + col] );
}
}
}
void TestQgsProcessingAlgs::cellStatistics_data()
{
QTest::addColumn<QStringList>( "inputRasters" );
QTest::addColumn<QString>( "referenceLayer" );
QTest::addColumn<int>( "statistic" );
QTest::addColumn<bool>( "ignoreNoData" );
QTest::addColumn<QString>( "expectedRaster" );
QTest::addColumn<Qgis::DataType>( "expectedDataType" );
/*
* Testcase 1: sum
*/
QTest::newRow( "testcase_1" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 0
<< false
<< QStringLiteral( "/cellstatistics_sum_result.tif" )
<< Qgis::Float64;
/*
* Testcase 2: count
*/
QTest::newRow( "testcase_2" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 1
<< false
<< QStringLiteral( "/cellstatistics_count_result.tif" )
<< Qgis::Int32;
/*
* Testcase 3: mean
*/
QTest::newRow( "testcase_3" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 2
<< false
<< QStringLiteral( "/cellstatistics_mean_result.tif" )
<< Qgis::Float64;
/*
* Testcase 4: median
*/
QTest::newRow( "testcase_4" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 3
<< false
<< QStringLiteral( "/cellstatistics_median_result.tif" )
<< Qgis::Float64;
/*
* Testcase 5: Standard deviation
*/
QTest::newRow( "testcase_5" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 4
<< false
<< QStringLiteral( "/cellstatistics_stddev_result.tif" )
<< Qgis::Float64;
/*
* Testcase 6: Variance
*/
QTest::newRow( "testcase_6" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 5
<< false
<< QStringLiteral( "/cellstatistics_variance_result.tif" )
<< Qgis::Float64;
/*
* Testcase 7: Minimum
*/
QTest::newRow( "testcase_7" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 6
<< false
<< QStringLiteral( "/cellstatistics_min_result.tif" )
<< Qgis::Float64;
/*
* Testcase 8: Maximum
*/
QTest::newRow( "testcase_8" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 7
<< false
<< QStringLiteral( "/cellstatistics_max_result.tif" )
<< Qgis::Float64;
/*
* Testcase 9: Minority
*/
QTest::newRow( "testcase_9" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 8
<< false
<< QStringLiteral( "/cellstatistics_minority_result.tif" )
<< Qgis::Float64;
/*
* Testcase 10: Majority
*/
QTest::newRow( "testcase_10" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 9
<< false
<< QStringLiteral( "/cellstatistics_majority_result.tif" )
<< Qgis::Float64;
/*
* Testcase 11: Range
*/
QTest::newRow( "testcase_11" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 10
<< false
<< QStringLiteral( "/cellstatistics_range_result.tif" )
<< Qgis::Float64;
/*
* Testcase 12: Variety
*/
QTest::newRow( "testcase_12" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 11
<< false
<< QStringLiteral( "/cellstatistics_variety_result.tif" )
<< Qgis::Int32;
/*
* Testcase 13: Sum (integer)
*/
QTest::newRow( "testcase_13" )
<< QStringList( {"/raster/statisticsRas1_int32.tif", "/raster/statisticsRas2_int32.tif", "/raster/statisticsRas3_int32.tif"} )
<< QStringLiteral( "/raster/statisticsRas1_int32.tif" )
<< 0
<< false
<< QStringLiteral( "/cellstatistics_sum_result_int32.tif" )
<< Qgis::Int32;
/*
* Testcase 14: sum (ignore nodata)
*/
QTest::newRow( "testcase_14" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 0
<< true
<< QStringLiteral( "/cellstatistics_sum_ignore_nodata_result.tif" )
<< Qgis::Float64;
/*
* Testcase 15: mean
*/
QTest::newRow( "testcase_15" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 2
<< true
<< QStringLiteral( "/cellstatistics_mean_ignore_nodata_result.tif" )
<< Qgis::Float64;
/*
* Testcase 16: Sum (integer)
*/
QTest::newRow( "testcase_16" )
<< QStringList( {"/raster/statisticsRas1_int32.tif", "/raster/statisticsRas2_int32.tif", "/raster/statisticsRas3_int32.tif"} )
<< QStringLiteral( "/raster/statisticsRas1_int32.tif" )
<< 5
<< false
<< QStringLiteral( "/cellstatistics_variance_result_float32.tif" )
<< Qgis::Float32;
/*
* Testcase 17: median with even number of layers
*/
QTest::newRow( "testcase_17" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< 3
<< false
<< QStringLiteral( "/cellstatistics_median_result_fourLayers.tif" )
<< Qgis::Float64;
/*
* Testcase 18: median with even number of layers and integer inputs
*/
QTest::newRow( "testcase_18" )
<< QStringList( {"/raster/statisticsRas1_int32.tif", "/raster/statisticsRas1_int32.tif", "/raster/statisticsRas2_int32.tif", "/raster/statisticsRas3_int32.tif"} )
<< QStringLiteral( "/raster/statisticsRas1_int32.tif" )
<< 3
<< false
<< QStringLiteral( "/cellstatistics_median_result_fourLayers_float32.tif" )
<< Qgis::Float32;
/*
* Testcase 19: sum with raster cell stacks containing only nodata
*/
QTest::newRow( "testcase_19" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas1_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas3_int32.tif" )
<< 0
<< true
<< QStringLiteral( "/cellstatistics_sum_result_ignoreNoData.tif" )
<< Qgis::Float64;
}
void TestQgsProcessingAlgs::cellStatistics()
{
QFETCH( QStringList, inputRasters );
QFETCH( QString, referenceLayer );
QFETCH( int, statistic );
QFETCH( bool, ignoreNoData );
QFETCH( QString, expectedRaster );
QFETCH( Qgis::DataType, expectedDataType );
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:cellstatistics" ) ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
QStringList inputDatasetPaths;
for ( const auto &raster : inputRasters )
{
inputDatasetPaths << myDataPath + raster;
}
std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = qgis::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
//set project crs and ellipsoid from input layer
p.setCrs( inputRasterLayer1->crs(), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), inputDatasetPaths );
parameters.insert( QStringLiteral( "STATISTIC" ), statistic );
parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
parameters.insert( QStringLiteral( "REFERENCE_LAYER" ), QString( myDataPath + referenceLayer ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
//prepare expectedRaster
std::unique_ptr<QgsRasterLayer> expectedRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images/expected_cellStatistics/" + expectedRaster, "expectedDataset", "gdal" );
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
QgsRasterIterator expectedIter( expectedInterface.get() );
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
//run alg...
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
int expectedIterLeft = 0;
int expectedIterTop = 0;
int expectedIterCols = 0;
int expectedIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
{
for ( int row = 0; row < expectedIterRows; row++ )
{
for ( int column = 0; column < expectedIterCols; column++ )
{
double expectedValue = expectedRasterBlock->value( row, column );
double outputValue = outputRasterBlock->value( row, column );
QCOMPARE( outputValue, expectedValue );
}
}
}
}
void TestQgsProcessingAlgs::rasterFrequencyByComparisonOperator_data()
{
QTest::addColumn<QString>( "algName" );
QTest::addColumn<QString>( "inputValueRaster" );
QTest::addColumn<int>( "inputValueRasterBand" );
QTest::addColumn<QStringList>( "inputRasters" );
QTest::addColumn<bool>( "ignoreNoData" );
QTest::addColumn<QString>( "expectedRaster" );
QTest::addColumn<Qgis::DataType>( "expectedDataType" );
/*
* Testcase 1 - equal to frequency: don't ignore NoData
*/
QTest::newRow( "testcase_1" )
<< QStringLiteral( "native:equaltofrequency" )
<< QStringLiteral( "/raster/valueRas1_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest1.tif" )
<< Qgis::Int32;
/*
* Testcase 2 - equal to frequency: ignore NoData
*/
QTest::newRow( "testcase_2" )
<< QStringLiteral( "native:equaltofrequency" )
<< QStringLiteral( "/raster/valueRas1_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< true
<< QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest2.tif" )
<< Qgis::Int32;
/*
* Testcase 3 - equal to frequency: NoData in value raster
*/
QTest::newRow( "testcase_3" )
<< QStringLiteral( "native:equaltofrequency" )
<< QStringLiteral( "/raster/valueRas2_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest3.tif" )
<< Qgis::Int32;
/*
* Testcase 4 - equal to frequency: test with random byte raster
*/
QTest::newRow( "testcase_4" )
<< QStringLiteral( "native:equaltofrequency" )
<< QStringLiteral( "/raster/valueRas3_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_equalToFrequency/equalToFrequencyTest4.tif" )
<< Qgis::Int32;
/*
* Testcase 5 - greater than frequency: don't ignore NoData
*/
QTest::newRow( "testcase_5" )
<< QStringLiteral( "native:greaterthanfrequency" )
<< QStringLiteral( "/raster/valueRas1_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest1.tif" )
<< Qgis::Int32;
/*
* Testcase 6 - greater than frequency: ignore NoData
*/
QTest::newRow( "testcase_6" )
<< QStringLiteral( "native:greaterthanfrequency" )
<< QStringLiteral( "/raster/valueRas1_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< true
<< QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest2.tif" )
<< Qgis::Int32;
/*
* Testcase 7 - greater than frequency: NoData in value raster
*/
QTest::newRow( "testcase_7" )
<< QStringLiteral( "native:greaterthanfrequency" )
<< QStringLiteral( "/raster/valueRas2_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest3.tif" )
<< Qgis::Int32;
/*
* Testcase 8 - greater than frequency: test with random byte raster
*/
QTest::newRow( "testcase_8" )
<< QStringLiteral( "native:greaterthanfrequency" )
<< QStringLiteral( "/raster/valueRas3_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_greaterThanFrequency/greaterThanFrequencyTest4.tif" )
<< Qgis::Int32;
/*
* Testcase 9 - less than frequency: don't ignore NoData
*/
QTest::newRow( "testcase_9" )
<< QStringLiteral( "native:lessthanfrequency" )
<< QStringLiteral( "/raster/valueRas1_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest1.tif" )
<< Qgis::Int32;
/*
* Testcase 10 - greater than frequency: ignore NoData
*/
QTest::newRow( "testcase_10" )
<< QStringLiteral( "native:lessthanfrequency" )
<< QStringLiteral( "/raster/valueRas1_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< true
<< QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest2.tif" )
<< Qgis::Int32;
/*
* Testcase 11 - less than frequency: NoData in value raster
*/
QTest::newRow( "testcase_11" )
<< QStringLiteral( "native:lessthanfrequency" )
<< QStringLiteral( "/raster/valueRas2_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest3.tif" )
<< Qgis::Int32;
/*
* Testcase 12 - less than frequency: test with random byte raster
*/
QTest::newRow( "testcase_12" )
<< QStringLiteral( "native:lessthanfrequency" )
<< QStringLiteral( "/raster/valueRas3_float64.asc" )
<< 1
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< false
<< QStringLiteral( "/expected_lessThanFrequency/lessThanFrequencyTest4.tif" )
<< Qgis::Int32;
}
void TestQgsProcessingAlgs::rasterFrequencyByComparisonOperator()
{
QFETCH( QString, algName );
QFETCH( QString, inputValueRaster );
QFETCH( int, inputValueRasterBand );
QFETCH( QStringList, inputRasters );
QFETCH( bool, ignoreNoData );
QFETCH( QString, expectedRaster );
QFETCH( Qgis::DataType, expectedDataType );
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algName ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
QStringList inputDatasetPaths;
for ( const auto &raster : inputRasters )
{
inputDatasetPaths << myDataPath + raster;
}
std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = qgis::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
//set project crs and ellipsoid from input layer
p.setCrs( inputRasterLayer1->crs(), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT_VALUE_RASTER" ), QString( myDataPath + inputValueRaster ) );
parameters.insert( QStringLiteral( "INPUT_VALUE_RASTER_BAND" ), inputValueRasterBand );
parameters.insert( QStringLiteral( "INPUT_RASTERS" ), inputDatasetPaths );
parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
//prepare expectedRaster
std::unique_ptr<QgsRasterLayer> expectedRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images" + expectedRaster, "expectedDataset", "gdal" );
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
QgsRasterIterator expectedIter( expectedInterface.get() );
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
//run alg...
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
int expectedIterLeft = 0;
int expectedIterTop = 0;
int expectedIterCols = 0;
int expectedIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
{
for ( int row = 0; row < expectedIterRows; row++ )
{
for ( int column = 0; column < expectedIterCols; column++ )
{
double expectedValue = expectedRasterBlock->value( row, column );
double outputValue = outputRasterBlock->value( row, column );
QCOMPARE( outputValue, expectedValue );
Qgis::DataType outputDataType = outputRasterBlock->dataType();
QCOMPARE( outputDataType, expectedDataType );
}
}
}
}
void TestQgsProcessingAlgs::rasterLocalPosition_data()
{
QTest::addColumn<QString>( "algName" );
QTest::addColumn<QStringList>( "inputRasters" );
QTest::addColumn<QString>( "referenceRaster" );
QTest::addColumn<bool>( "ignoreNoData" );
QTest::addColumn<QString>( "expectedRaster" );
QTest::newRow( "testcase_1" )
<< QStringLiteral( "native:lowestpositioninrasterstack" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< false
<< QStringLiteral( "/expected_lowestPosition/expectedLowestPositionTest1.tif" );
QTest::newRow( "testcase_2" )
<< QStringLiteral( "native:lowestpositioninrasterstack" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< true
<< QStringLiteral( "/expected_lowestPosition/expectedLowestPositionTest2.tif" );
QTest::newRow( "testcase_3" )
<< QStringLiteral( "native:lowestpositioninrasterstack" )
<< QStringList( {"/raster/statisticsRas2_float64.asc", "/raster/statisticsRas1_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< false
<< QStringLiteral( "/expected_lowestPosition/expectedLowestPositionTest3.tif" );
QTest::newRow( "testcase_4" )
<< QStringLiteral( "native:highestpositioninrasterstack" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< false
<< QStringLiteral( "/expected_highestPosition/expectedHighestPositionTest1.tif" );
QTest::newRow( "testcase_5" )
<< QStringLiteral( "native:highestpositioninrasterstack" )
<< QStringList( {"/raster/statisticsRas1_float64.asc", "/raster/statisticsRas2_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< true
<< QStringLiteral( "/expected_highestPosition/expectedHighestPositionTest2.tif" );
QTest::newRow( "testcase_6" )
<< QStringLiteral( "native:highestpositioninrasterstack" )
<< QStringList( {"/raster/statisticsRas2_float64.asc", "/raster/statisticsRas1_float64.asc", "/raster/statisticsRas3_float64.asc"} )
<< QStringLiteral( "/raster/statisticsRas1_float64.asc" )
<< false
<< QStringLiteral( "/expected_highestPosition/expectedHighestPositionTest3.tif" );
}
void TestQgsProcessingAlgs::rasterLocalPosition()
{
QFETCH( QString, algName );
QFETCH( QStringList, inputRasters );
QFETCH( QString, referenceRaster );
QFETCH( bool, ignoreNoData );
QFETCH( QString, expectedRaster );
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algName ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
QStringList inputDatasetPaths;
for ( const auto &raster : inputRasters )
{
inputDatasetPaths << myDataPath + raster;
}
std::unique_ptr<QgsRasterLayer> inputRasterLayer1 = qgis::make_unique< QgsRasterLayer >( myDataPath + inputRasters[0], "inputDataset", "gdal" );
//set project crs and ellipsoid from input layer
p.setCrs( inputRasterLayer1->crs(), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT_RASTERS" ), inputDatasetPaths );
parameters.insert( QStringLiteral( "REFERENCE_LAYER" ), QString( myDataPath + referenceRaster ) );
parameters.insert( QStringLiteral( "IGNORE_NODATA" ), ignoreNoData );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
//prepare expectedRaster
std::unique_ptr<QgsRasterLayer> expectedRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images" + expectedRaster, "expectedDataset", "gdal" );
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
QgsRasterIterator expectedIter( expectedInterface.get() );
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
//run alg...
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
int expectedIterLeft = 0;
int expectedIterTop = 0;
int expectedIterCols = 0;
int expectedIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
{
for ( int row = 0; row < expectedIterRows; row++ )
{
for ( int column = 0; column < expectedIterCols; column++ )
{
double expectedValue = expectedRasterBlock->value( row, column );
double outputValue = outputRasterBlock->value( row, column );
QCOMPARE( outputValue, expectedValue );
Qgis::DataType outputDataType = outputRasterBlock->dataType();
QCOMPARE( outputDataType, Qgis::Int32 );
}
}
}
}
void TestQgsProcessingAlgs::roundRasterValues_data()
{
QTest::addColumn<QString>( "inputRaster" );
QTest::addColumn<QString>( "expectedRaster" );
QTest::addColumn<int>( "inputBand" );
QTest::addColumn<int>( "roundingDirection" );
QTest::addColumn<int>( "decimals" );
QTest::addColumn<int>( "baseN" );
/*
* Testcase 1
*
* Integer Raster Layer
* band = 1
* roundingDirection = nearest
* decimals = 2
*/
QTest::newRow( "testcase 1" )
<< "/raster/dem.tif"
<< QStringLiteral( "/roundRasterValues_testcase1.tif" ) //no output expected: can't round integer
<< 1
<< 1
<< 2
<< 10;
/*
* Testcase 2
*
* WGS84 dem
* band = 1
* roundingDirection = up
* decimals = 2
*/
QTest::newRow( "testcase 2" )
<< "/raster/dem.tif"
<< QStringLiteral( "/roundRasterValues_testcase2.tif" )
<< 1
<< 0
<< 2
<< 10;
/*
* Testcase 3
*
* WGS84 dem
* band = 1
* roundingDirection = down
* decimals = 1
*/
QTest::newRow( "testcase 3" )
<< "/raster/dem.tif"
<< QStringLiteral( "/roundRasterValues_testcase3.tif" )
<< 1
<< 2
<< 1
<< 10;
/*
* Testcase 4
*
* WGS84 dem
* band = 1
* roundingDirection = nearest
* decimals = -1
*/
QTest::newRow( "testcase 4" )
<< "/raster/dem.tif"
<< QStringLiteral( "/roundRasterValues_testcase4.tif" )
<< 1
<< 1
<< -1
<< 10;
/*
* Testcase 5
*
* WGS84 dem
* band = 1
* roundingDirection = up
* decimals = -1
*/
QTest::newRow( "testcase 5" )
<< "/raster/dem.tif"
<< QStringLiteral( "/roundRasterValues_testcase5.tif" )
<< 1
<< 0
<< -1
<< 10;
/*
* Testcase 6
*
* WGS84 dem
* band = 1
* roundingDirection = down
* decimals = -1
*/
QTest::newRow( "testcase 6" )
<< "/raster/dem.tif"
<< QStringLiteral( "/roundRasterValues_testcase6.tif" )
<< 1
<< 2
<< -1
<< 10;
/*
* Testcase 7
*
* WGS84 int
* band = 1
* roundingDirection = nearest
* decimals = 2
*/
QTest::newRow( "testcase 7" )
<< "/raster/band1_int16_noct_epsg4326.tif"
<< QStringLiteral( "/roundRasterValues_testcase7.tif" )
<< 1
<< 1
<< -1
<< 10;
/*
* Testcase 8
*
* WGS84 int
* band = 1
* roundingDirection = nearest
* decimals = -1
*/
QTest::newRow( "testcase 8" )
<< "/raster/band1_int16_noct_epsg4326.tif"
<< QStringLiteral( "/roundRasterValues_testcase8.tif" )
<< 1
<< 1
<< -1
<< 10;
}
void TestQgsProcessingAlgs::roundRasterValues()
{
QFETCH( QString, inputRaster );
QFETCH( QString, expectedRaster );
QFETCH( int, inputBand );
QFETCH( int, roundingDirection );
QFETCH( int, decimals );
QFETCH( int, baseN );
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:roundrastervalues" ) ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
std::unique_ptr<QgsRasterLayer> inputRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + inputRaster, "inputDataset", "gdal" );
//set project crs and ellipsoid from input layer
p.setCrs( inputRasterLayer->crs(), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QString( myDataPath + inputRaster ) );
parameters.insert( QStringLiteral( "BAND" ), inputBand );
parameters.insert( QStringLiteral( "ROUNDING_DIRECTION" ), roundingDirection );
parameters.insert( QStringLiteral( "DECIMAL_PLACES" ), decimals );
parameters.insert( QStringLiteral( "BASE_N" ), baseN );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
//prepare expectedRaster
std::unique_ptr<QgsRasterLayer> expectedRasterLayer = qgis::make_unique< QgsRasterLayer >( myDataPath + "/control_images/roundRasterValues/" + expectedRaster, "expectedDataset", "gdal" );
std::unique_ptr< QgsRasterInterface > expectedInterface( expectedRasterLayer->dataProvider()->clone() );
QgsRasterIterator expectedIter( expectedInterface.get() );
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
//run alg...
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
int expectedIterLeft = 0;
int expectedIterTop = 0;
int expectedIterCols = 0;
int expectedIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
std::unique_ptr< QgsRasterBlock > expectedRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) &&
expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
{
for ( int row = 0; row < expectedIterRows; row++ )
{
for ( int column = 0; column < expectedIterCols; column++ )
{
double expectedValue = expectedRasterBlock->value( row, column );
double outputValue = outputRasterBlock->value( row, column );
QCOMPARE( outputValue, expectedValue );
}
}
}
}
void TestQgsProcessingAlgs::layoutMapExtent()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:printlayoutmapextenttolayer" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "l" ) );
parameters.insert( QStringLiteral( "MAP" ), QStringLiteral( "m" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
// no layout
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QgsPrintLayout *layout = new QgsPrintLayout( &p );
layout->setName( QStringLiteral( "l" ) );
p.layoutManager()->addLayout( layout );
// no matching map
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
layout->addLayoutItem( map );
map->setId( QStringLiteral( "m" ) );
map->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) );
map->attemptSetSceneRect( QRectF( 100, 100, 150, 180 ) );
map->zoomToExtent( QgsRectangle( 10000, 100000, 60000, 180000 ) );
map->setMapRotation( 45 );
map->setScale( 10000 );
QgsLayoutItemMap *map2 = new QgsLayoutItemMap( layout );
layout->addLayoutItem( map2 );
map2->setId( QStringLiteral( "m2" ) );
map2->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3785" ) ) );
map2->attemptSetSceneRect( QRectF( 100, 100, 50, 80 ) );
map2->zoomToExtent( QgsRectangle( 10000, 100000, 5000, 8000 ) );
map2->setMapRotation( 0 );
map2->setScale( 1000 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "WIDTH" ) ).toDouble(), 150.0 );
QCOMPARE( results.value( QStringLiteral( "HEIGHT" ) ).toDouble(), 180.0 );
QCOMPARE( results.value( QStringLiteral( "SCALE" ) ).toDouble(), 10000.0 );
QCOMPARE( results.value( QStringLiteral( "ROTATION" ) ).toDouble(), 45.0 );
QgsFeature f;
QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:3111" ) );
QVERIFY( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures().nextFeature( f ) );
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m" ) );
QCOMPARE( f.attribute( 1 ).toDouble(), 150.0 );
QCOMPARE( f.attribute( 2 ).toDouble(), 180.0 );
QCOMPARE( f.attribute( 3 ).toDouble(), 10000.0 );
QCOMPARE( f.attribute( 4 ).toDouble(), 45.0 );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((33833 140106, 34894 141167, 36167 139894, 35106 138833, 33833 140106))" ) );
// all maps
parameters.remove( QStringLiteral( "MAP" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "WIDTH" ) ).isValid() );
QVERIFY( !results.value( QStringLiteral( "HEIGHT" ) ).isValid() );
QVERIFY( !results.value( QStringLiteral( "SCALE" ) ).isValid() );
QVERIFY( !results.value( QStringLiteral( "ROTATION" ) ).isValid() );
QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:3785" ) );
QgsFeatureIterator it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
QgsFeature f1;
QVERIFY( it.nextFeature( f1 ) );
QgsFeature f2;
QVERIFY( it.nextFeature( f2 ) );
f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f1 : f2;
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m" ) );
QCOMPARE( f.attribute( 1 ).toDouble(), 150.0 );
QCOMPARE( f.attribute( 2 ).toDouble(), 180.0 );
QCOMPARE( f.attribute( 3 ).toDouble(), 10000.0 );
QCOMPARE( f.attribute( 4 ).toDouble(), 45.0 );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((12077408 -7108521, 12079627 -7107575, 12080760 -7110245, 12078540 -7111191, 12077408 -7108521))" ) );
f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f2 : f1;
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m2" ) );
QCOMPARE( f.attribute( 1 ).toDouble(), 50.0 );
QCOMPARE( f.attribute( 2 ).toDouble(), 80.0 );
QGSCOMPARENEAR( f.attribute( 3 ).toDouble(), 1000.0, 0.0001 );
QCOMPARE( f.attribute( 4 ).toDouble(), 0.0 );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((7475 54040, 7525 54040, 7525 53960, 7475 53960, 7475 54040))" ) );
// crs override
parameters.insert( QStringLiteral( "CRS" ), QStringLiteral( "EPSG:3111" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "WIDTH" ) ).isValid() );
QVERIFY( !results.value( QStringLiteral( "HEIGHT" ) ).isValid() );
QVERIFY( !results.value( QStringLiteral( "SCALE" ) ).isValid() );
QVERIFY( !results.value( QStringLiteral( "ROTATION" ) ).isValid() );
QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:3111" ) );
it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
QVERIFY( it.nextFeature( f1 ) );
QVERIFY( it.nextFeature( f2 ) );
f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f1 : f2;
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m" ) );
QCOMPARE( f.attribute( 1 ).toDouble(), 150.0 );
QCOMPARE( f.attribute( 2 ).toDouble(), 180.0 );
QCOMPARE( f.attribute( 3 ).toDouble(), 10000.0 );
QCOMPARE( f.attribute( 4 ).toDouble(), 45.0 );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((33833 140106, 34894 141167, 36167 139894, 35106 138833, 33833 140106))" ) );
f = f1.attribute( 0 ).toString() == QLatin1String( "m" ) ? f2 : f1;
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "m2" ) );
QCOMPARE( f.attribute( 1 ).toDouble(), 50.0 );
QCOMPARE( f.attribute( 2 ).toDouble(), 80.0 );
QGSCOMPARENEAR( f.attribute( 3 ).toDouble(), 1000.0, 0.0001 );
QCOMPARE( f.attribute( 4 ).toDouble(), 0.0 );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((-10399464 -5347896, -10399461 -5347835, -10399364 -5347840, -10399367 -5347901, -10399464 -5347896))" ) );
}
void TestQgsProcessingAlgs::styleFromProject()
{
QgsProject p;
QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
QVERIFY( vl->isValid() );
p.addMapLayer( vl );
QgsSimpleMarkerSymbolLayer *simpleMarkerLayer = new QgsSimpleMarkerSymbolLayer();
QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol();
markerSymbol->changeSymbolLayer( 0, simpleMarkerLayer );
vl->setRenderer( new QgsSingleSymbolRenderer( markerSymbol ) );
// rule based renderer
QgsVectorLayer *vl2 = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
QVERIFY( vl2->isValid() );
p.addMapLayer( vl2 );
QgsSymbol *s1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
s1->setColor( QColor( 0, 255, 0 ) );
QgsSymbol *s2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
s2->setColor( QColor( 0, 255, 255 ) );
QgsRuleBasedRenderer::Rule *rootRule = new QgsRuleBasedRenderer::Rule( nullptr );
QgsRuleBasedRenderer::Rule *rule2 = new QgsRuleBasedRenderer::Rule( s1, 0, 0, QStringLiteral( "fld >= 5 and fld <= 20" ) );
rootRule->appendChild( rule2 );
QgsRuleBasedRenderer::Rule *rule3 = new QgsRuleBasedRenderer::Rule( s2, 0, 0, QStringLiteral( "fld <= 10" ) );
rule2->appendChild( rule3 );
vl2->setRenderer( new QgsRuleBasedRenderer( rootRule ) );
// labeling
QgsPalLayerSettings settings;
settings.fieldName = QStringLiteral( "Class" );
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
// raster layer
QgsRasterLayer *rl = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/tenbytenraster.asc",
QStringLiteral( "rl" ) );
QVERIFY( rl->isValid() );
p.addMapLayer( rl );
QgsRasterShader *rasterShader = new QgsRasterShader();
QgsColorRampShader *colorRampShader = new QgsColorRampShader();
colorRampShader->setColorRampType( QgsColorRampShader::Interpolated );
colorRampShader->setSourceColorRamp( new QgsGradientColorRamp( QColor( 255, 255, 0 ), QColor( 255, 0, 255 ) ) );
rasterShader->setRasterShaderFunction( colorRampShader );
QgsSingleBandPseudoColorRenderer *r = new QgsSingleBandPseudoColorRenderer( rl->dataProvider(), 1, rasterShader );
rl->setRenderer( r );
// with layout
QgsPrintLayout *l = new QgsPrintLayout( &p );
l->setName( QStringLiteral( "test layout" ) );
l->initializeDefaults();
QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( l );
scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) );
l->addLayoutItem( scalebar );
scalebar->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) );
p.layoutManager()->addLayout( l );
// with annotations
QgsTextAnnotation *annotation = new QgsTextAnnotation();
QgsSymbol *a1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
a1->setColor( QColor( 0, 200, 0 ) );
annotation->setMarkerSymbol( static_cast< QgsMarkerSymbol * >( a1 ) );
QgsSymbol *a2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry );
a2->setColor( QColor( 200, 200, 0 ) );
annotation->setFillSymbol( static_cast< QgsFillSymbol * >( a2 ) );
p.annotationManager()->addAnnotation( annotation );
// ok, run alg
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:stylefromproject" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "SYMBOLS" ) ).toInt(), 6 );
QCOMPARE( results.value( QStringLiteral( "COLORRAMPS" ) ).toInt(), 1 );
QCOMPARE( results.value( QStringLiteral( "TEXTFORMATS" ) ).toInt(), 1 );
QCOMPARE( results.value( QStringLiteral( "LABELSETTINGS" ) ).toInt(), 1 );
// read style file back in
QgsStyle s;
s.createMemoryDatabase();
QVERIFY( s.importXml( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QCOMPARE( s.symbolCount(), 6 );
QVERIFY( s.symbolNames().contains( QStringLiteral( "Annotation Fill" ) ) );
QVERIFY( s.symbolNames().contains( QStringLiteral( "Annotation Marker" ) ) );
QVERIFY( s.symbolNames().contains( QStringLiteral( "test layout Page" ) ) );
QVERIFY( s.symbolNames().contains( QStringLiteral( "vl" ) ) );
QVERIFY( s.symbolNames().contains( QStringLiteral( "vl2" ) ) );
QVERIFY( s.symbolNames().contains( QStringLiteral( "vl2 (2)" ) ) );
QCOMPARE( s.colorRampCount(), 1 );
QVERIFY( s.colorRampNames().contains( QStringLiteral( "rl" ) ) );
QCOMPARE( s.textFormatCount(), 1 );
QVERIFY( s.textFormatNames().contains( QStringLiteral( "test layout <Scalebar>" ) ) );
QCOMPARE( s.labelSettingsCount(), 1 );
QVERIFY( s.labelSettingsNames().contains( QStringLiteral( "vl" ) ) );
// using a project path
QTemporaryFile tmpFile;
tmpFile.open();
tmpFile.close();
QVERIFY( p.write( tmpFile.fileName() ) );
p.clear();
parameters.insert( QStringLiteral( "INPUT" ), tmpFile.fileName() );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "SYMBOLS" ) ).toInt(), 6 );
// this should be 1, but currently raster layers aren't supported -
// we first need to allow raster renderers to be read and restored for invalid layer sources
QCOMPARE( results.value( QStringLiteral( "COLORRAMPS" ) ).toInt(), 0 );
QCOMPARE( results.value( QStringLiteral( "TEXTFORMATS" ) ).toInt(), 1 );
QCOMPARE( results.value( QStringLiteral( "LABELSETTINGS" ) ).toInt(), 1 );
}
void TestQgsProcessingAlgs::combineStyles()
{
QgsStyle s1;
s1.createMemoryDatabase();
QgsStyle s2;
s2.createMemoryDatabase();
QgsSimpleMarkerSymbolLayer *simpleMarkerLayer = new QgsSimpleMarkerSymbolLayer();
QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol();
markerSymbol->changeSymbolLayer( 0, simpleMarkerLayer );
s1.addSymbol( QStringLiteral( "sym1" ), markerSymbol, true );
s1.tagSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ), QStringList() << QStringLiteral( "t1" ) << QStringLiteral( "t2" ) );
QgsSymbol *sym1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
s2.addSymbol( QStringLiteral( "sym2" ), sym1, true );
QgsSymbol *sym2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
s2.addSymbol( QStringLiteral( "sym1" ), sym2, true );
QgsPalLayerSettings settings;
settings.fieldName = QStringLiteral( "Class" );
s1.addLabelSettings( QStringLiteral( "label1" ), settings, true );
s2.addColorRamp( QStringLiteral( "ramp1" ), new QgsGradientColorRamp( QColor( 255, 255, 0 ), QColor( 255, 0, 255 ) ), true );
s2.addTextFormat( QStringLiteral( "format2" ), QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ), true );
QTemporaryFile tmpFile;
tmpFile.open();
tmpFile.close();
QVERIFY( s1.exportXml( tmpFile.fileName() ) );
QTemporaryFile tmpFile2;
tmpFile2.open();
tmpFile2.close();
QVERIFY( s2.exportXml( tmpFile2.fileName() ) );
// ok, run alg
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:combinestyles" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QStringList() << tmpFile.fileName() << tmpFile2.fileName() );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "SYMBOLS" ) ).toInt(), 3 );
QCOMPARE( results.value( QStringLiteral( "COLORRAMPS" ) ).toInt(), 1 );
QCOMPARE( results.value( QStringLiteral( "TEXTFORMATS" ) ).toInt(), 1 );
QCOMPARE( results.value( QStringLiteral( "LABELSETTINGS" ) ).toInt(), 1 );
// check result
QgsStyle s;
s.createMemoryDatabase();
QVERIFY( s.importXml( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QCOMPARE( s.symbolCount(), 3 );
QVERIFY( s.symbolNames().contains( QStringLiteral( "sym1" ) ) );
QVERIFY( s.symbolNames().contains( QStringLiteral( "sym2" ) ) );
QVERIFY( s.symbolNames().contains( QStringLiteral( "sym1 (2)" ) ) );
QCOMPARE( s.tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ) ).count(), 2 );
QVERIFY( s.tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ) ).contains( QStringLiteral( "t1" ) ) );
QVERIFY( s.tagsOfSymbol( QgsStyle::SymbolEntity, QStringLiteral( "sym1" ) ).contains( QStringLiteral( "t2" ) ) );
QCOMPARE( s.colorRampCount(), 1 );
QVERIFY( s.colorRampNames().contains( QStringLiteral( "ramp1" ) ) );
QCOMPARE( s.textFormatCount(), 1 );
QVERIFY( s.textFormatNames().contains( QStringLiteral( "format2" ) ) );
QCOMPARE( s.labelSettingsCount(), 1 );
QVERIFY( s.labelSettingsNames().contains( QStringLiteral( "label1" ) ) );
}
void TestQgsProcessingAlgs::bookmarksToLayer()
{
QgsApplication::bookmarkManager()->clear();
// create some bookmarks
QgsBookmark b1;
b1.setName( QStringLiteral( "test name" ) );
b1.setGroup( QStringLiteral( "test group" ) );
b1.setExtent( QgsReferencedRectangle( QgsRectangle( 1, 2, 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ) );
QgsApplication::bookmarkManager()->addBookmark( b1 );
QgsBookmark b2;
b2.setName( QStringLiteral( "test name 2" ) );
b2.setExtent( QgsReferencedRectangle( QgsRectangle( 16259461, -2477192, 16391255, -2372535 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) ) );
QgsApplication::bookmarkManager()->addBookmark( b2 );
QgsBookmark b3;
b3.setName( QStringLiteral( "test name 3" ) );
b3.setExtent( QgsReferencedRectangle( QgsRectangle( 11, 21, 31, 41 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ) );
QgsProject p;
p.bookmarkManager()->addBookmark( b3 );
// ok, run alg
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:bookmarkstolayer" ) ) );
QVERIFY( alg != nullptr );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "SOURCE" ), QVariantList() << 0 );
parameters.insert( QStringLiteral( "CRS" ), QStringLiteral( "EPSG:4326" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
// check result
QgsFeature f;
QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:4326" ) );
QgsFeatureIterator it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
QVERIFY( it.nextFeature( f ) );
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 3" ) );
QCOMPARE( f.attribute( 1 ).toString(), QString() );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((11 21, 31 21, 31 41, 11 41, 11 21))" ) );
QVERIFY( !it.nextFeature( f ) );
// user bookmarks
parameters.insert( QStringLiteral( "SOURCE" ), QVariantList() << 1 );
ok = false;
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:bookmarkstolayer" ) ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:4326" ) );
it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
QVERIFY( it.nextFeature( f ) );
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name" ) );
QCOMPARE( f.attribute( 1 ).toString(), QStringLiteral( "test group" ) );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((1 2, 3 2, 3 4, 1 4, 1 2))" ) );
QVERIFY( it.nextFeature( f ) );
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 2" ) );
QCOMPARE( f.attribute( 1 ).toString(), QString() );
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))" ) );
QVERIFY( !it.nextFeature( f ) );
// both
parameters.insert( QStringLiteral( "SOURCE" ), QVariantList() << 0 << 1 );
ok = false;
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:bookmarkstolayer" ) ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() )->crs().authid(), QStringLiteral( "EPSG:4326" ) );
it = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->getFeatures();
QVERIFY( it.nextFeature( f ) );
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 3" ) );
QCOMPARE( f.attribute( 1 ).toString(), QString() );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((11 21, 31 21, 31 41, 11 41, 11 21))" ) );
QVERIFY( it.nextFeature( f ) );
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name" ) );
QCOMPARE( f.attribute( 1 ).toString(), QStringLiteral( "test group" ) );
QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Polygon ((1 2, 3 2, 3 4, 1 4, 1 2))" ) );
QVERIFY( it.nextFeature( f ) );
QCOMPARE( f.attribute( 0 ).toString(), QStringLiteral( "test name 2" ) );
QCOMPARE( f.attribute( 1 ).toString(), QString() );
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))" ) );
QVERIFY( !it.nextFeature( f ) );
}
void TestQgsProcessingAlgs::layerToBookmarks()
{
std::unique_ptr<QgsVectorLayer> inputLayer( qgis::make_unique<QgsVectorLayer>( QStringLiteral( "Polygon?crs=epsg:4326&field=province:string&field=municipality:string" ), QStringLiteral( "layer" ), QStringLiteral( "memory" ) ) );
QVERIFY( inputLayer->isValid() );
QgsFeature f;
f.setAttributes( QgsAttributes() << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((11 21, 31 21, 31 41, 11 41, 11 21))" ) ) );
inputLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << QStringLiteral( "b2" ) << QString() );
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))" ) ) );
inputLayer->dataProvider()->addFeature( f );
QgsApplication::bookmarkManager()->clear();
// run alg
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:layertobookmarks" ) ) );
QVERIFY( alg != nullptr );
QgsProject p;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( inputLayer.get() ) );
parameters.insert( QStringLiteral( "DESTINATION" ), 0 );
parameters.insert( QStringLiteral( "NAME_EXPRESSION" ), QStringLiteral( "upper(province)" ) );
parameters.insert( QStringLiteral( "GROUP_EXPRESSION" ), QStringLiteral( "upper(municipality)" ) );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( p.bookmarkManager()->bookmarks().count(), 2 );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).name(), QStringLiteral( "B1" ) );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).group(), QStringLiteral( "G1" ) );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 0 ).extent().toString( 0 ), QStringLiteral( "11,21 : 31,41" ) );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).name(), QStringLiteral( "B2" ) );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).group(), QString() );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
QCOMPARE( p.bookmarkManager()->bookmarks().at( 1 ).extent().toString( 0 ), QStringLiteral( "146,-22 : 147,-21" ) );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().count(), 0 );
p.bookmarkManager()->clear();
// send to application bookmarks
parameters.insert( QStringLiteral( "DESTINATION" ), 1 );
parameters.insert( QStringLiteral( "GROUP_EXPRESSION" ), QVariant() );
ok = false;
alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:layertobookmarks" ) ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( p.bookmarkManager()->bookmarks().count(), 0 );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().count(), 2 );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).name(), QStringLiteral( "B1" ) );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).group(), QString() );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 0 ).extent().toString( 0 ), QStringLiteral( "11,21 : 31,41" ) );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).name(), QStringLiteral( "B2" ) );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).group(), QString() );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).extent().crs().authid(), QStringLiteral( "EPSG:4326" ) );
QCOMPARE( QgsApplication::bookmarkManager()->bookmarks().at( 1 ).extent().toString( 0 ), QStringLiteral( "146,-22 : 147,-21" ) );
}
void TestQgsProcessingAlgs::repairShapefile()
{
QTemporaryDir tmpPath;
QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.prj" ) ) );
QFile::copy( dataDir + "/points.shp", tmpPath.filePath( QStringLiteral( "points.dbf" ) ) );
// no shx!!
std::unique_ptr< QgsVectorLayer > layer = qgis::make_unique< QgsVectorLayer >( tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
QVERIFY( !layer->isValid() );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:repairshapefile" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "not a file" ) );
bool ok = false;
QgsProcessingFeedback feedback;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
parameters.insert( QStringLiteral( "INPUT" ), tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "OUTPUT" ) ).toString(), tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
layer = qgis::make_unique< QgsVectorLayer >( tmpPath.filePath( QStringLiteral( "points.shp" ) ) );
QVERIFY( layer->isValid() );
}
void TestQgsProcessingAlgs::renameField()
{
QgsProject p;
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
QVERIFY( layer->isValid() );
p.addMapLayer( layer );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:renametablefield" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( layer ) );
parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "doesntexist" ) );
parameters.insert( QStringLiteral( "NEW_NAME" ), QStringLiteral( "newname" ) );
bool ok = false;
QgsProcessingFeedback feedback;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
// field doesn't exist
QVERIFY( !ok );
parameters.insert( QStringLiteral( "FIELD" ), QStringLiteral( "col1" ) );
parameters.insert( QStringLiteral( "NEW_NAME" ), QStringLiteral( "pk" ) );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
// already a field with this name
QVERIFY( !ok );
parameters.insert( QStringLiteral( "NEW_NAME" ), QStringLiteral( "newname" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) )->fields().at( 1 ).name(), QStringLiteral( "newname" ) );
}
void TestQgsProcessingAlgs::compareDatasets()
{
QgsProject p;
QgsVectorLayer *pointLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
QVERIFY( pointLayer->isValid() );
p.addMapLayer( pointLayer );
QgsVectorLayer *originalLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326&field=pk:int&field=col1:string&field=col2:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
QVERIFY( originalLayer->isValid() );
p.addMapLayer( originalLayer );
QgsVectorLayer *revisedLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326&field=pk:int&field=col1:string&field=col2:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
QVERIFY( revisedLayer->isValid() );
p.addMapLayer( revisedLayer );
QgsVectorLayer *differentAttrs = new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326&field=pk:int&field=col3:string&field=col2:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) );
QVERIFY( differentAttrs->isValid() );
p.addMapLayer( differentAttrs );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:detectvectorchanges" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
// differing geometry types - alg should fail
parameters.insert( QStringLiteral( "ORIGINAL" ), QVariant::fromValue( pointLayer ) );
parameters.insert( QStringLiteral( "REVISED" ), QVariant::fromValue( originalLayer ) );
bool ok = false;
QgsProcessingFeedback feedback;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
// differing fields - alg should fail
parameters.insert( QStringLiteral( "ORIGINAL" ), QVariant::fromValue( originalLayer ) );
parameters.insert( QStringLiteral( "REVISED" ), QVariant::fromValue( differentAttrs ) );
parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
// yet if we aren't comparing the field, we shouldn't fail...
parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col2" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
// no features, no outputs
parameters.insert( QStringLiteral( "ORIGINAL" ), QVariant::fromValue( originalLayer ) );
parameters.insert( QStringLiteral( "REVISED" ), QVariant::fromValue( revisedLayer ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 0LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 0LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 0LL );
parameters.insert( QStringLiteral( "UNCHANGED" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "ADDED" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "DELETED" ), QgsProcessing::TEMPORARY_OUTPUT );
// add two features to original
QgsFeature f;
f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
originalLayer->dataProvider()->addFeature( f );
f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
originalLayer->dataProvider()->addFeature( f );
// just compare two columns
parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 0LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "UNCHANGED" ) ).toString() ) )->featureCount(), 0L );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 0LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "ADDED" ) ).toString() ) )->featureCount(), 0L );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "DELETED" ) ).toString() ) )->featureCount(), 2L );
// add one same to revised - note that the first attributes differs here, but we aren't considering that
f.setAttributes( QgsAttributes() << 55 << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
revisedLayer->dataProvider()->addFeature( f );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 1LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "UNCHANGED" ) ).toString() ) )->featureCount(), 1L );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 0LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "ADDED" ) ).toString() ) )->featureCount(), 0L );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "DELETED" ) ).toString() ) )->featureCount(), 1L );
// ok, let's compare the differing attribute too
parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2;pk" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 0LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 1LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 2LL );
parameters.insert( QStringLiteral( "COMPARE_ATTRIBUTES" ), QStringLiteral( "col1;col2" ) );
// similar to the second feature, but geometry differs
f.setAttributes( QgsAttributes() << 55 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 11 0)" ) ) );
revisedLayer->dataProvider()->addFeature( f );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 1LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "UNCHANGED" ) ).toString() ) )->featureCount(), 1L );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 1LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "ADDED" ) ).toString() ) )->featureCount(), 1L );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
QCOMPARE( qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "DELETED" ) ).toString() ) )->featureCount(), 1L );
// note - we skip the featureCount checks after this -- we can be confident at this stage that all sinks are correctly being populated
// add another which is identical to first, must be considered as another "added" feature (i.e.
// don't match to same original feature multiple times)
f.setAttributes( QgsAttributes() << 555 << QStringLiteral( "b1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
revisedLayer->dataProvider()->addFeature( f );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 1LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
// add a match for the second feature
f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 10 0)" ) ) );
revisedLayer->dataProvider()->addFeature( f );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 0LL );
// test topological match (different number of vertices)
f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "c1" ) << QStringLiteral( "g1" ) );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 10, 5 10, 10 10)" ) ) );
originalLayer->dataProvider()->addFeature( f );
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 10, 1 10, 5 10, 10 10)" ) ) );
revisedLayer->dataProvider()->addFeature( f );
// exact match shouldn't equate the two
parameters.insert( QStringLiteral( "MATCH_TYPE" ), 0 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 3LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
// but topological match should
parameters.insert( QStringLiteral( "MATCH_TYPE" ), 1 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 3LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 0LL );
// null geometry comparisons
f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "d1" ) << QStringLiteral( "g1" ) );
f.clearGeometry();
originalLayer->dataProvider()->addFeature( f );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 3LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "e1" ) << QStringLiteral( "g1" ) );
originalLayer->dataProvider()->addFeature( f );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 3LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 2LL );
f.setAttributes( QgsAttributes() << 5 << QStringLiteral( "d1" ) << QStringLiteral( "g1" ) );
revisedLayer->dataProvider()->addFeature( f );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "UNCHANGED_COUNT" ) ).toLongLong(), 4LL );
QCOMPARE( results.value( QStringLiteral( "ADDED_COUNT" ) ).toLongLong(), 2LL );
QCOMPARE( results.value( QStringLiteral( "DELETED_COUNT" ) ).toLongLong(), 1LL );
}
void TestQgsProcessingAlgs::shapefileEncoding()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:shpencodinginfo" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QString() );
bool ok = false;
QgsProcessingFeedback feedback;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
parameters.insert( QStringLiteral( "INPUT" ), QString( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/iso-8859-1.shp" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "ENCODING" ) ).toString(), QStringLiteral( "ISO-8859-1" ) );
QCOMPARE( results.value( QStringLiteral( "CPG_ENCODING" ) ).toString(), QStringLiteral( "ISO-8859-1" ) );
QCOMPARE( results.value( QStringLiteral( "LDID_ENCODING" ) ).toString(), QString() );
parameters.insert( QStringLiteral( "INPUT" ), QString( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/windows-1252_ldid.shp" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "ENCODING" ) ).toString(), QStringLiteral( "CP1252" ) );
QCOMPARE( results.value( QStringLiteral( "CPG_ENCODING" ) ).toString(), QString() );
QCOMPARE( results.value( QStringLiteral( "LDID_ENCODING" ) ).toString(), QStringLiteral( "CP1252" ) );
parameters.insert( QStringLiteral( "INPUT" ), QString( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/system_encoding.shp" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "ENCODING" ) ).toString(), QString() );
QCOMPARE( results.value( QStringLiteral( "CPG_ENCODING" ) ).toString(), QString() );
QCOMPARE( results.value( QStringLiteral( "LDID_ENCODING" ) ).toString(), QString() );
}
void TestQgsProcessingAlgs::setLayerEncoding()
{
QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( TEST_DATA_DIR ) + "/shapefile/system_encoding.shp",
QStringLiteral( "test" ), QStringLiteral( "ogr" ) );
QVERIFY( vl->isValid() );
QgsProject p;
p.addMapLayers(
QList<QgsMapLayer *>() << vl );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:setlayerencoding" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), QString() );
bool ok = false;
QgsProcessingFeedback feedback;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
parameters.insert( QStringLiteral( "INPUT" ), vl->id() );
parameters.insert( QStringLiteral( "ENCODING" ), QStringLiteral( "ISO-8859-1" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( vl->dataProvider()->encoding(), QStringLiteral( "ISO-8859-1" ) );
}
class TestProcessingFeedback : public QgsProcessingFeedback
{
public:
void reportError( const QString &error, bool ) override
{
errors << error;
}
QStringList errors;
};
void TestQgsProcessingAlgs::raiseException()
{
TestProcessingFeedback feedback;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:raiseexception" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "MESSAGE" ), QStringLiteral( "you done screwed up boy" ) );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you done screwed up boy" ) );
parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "FALSE" ) );
feedback.errors.clear();
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( feedback.errors, QStringList() );
parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "TRUE" ) );
feedback.errors.clear();
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you done screwed up boy" ) );
}
void TestQgsProcessingAlgs::raiseWarning()
{
TestProcessingFeedback feedback;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:raisewarning" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "MESSAGE" ), QStringLiteral( "you mighta screwed up boy, but i aint so sure" ) );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you mighta screwed up boy, but i aint so sure" ) );
parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "FALSE" ) );
feedback.errors.clear();
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( feedback.errors, QStringList() );
parameters.insert( QStringLiteral( "CONDITION" ), QStringLiteral( "TRUE" ) );
feedback.errors.clear();
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QCOMPARE( feedback.errors, QStringList() << QStringLiteral( "you mighta screwed up boy, but i aint so sure" ) );
}
void TestQgsProcessingAlgs::randomFloatingPointDistributionRaster_data()
{
QTest::addColumn<QString>( "inputExtent" );
QTest::addColumn<Qgis::DataType>( "expectedDataType" );
QTest::addColumn<bool>( "succeeds" );
QTest::addColumn<QString>( "crs" );
QTest::addColumn<double>( "pixelSize" );
QTest::addColumn<int>( "typeId" );
QTest::newRow( "testcase 1" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Float32
<< true
<< "EPSG:4326"
<< 1.0
<< 0;
QTest::newRow( "testcase 2" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Float64
<< true
<< "EPSG:4326"
<< 1.0
<< 1;
}
void TestQgsProcessingAlgs::randomFloatingPointDistributionRaster()
{
//this test only checks if the correct raster data type is chosen
//value by value comparison is not effective for random rasters
QFETCH( QString, inputExtent );
QFETCH( Qgis::DataType, expectedDataType );
QFETCH( bool, succeeds );
QFETCH( QString, crs );
QFETCH( double, pixelSize );
QFETCH( int, typeId );
//prepare input params
QgsProject p;
//set project crs and ellipsoid from input layer
p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
QStringList alglist = QStringList();
alglist << QStringLiteral( "native:createrandomnormalrasterlayer" )
<< QStringLiteral( "native:createrandomexponentialrasterlayer" )
<< QStringLiteral( "native:createrandomgammarasterlayer" );
for ( int i = 0; i < alglist.length(); i++ )
{
QString algname = alglist[i];
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algname ) );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
if ( !succeeds )
{
//verify if user feedback for unacceptable values are thrown
alg->run( parameters, *context, &feedback, &ok );
QCOMPARE( ok, succeeds );
}
else
{
//run alg...
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
}
}
}
void TestQgsProcessingAlgs::randomIntegerDistributionRaster_data()
{
QTest::addColumn<QString>( "inputExtent" );
QTest::addColumn<Qgis::DataType>( "expectedDataType" );
QTest::addColumn<bool>( "succeeds" );
QTest::addColumn<QString>( "crs" );
QTest::addColumn<double>( "pixelSize" );
QTest::addColumn<int>( "typeId" );
QTest::newRow( "testcase 1" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int16
<< true
<< "EPSG:4326"
<< 1.0
<< 0;
QTest::newRow( "testcase 2" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt16
<< true
<< "EPSG:4326"
<< 1.0
<< 1;
QTest::newRow( "testcase 3" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int32
<< true
<< "EPSG:4326"
<< 1.0
<< 2;
QTest::newRow( "testcase 4" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt32
<< true
<< "EPSG:4326"
<< 1.0
<< 3;
QTest::newRow( "testcase 5" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Float32
<< true
<< "EPSG:4326"
<< 1.0
<< 4;
QTest::newRow( "testcase 6" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Float64
<< true
<< "EPSG:4326"
<< 1.0
<< 5;
}
void TestQgsProcessingAlgs::randomIntegerDistributionRaster()
{
//this test only checks if the correct raster data type is chosen
//value by value comparison is not effective for random rasters
QFETCH( QString, inputExtent );
QFETCH( Qgis::DataType, expectedDataType );
QFETCH( bool, succeeds );
QFETCH( QString, crs );
QFETCH( double, pixelSize );
QFETCH( int, typeId );
//prepare input params
QgsProject p;
//set project crs and ellipsoid from input layer
p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
QStringList alglist = QStringList();
alglist << QStringLiteral( "native:createrandombinomialrasterlayer" )
<< QStringLiteral( "native:createrandomgeometricrasterlayer" )
<< QStringLiteral( "native:createrandomnegativebinomialrasterlayer" )
<< QStringLiteral( "native:createrandompoissonrasterlayer" );
for ( int i = 0; i < alglist.length(); i++ )
{
QString algname = alglist[i];
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( algname ) );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
if ( !succeeds )
{
//verify if user feedback for unacceptable values are thrown
alg->run( parameters, *context, &feedback, &ok );
QCOMPARE( ok, succeeds );
}
else
{
//run alg...
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
}
}
}
void TestQgsProcessingAlgs::randomRaster_data()
{
QTest::addColumn<QString>( "inputExtent" );
QTest::addColumn<Qgis::DataType>( "expectedDataType" );
QTest::addColumn<bool>( "succeeds" );
QTest::addColumn<QString>( "crs" );
QTest::addColumn<double>( "pixelSize" );
QTest::addColumn<double>( "lowerBound" );
QTest::addColumn<double>( "upperBound" );
QTest::addColumn<int>( "typeId" );
QTest::newRow( "testcase 1" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Byte
<< true
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 1.0 //should be min max
<< 0;
QTest::newRow( "testcase 2" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Byte
<< false
<< "EPSG:4326"
<< 1.0
<< -1.0 //fails --> value too small for byte
<< 10.0 //fails --> value too large
<< 0;
QTest::newRow( "testcase 3" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Byte
<< false
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 256.0 //fails --> value too big for byte
<< 0;
QTest::newRow( "testcase 4" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int16
<< true
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 10.0
<< 1;
QTest::newRow( "testcase 5" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int16
<< false
<< "EPSG:4326"
<< 1.0
<< -32769.0
<< -10.0
<< 1;
QTest::newRow( "testcase 6" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int16
<< false
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 32769.0
<< 1;
QTest::newRow( "testcase 7" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt16
<< false
<< "EPSG:4326"
<< 1.0
<< -1.0
<< 12.0
<< 2;
QTest::newRow( "testcase 8" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt16
<< false
<< "EPSG:4326"
<< 1.0
<< 100.0
<< -1.0
<< 2;
QTest::newRow( "testcase 9" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt16
<< false
<< "EPSG:4326"
<< 1.0
<< 0.0
<< 65536.0
<< 2;
QTest::newRow( "testcase 10" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int32
<< true
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 12.0
<< 3;
QTest::newRow( "testcase 10" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int32
<< false
<< "EPSG:4326"
<< 1.0
<< 15.0
<< 12.0
<< 3;
QTest::newRow( "testcase 11" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int32
<< false
<< "EPSG:4326"
<< 1.0
<< -2147483649.0
<< 1.0
<< 3;
QTest::newRow( "testcase 12" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Int32
<< false
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 2147483649.0
<< 3;
QTest::newRow( "testcase 13" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt32
<< true
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 12.0
<< 4;
QTest::newRow( "testcase 14" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt32
<< false
<< "EPSG:4326"
<< 1.0
<< 1.0
<< 4294967296.0
<< 4;
QTest::newRow( "testcase 14" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::UInt32
<< false
<< "EPSG:4326"
<< 1.0
<< -10.0
<< -1.0
<< 4;
QTest::newRow( "testcase 16" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Float32
<< true
<< "EPSG:4326"
<< 1.0
<< 0.0
<< 12.12
<< 5;
QTest::newRow( "testcase 17" )
<< "-3.000000000,7.000000000,-4.000000000,6.000000000 [EPSG:4326]"
<< Qgis::Float64
<< true
<< "EPSG:4326"
<< 1.0
<< -15.0
<< 12.125789212532487
<< 6;
}
void TestQgsProcessingAlgs::randomRaster()
{
QFETCH( QString, inputExtent );
QFETCH( Qgis::DataType, expectedDataType );
QFETCH( bool, succeeds );
QFETCH( QString, crs );
QFETCH( double, pixelSize );
QFETCH( double, lowerBound );
QFETCH( double, upperBound );
QFETCH( int, typeId );
if ( qgsDoubleNear( lowerBound, upperBound ) )
{
//if bounds are the same, use numeric min and max as bounds
switch ( typeId )
{
case 0:
lowerBound = std::numeric_limits<quint8>::min();
upperBound = std::numeric_limits<quint8>::max();
break;
case 1:
lowerBound = std::numeric_limits<qint16>::min();
upperBound = std::numeric_limits<qint16>::max();
break;
case 2:
lowerBound = std::numeric_limits<quint16>::min();
upperBound = std::numeric_limits<quint16>::max();
break;
case 3:
lowerBound = std::numeric_limits<qint32>::min();
upperBound = std::numeric_limits<qint32>::max();
break;
case 4:
lowerBound = std::numeric_limits<quint32>::min();
upperBound = std::numeric_limits<quint32>::max();
break;
case 5:
lowerBound = std::numeric_limits<float>::min();
upperBound = std::numeric_limits<float>::max();
break;
case 6:
lowerBound = std::numeric_limits<double>::min();
upperBound = std::numeric_limits<double>::max();
break;
}
}
//prepare input params
QgsProject p;
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createrandomuniformrasterlayer" ) ) );
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
//set project crs and ellipsoid from input layer
p.setCrs( QgsCoordinateReferenceSystem( crs ), true );
//set project after layer has been added so that transform context/ellipsoid from crs is also set
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QVariantMap parameters;
parameters.insert( QStringLiteral( "EXTENT" ), inputExtent );
parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( crs ) );
parameters.insert( QStringLiteral( "PIXEL_SIZE" ), pixelSize );
parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), typeId );
parameters.insert( QStringLiteral( "LOWER_BOUND" ), lowerBound );
parameters.insert( QStringLiteral( "UPPER_BOUND" ), upperBound );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
QgsProcessingFeedback feedback;
QVariantMap results;
if ( !succeeds )
{
//verify if user feedback for unacceptable values are thrown
alg->run( parameters, *context, &feedback, &ok );
QCOMPARE( ok, succeeds );
}
else
{
//run alg...
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
//...and check results with expected datasets
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
std::unique_ptr< QgsRasterInterface > outputInterface( outputRaster->dataProvider()->clone() );
QCOMPARE( outputInterface->dataType( 1 ), expectedDataType );
QgsRasterIterator outputIter( outputInterface.get() );
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
int outputIterLeft = 0;
int outputIterTop = 0;
int outputIterCols = 0;
int outputIterRows = 0;
std::unique_ptr< QgsRasterBlock > outputRasterBlock;
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) )
{
for ( int row = 0; row < outputIterRows; row++ )
{
for ( int column = 0; column < outputIterCols; column++ )
{
double outputValue = outputRasterBlock->value( row, column );
//check if random values are in range
QVERIFY( outputValue >= lowerBound && outputValue <= upperBound );
}
}
}
}
}
void TestQgsProcessingAlgs::filterByLayerType()
{
QgsProject p;
QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
QVERIFY( vl->isValid() );
p.addMapLayer( vl );
// raster layer
QgsRasterLayer *rl = new QgsRasterLayer( QStringLiteral( TEST_DATA_DIR ) + "/tenbytenraster.asc", QStringLiteral( "rl" ) );
QVERIFY( rl->isValid() );
p.addMapLayer( rl );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:filterlayersbytype" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
// vector input
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "vl" ) );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "VECTOR" ) ).toString().isEmpty() );
QVERIFY( !results.contains( QStringLiteral( "RASTER" ) ) );
// raster input
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "rl" ) );
ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "RASTER" ) ).toString().isEmpty() );
QVERIFY( !results.contains( QStringLiteral( "VECTOR" ) ) );
}
void TestQgsProcessingAlgs::conditionalBranch()
{
QVariantMap config;
QVariantList conditions;
QVariantMap cond1;
cond1.insert( QStringLiteral( "name" ), QStringLiteral( "name1" ) );
cond1.insert( QStringLiteral( "expression" ), QStringLiteral( "1 * 1" ) );
conditions << cond1;
QVariantMap cond2;
cond2.insert( QStringLiteral( "name" ), QStringLiteral( "name2" ) );
cond2.insert( QStringLiteral( "expression" ), QStringLiteral( "1 * 0" ) );
conditions << cond2;
config.insert( QStringLiteral( "conditions" ), conditions );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:condition" ), config ) );
QVERIFY( alg != nullptr );
QCOMPARE( alg->outputDefinitions().size(), 2 );
QCOMPARE( alg->outputDefinitions().at( 0 )->name(), QStringLiteral( "name1" ) );
QCOMPARE( alg->outputDefinitions().at( 1 )->name(), QStringLiteral( "name2" ) );
QVariantMap parameters;
// vector input
parameters.insert( QStringLiteral( "INPUT" ), QStringLiteral( "vl" ) );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok, config );
QVERIFY( ok );
QCOMPARE( results.value( QStringLiteral( "name1" ) ).toInt(), 1 );
QCOMPARE( results.value( QStringLiteral( "name2" ) ).toInt(), 0 );
}
void TestQgsProcessingAlgs::saveLog()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:savelog" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProcessingFeedback feedback;
feedback.reportError( QStringLiteral( "test" ) );
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
QFile file( results.value( QStringLiteral( "OUTPUT" ) ).toString() );
QVERIFY( file.open( QFile::ReadOnly | QIODevice::Text ) );
QCOMPARE( QString( file.readAll() ), QStringLiteral( "test\n" ) );
parameters.insert( QStringLiteral( "USE_HTML" ), true );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( !results.value( QStringLiteral( "OUTPUT" ) ).toString().isEmpty() );
QFile file2( results.value( QStringLiteral( "OUTPUT" ) ).toString() );
QVERIFY( file2.open( QFile::ReadOnly | QIODevice::Text ) );
QCOMPARE( QString( file2.readAll() ), QStringLiteral( "<span style=\"color:red\">test</span><br/>" ) );
}
void TestQgsProcessingAlgs::setProjectVariable()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:setprojectvariable" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "NAME" ), QStringLiteral( "my_var" ) );
parameters.insert( QStringLiteral( "VALUE" ), 11 );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
QgsProject p;
context->setProject( &p );
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::projectScope( &p ) );
QCOMPARE( scope->variable( QStringLiteral( "my_var" ) ).toInt(), 11 );
//overwrite existing
parameters.insert( QStringLiteral( "VALUE" ), 13 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
scope.reset( QgsExpressionContextUtils::projectScope( &p ) );
QCOMPARE( scope->variable( QStringLiteral( "my_var" ) ).toInt(), 13 );
}
#ifndef QT_NO_PRINTER
void TestQgsProcessingAlgs::exportLayoutPdf()
{
QgsProject p;
QgsPrintLayout *layout = new QgsPrintLayout( &p );
layout->initializeDefaults();
layout->setName( QStringLiteral( "my layout" ) );
p.layoutManager()->addLayout( layout );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:printlayouttopdf" ) ) );
QVERIFY( alg != nullptr );
const QString outputPdf = QDir::tempPath() + "/my_layout.pdf";
if ( QFile::exists( outputPdf ) )
QFile::remove( outputPdf );
QVariantMap parameters;
parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "missing" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), outputPdf );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
// invalid layout name
QVERIFY( !ok );
QVERIFY( !QFile::exists( outputPdf ) );
parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( outputPdf ) );
}
void TestQgsProcessingAlgs::exportLayoutPng()
{
QgsProject p;
QgsPrintLayout *layout = new QgsPrintLayout( &p );
layout->initializeDefaults();
layout->setName( QStringLiteral( "my layout" ) );
QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
map->setBackgroundEnabled( false );
map->setFrameEnabled( false );
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
layout->addLayoutItem( map );
map->setExtent( mPointsLayer->extent() );
p.layoutManager()->addLayout( layout );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:printlayouttoimage" ) ) );
QVERIFY( alg != nullptr );
QString outputPng = QDir::tempPath() + "/my_layout.png";
if ( QFile::exists( outputPng ) )
QFile::remove( outputPng );
QVariantMap parameters;
parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "missing" ) );
parameters.insert( QStringLiteral( "OUTPUT" ), outputPng );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
// invalid layout name
QVERIFY( !ok );
QVERIFY( !QFile::exists( outputPng ) );
parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( outputPng ) );
outputPng = QDir::tempPath() + "/my_layout_custom_layers.png";
if ( QFile::exists( outputPng ) )
QFile::remove( outputPng );
parameters.insert( QStringLiteral( "OUTPUT" ), outputPng );
parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( mPointsLayer ) );
parameters.insert( QStringLiteral( "DPI" ), 96 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( outputPng ) );
QVERIFY( imageCheck( "export_layout_custom_layers", outputPng ) );
}
void TestQgsProcessingAlgs::exportAtlasLayoutPdf()
{
QgsMapLayer *polygonLayer = mPolygonLayer->clone();
QgsProject p;
p.addMapLayers( QList<QgsMapLayer *>() << polygonLayer );
QgsPrintLayout *layout = new QgsPrintLayout( &p );
layout->initializeDefaults();
layout->setName( QStringLiteral( "my layout" ) );
QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
map->setBackgroundEnabled( false );
map->setFrameEnabled( false );
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
layout->addLayoutItem( map );
map->setExtent( polygonLayer->extent() );
p.layoutManager()->addLayout( layout );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:atlaslayouttopdf" ) ) );
QVERIFY( alg != nullptr );
const QString outputPdf = QDir::tempPath() + "/my_atlas_layout.pdf";
if ( QFile::exists( outputPdf ) )
QFile::remove( outputPdf );
QVariantMap parameters;
parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
parameters.insert( QStringLiteral( "COVERAGE_LAYER" ), QVariant::fromValue( polygonLayer ) );
parameters.insert( QStringLiteral( "OUTPUT" ), outputPdf );
parameters.insert( QStringLiteral( "DPI" ), 96 );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( outputPdf ) );
}
void TestQgsProcessingAlgs::exportAtlasLayoutPng()
{
QgsMapLayer *polygonLayer = mPolygonLayer->clone();
QgsProject p;
p.addMapLayers( QList<QgsMapLayer *>() << polygonLayer );
QgsPrintLayout *layout = new QgsPrintLayout( &p );
layout->initializeDefaults();
layout->setName( QStringLiteral( "my layout" ) );
QgsLayoutItemMap *map = new QgsLayoutItemMap( layout );
map->setBackgroundEnabled( false );
map->setFrameEnabled( false );
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
layout->addLayoutItem( map );
map->setExtent( polygonLayer->extent() );
p.layoutManager()->addLayout( layout );
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:atlaslayouttoimage" ) ) );
QVERIFY( alg != nullptr );
QDir tempDir( QDir::tempPath() );
if ( !tempDir.mkdir( "my_atlas" ) )
{
QDir dir( QDir::tempPath() + "/my_atlas" );
const QStringList files = dir.entryList( QStringList() << "*.*", QDir::Files );
for ( const QString &file : files )
QFile::remove( QDir::tempPath() + "/my_atlas/" + file );
}
QVariantMap parameters;
parameters.insert( QStringLiteral( "LAYOUT" ), QStringLiteral( "my layout" ) );
parameters.insert( QStringLiteral( "COVERAGE_LAYER" ), QVariant::fromValue( polygonLayer ) );
parameters.insert( QStringLiteral( "FOLDER" ), QString( QDir::tempPath() + "/my_atlas" ) );
parameters.insert( QStringLiteral( "FILENAME_EXPRESSION" ), QStringLiteral( "'export_'||@atlas_featurenumber" ) );
parameters.insert( QStringLiteral( "DPI" ), 96 );
bool ok = false;
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( &p );
QgsProcessingFeedback feedback;
QVariantMap results;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/export_1.png" ) );
QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/export_10.png" ) );
QVERIFY( imageCheck( "export_atlas", QDir::tempPath() + "/my_atlas/export_1.png" ) );
parameters[QStringLiteral( "FILENAME_EXPRESSION" )] = QStringLiteral( "'custom_'||@atlas_featurenumber" );
parameters.insert( QStringLiteral( "LAYERS" ), QVariantList() << QVariant::fromValue( mPointsLayer ) );
ok = false;
results.clear();
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/custom_1.png" ) );
QVERIFY( QFile::exists( QDir::tempPath() + "/my_atlas/custom_10.png" ) );
QVERIFY( imageCheck( "export_atlas_custom_layers", QDir::tempPath() + "/my_atlas/custom_1.png" ) );
}
#endif
void TestQgsProcessingAlgs::tinMeshCreation()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:tinmeshcreation" ) ) );
QVERIFY( alg != nullptr );
QVariantList inputLayers;
QVariantMap pointLayer;
pointLayer[QStringLiteral( "source" )] = "points";
pointLayer[QStringLiteral( "type" )] = 0;
pointLayer[QStringLiteral( "attributeIndex" )] = mPointsLayer->fields().indexOf( "Importance" );
inputLayers.append( pointLayer );
QVariantMap polyLayer;
polyLayer[QStringLiteral( "source" )] = "polygons";
polyLayer[QStringLiteral( "type" )] = 2;
polyLayer[QStringLiteral( "attributeIndex" )] = mPolygonLayer->fields().indexOf( "Value" );
inputLayers.append( polyLayer );
QVariantMap parameters;
parameters.insert( QStringLiteral( "SOURCE_DATA" ), inputLayers );
parameters.insert( QStringLiteral( "OUTPUT_MESH" ), QString( QDir::tempPath() + "/meshLayer.2dm" ) );
parameters.insert( QStringLiteral( "MESH_FORMAT" ), 0 );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QgsMeshLayer meshLayer( QDir::tempPath() + "/meshLayer.2dm", "mesh", "mdal" );
QVERIFY( meshLayer.isValid() );
QgsMeshDataProvider *provider = meshLayer.dataProvider();
QCOMPARE( provider->vertexCount(), 627 );
QCOMPARE( provider->faceCount(), 1218 );
meshLayer.updateTriangularMesh();
QVERIFY( qgsDoubleNear( meshLayer.datasetValue( QgsMeshDatasetIndex( 0, 0 ), QgsPointXY( -103.0, 39.0 ) ).scalar(), 20.0, 0.001 ) );
QVERIFY( qgsDoubleNear( meshLayer.datasetValue( QgsMeshDatasetIndex( 0, 0 ), QgsPointXY( -86.0, 35.0 ) ).scalar(), 1.855, 0.001 ) ) ;
}
void TestQgsProcessingAlgs::exportMeshVertices()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshvertices" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
QVariantList datasetGroup;
datasetGroup << 1 << 2;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( resultLayer );
QVERIFY( resultLayer->isValid() );
QVERIFY( resultLayer->geometryType() == QgsWkbTypes::PointGeometry );
QCOMPARE( resultLayer->featureCount(), 5l );
QgsAttributeList attributeList = resultLayer->attributeList();
QCOMPARE( resultLayer->fields().count(), 5 );
QCOMPARE( resultLayer->fields().at( 0 ).name(), QStringLiteral( "VertexScalarDataset" ) );
QCOMPARE( resultLayer->fields().at( 1 ).name(), QStringLiteral( "VertexVectorDataset_x" ) );
QCOMPARE( resultLayer->fields().at( 2 ).name(), QStringLiteral( "VertexVectorDataset_y" ) );
QCOMPARE( resultLayer->fields().at( 3 ).name(), QStringLiteral( "VertexVectorDataset_mag" ) );
QCOMPARE( resultLayer->fields().at( 4 ).name(), QStringLiteral( "VertexVectorDataset_dir" ) );
QgsFeatureIterator featIt = resultLayer->getFeatures();
QgsFeature feat;
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (1000 2000 20)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.828, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (2000 2000 30)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 3.605, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 56.3099, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (3000 2000 40)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 4.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 4.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 5.0, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 53.130, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (2000 3000 50)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 4.242, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PointZ (1000 3000 10)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), -1.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.236, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 116.565, 2 ) );
}
void TestQgsProcessingAlgs::exportMeshFaces()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshfaces" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
QVariantList datasetGroup;
datasetGroup << 3 << 4;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( resultLayer );
QVERIFY( resultLayer->isValid() );
QVERIFY( resultLayer->geometryType() == QgsWkbTypes::PolygonGeometry );
QCOMPARE( resultLayer->featureCount(), 2l );
QgsAttributeList attributeList = resultLayer->attributeList();
QCOMPARE( resultLayer->fields().count(), 5 );
QCOMPARE( resultLayer->fields().at( 0 ).name(), QStringLiteral( "FaceScalarDataset" ) );
QCOMPARE( resultLayer->fields().at( 1 ).name(), QStringLiteral( "FaceVectorDataset_x" ) );
QCOMPARE( resultLayer->fields().at( 2 ).name(), QStringLiteral( "FaceVectorDataset_y" ) );
QCOMPARE( resultLayer->fields().at( 3 ).name(), QStringLiteral( "FaceVectorDataset_mag" ) );
QCOMPARE( resultLayer->fields().at( 4 ).name(), QStringLiteral( "FaceVectorDataset_dir" ) );
QgsFeatureIterator featIt = resultLayer->getFeatures();
QgsFeature feat;
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PolygonZ ((1000 2000 20, 2000 2000 30, 2000 3000 50, 1000 3000 10, 1000 2000 20))" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.828, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PolygonZ ((2000 2000 30, 3000 2000 40, 2000 3000 50, 2000 2000 30))" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 4.242, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
}
void TestQgsProcessingAlgs::exportMeshEdges()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshedges" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer 1D" );
QVariantList datasetGroup;
datasetGroup << 1 << 2;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( resultLayer );
QVERIFY( resultLayer->isValid() );
QVERIFY( resultLayer->geometryType() == QgsWkbTypes::LineGeometry );
QCOMPARE( resultLayer->featureCount(), 3l );
QgsAttributeList attributeList = resultLayer->attributeList();
QCOMPARE( resultLayer->fields().count(), 5 );
QCOMPARE( resultLayer->fields().at( 0 ).name(), QStringLiteral( "EdgeScalarDataset" ) );
QCOMPARE( resultLayer->fields().at( 1 ).name(), QStringLiteral( "EdgeVectorDataset_x" ) );
QCOMPARE( resultLayer->fields().at( 2 ).name(), QStringLiteral( "EdgeVectorDataset_y" ) );
QCOMPARE( resultLayer->fields().at( 3 ).name(), QStringLiteral( "EdgeVectorDataset_mag" ) );
QCOMPARE( resultLayer->fields().at( 4 ).name(), QStringLiteral( "EdgeVectorDataset_dir" ) );
QgsFeatureIterator featIt = resultLayer->getFeatures();
QgsFeature feat;
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "LineStringZ (1000 2000 20, 2000 2000 30)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 2.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 2.828, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "LineStringZ (2000 2000 30, 3000 2000 40)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 3.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 3.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 4.242, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "LineStringZ (3000 2000 40, 2000 3000 50)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toDouble(), 4.0 );
QCOMPARE( feat.attributes().at( 1 ).toDouble(), 4.0 );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 4.0 );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 5.656, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 45.0, 2 ) );
}
void TestQgsProcessingAlgs::exportMeshOnGrid()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:exportmeshongrid" ) ) );
QVERIFY( alg != nullptr );
QString dataDir = QString( TEST_DATA_DIR ); //defined in CmakeLists.txt
QString meshUri( dataDir + "/mesh/trap_steady_05_3D.nc" );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), meshUri );
QVariantList datasetGroup;
for ( int i = 0; i < 12; ++i )
datasetGroup.append( i );
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "GRID_SPACING" ), 25.0 );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "VECTOR_OPTION" ), 2 );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QgsVectorLayer *resultLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT" ) ).toString() ) );
QVERIFY( resultLayer );
QVERIFY( resultLayer->isValid() );
QVERIFY( resultLayer->geometryType() == QgsWkbTypes::PointGeometry );
QCOMPARE( resultLayer->featureCount(), 205l );
QgsAttributeList attributeList = resultLayer->attributeList();
QCOMPARE( resultLayer->fields().count(), 21 );
QStringList fieldsName;
fieldsName << QStringLiteral( "Bed Elevation" ) << QStringLiteral( "temperature" ) << QStringLiteral( "temperature/Maximums" )
<< QStringLiteral( "temperature/Minimums" ) << QStringLiteral( "temperature/Time at Maximums" ) << QStringLiteral( "temperature/Time at Minimums" )
<< QStringLiteral( "velocity_x" ) << QStringLiteral( "velocity_y" ) << QStringLiteral( "velocity_mag" ) << QStringLiteral( "velocity_dir" )
<< QStringLiteral( "velocity/Maximums_x" ) << QStringLiteral( "velocity/Maximums_y" ) << QStringLiteral( "velocity/Maximums_mag" ) << QStringLiteral( "velocity/Maximums_dir" )
<< QStringLiteral( "velocity/Minimums_x" ) << QStringLiteral( "velocity/Minimums_y" ) << QStringLiteral( "velocity/Minimums_mag" ) << QStringLiteral( "velocity/Minimums_dir" )
<< QStringLiteral( "velocity/Time at Maximums" ) << QStringLiteral( "velocity/Time at Minimums" ) << QStringLiteral( "water depth" );
for ( int i = 0; i < fieldsName.count(); ++i )
QCOMPARE( fieldsName.at( i ), resultLayer->fields().at( i ).name() );
QgsFeatureIterator featIt = resultLayer->getFeatures();
QgsFeature feat;
for ( int i = 0; i < 8; ++i )
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "Point (25 50)" ), feat.geometry().asWkt() );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 0 ).toDouble(), -5.025, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 1 ).toDouble(), 1.424, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 2 ).toDouble(), 5.00, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 3 ).toDouble(), 1.32e-36, 2 ) );
QVERIFY( qgsDoubleNearSig( feat.attributes().at( 4 ).toDouble(), 0.02776, 2 ) );
}
void TestQgsProcessingAlgs::rasterizeMesh()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshrasterize" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
QVariantList datasetGroup;
datasetGroup << 1 << 2 << 3;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "PIXEL_SIZE" ), 200.0 );
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
std::unique_ptr<QgsRasterLayer> outputRaster = qgis::make_unique< QgsRasterLayer >( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
QVERIFY( outputRaster );
QVERIFY( outputRaster->isValid() );
QgsRasterDataProvider *outputProvider = outputRaster->dataProvider();
QCOMPARE( outputProvider->bandCount(), 3 );
QCOMPARE( outputProvider->xSize(), 10 );
QCOMPARE( outputProvider->ySize(), 5 );
std::unique_ptr<QgsRasterBlock> outputBlock_1( outputProvider->block( 1, outputRaster->extent(), 10, 5 ) );
std::unique_ptr<QgsRasterBlock> outputBlock_2( outputProvider->block( 2, outputRaster->extent(), 10, 5 ) );
std::unique_ptr<QgsRasterBlock> outputBlock_3( outputProvider->block( 3, outputRaster->extent(), 10, 5 ) );
// load expected result
QString dataDir = QString( TEST_DATA_DIR ); //defined in CmakeLists.txt
std::unique_ptr<QgsRasterLayer> expectedRaster = qgis::make_unique< QgsRasterLayer >( dataDir + "/mesh/rasterized_mesh.tif", "expected", "gdal" );
QVERIFY( expectedRaster );
QVERIFY( expectedRaster->isValid() );
QgsRasterDataProvider *expectedProvider = outputRaster->dataProvider();
std::unique_ptr<QgsRasterBlock> expectedBlock_1( expectedProvider->block( 1, expectedRaster->extent(), 10, 5 ) );
std::unique_ptr<QgsRasterBlock> expectedBlock_2( expectedProvider->block( 2, expectedRaster->extent(), 10, 5 ) );
std::unique_ptr<QgsRasterBlock> expectedBlock_3( expectedProvider->block( 3, expectedRaster->extent(), 10, 5 ) );
for ( int ix = 0; ix < 10; ++ix )
{
for ( int iy = 0; iy < 5; ++iy )
{
if ( !( std::isnan( outputBlock_1->value( iy, ix ) ) && std::isnan( expectedBlock_1->value( iy, ix ) ) ) )
QCOMPARE( outputBlock_1->value( iy, ix ), expectedBlock_1->value( iy, ix ) );
if ( !( std::isnan( outputBlock_2->value( iy, ix ) ) && std::isnan( expectedBlock_2->value( iy, ix ) ) ) )
QCOMPARE( outputBlock_2->value( iy, ix ), expectedBlock_2->value( iy, ix ) );
if ( !( std::isnan( outputBlock_2->value( iy, ix ) ) && std::isnan( expectedBlock_2->value( iy, ix ) ) ) )
QCOMPARE( outputBlock_3->value( iy, ix ), expectedBlock_3->value( iy, ix ) );
}
}
}
void TestQgsProcessingAlgs::exportMeshContours()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshcontours" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
QVariantList datasetGroup;
datasetGroup << 1 << 2 << 3;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "OUTPUT_LINES" ), QgsProcessing::TEMPORARY_OUTPUT );
parameters.insert( QStringLiteral( "OUTPUT_POLYGONS" ), QgsProcessing::TEMPORARY_OUTPUT );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
// min>max
parameters.insert( QStringLiteral( "INCREMENT" ), 0.5 );
parameters.insert( QStringLiteral( "MINIMUM" ), 5.0 );
parameters.insert( QStringLiteral( "MAXIMUM" ), 2.0 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
// min-max<increrment
parameters.insert( QStringLiteral( "INCREMENT" ), 10 );
parameters.insert( QStringLiteral( "MINIMUM" ), 5.0 );
parameters.insert( QStringLiteral( "MAXIMUM" ), 2.0 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
// min-max<increrment
parameters.insert( QStringLiteral( "INCREMENT" ), 2 );
parameters.insert( QStringLiteral( "MINIMUM" ), 0.25 );
parameters.insert( QStringLiteral( "MAXIMUM" ), 6.25 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QgsVectorLayer *resultLinesLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT_LINES" ) ).toString() ) );
QVERIFY( resultLinesLayer );
QVERIFY( resultLinesLayer->isValid() );
QgsAttributeList attributeList = resultLinesLayer->attributeList();
QCOMPARE( resultLinesLayer->fields().count(), 3 );
QCOMPARE( resultLinesLayer->fields().at( 0 ).name(), QStringLiteral( "group" ) );
QCOMPARE( resultLinesLayer->fields().at( 1 ).name(), QStringLiteral( "time" ) );
QCOMPARE( resultLinesLayer->fields().at( 2 ).name(), QStringLiteral( "value" ) );
QCOMPARE( resultLinesLayer->featureCount(), 4l );
QgsFeatureIterator featIt = resultLinesLayer->getFeatures();
QgsFeature feat;
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "LineStringZ (1250 3000 20, 1250 2250 27.5, 1250 2000 22.5)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexScalarDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "LineStringZ (1006.94319345290614365 3000 10.27772773811624596, 1000 2976.48044676110157525 10.23519553238898538)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexVectorDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "LineStringZ (2009.71706923721990279 2990.28293076277986984 49.90282930762779756, 2462.15304528350043256 2000 34.62153045283500319)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexVectorDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 4.25 );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "LineStringZ (1500 3000 30, 1500 2500 35, 1500 2000 25)" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "FaceScalarDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
QgsVectorLayer *resultpolygonLayer = qobject_cast< QgsVectorLayer * >( context->getMapLayer( results.value( QStringLiteral( "OUTPUT_POLYGONS" ) ).toString() ) );
QVERIFY( resultpolygonLayer );
QVERIFY( resultpolygonLayer->isValid() );
attributeList = resultpolygonLayer->attributeList();
QCOMPARE( resultpolygonLayer->fields().count(), 4 );
QCOMPARE( resultpolygonLayer->fields().at( 0 ).name(), QStringLiteral( "group" ) );
QCOMPARE( resultpolygonLayer->fields().at( 1 ).name(), QStringLiteral( "time" ) );
QCOMPARE( resultpolygonLayer->fields().at( 2 ).name(), QStringLiteral( "min_value" ) );
QCOMPARE( resultpolygonLayer->fields().at( 3 ).name(), QStringLiteral( "max_value" ) );
QCOMPARE( resultpolygonLayer->featureCount(), 6l );
featIt = resultpolygonLayer->getFeatures();
featIt.nextFeature( feat );
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() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexScalarDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 0.25 );
QCOMPARE( feat.attributes().at( 3 ).toDouble(), 2.25 );
featIt.nextFeature( feat );
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() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexScalarDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
QCOMPARE( feat.attributes().at( 3 ).toDouble(), 4.25 );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PolygonZ ((1006.94319345290614365 3000 10.27772773811624596, 1000 3000 10, 1000 2976.48044676110157525 10.23519553238898538, 1006.94319345290614365 3000 10.27772773811624596))" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "VertexVectorDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 0.25 );
QCOMPARE( feat.attributes().at( 3 ).toDouble(), 2.25 );
featIt.nextFeature( feat );
featIt.nextFeature( feat );
QCOMPARE( QStringLiteral( "PolygonZ ((1500 2500 35, 1500 2000 25, 1000 2000 20, 1000 3000 10, 1500 3000 30, 1500 2500 35))" ), feat.geometry().asWkt() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "FaceScalarDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 0.25 );
QCOMPARE( feat.attributes().at( 3 ).toDouble(), 2.25 );
featIt.nextFeature( feat );
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() );
QCOMPARE( feat.attributes().at( 0 ).toString(), QStringLiteral( "FaceScalarDataset" ) );
QCOMPARE( feat.attributes().at( 1 ).toString(), QStringLiteral( "1950-01-01 01:00:00" ) );
QCOMPARE( feat.attributes().at( 2 ).toDouble(), 2.25 );
QCOMPARE( feat.attributes().at( 3 ).toDouble(), 4.25 );
parameters.insert( QStringLiteral( "CONTOUR_LEVEL_LIST" ), QStringLiteral( "4,2,3" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
parameters.insert( QStringLiteral( "CONTOUR_LEVEL_LIST" ), QStringLiteral( "2,2,3" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
parameters.insert( QStringLiteral( "CONTOUR_LEVEL_LIST" ), QStringLiteral( "1,2,3" ) );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
}
void TestQgsProcessingAlgs::exportMeshCrossSection()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshexportcrosssection" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
QVariantList datasetGroup;
datasetGroup << 1 << 2 << 3;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetTime;
datasetTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndex;
datasetIndex << 1 << 1;
datasetTime[QStringLiteral( "value" )] = datasetIndex;
parameters.insert( QStringLiteral( "DATASET_TIME" ), datasetTime );
parameters.insert( QStringLiteral( "RESOLUTION" ), 100 );
QString outputPath = QDir::tempPath() + "/test_mesh_xs.csv";
parameters.insert( QStringLiteral( "OUTPUT" ), outputPath );
QgsVectorLayer *layerLine = new QgsVectorLayer( QStringLiteral( "LineString" ),
QStringLiteral( "lines_for_xs" ),
QStringLiteral( "memory" ) );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QStringList wktLines;
wktLines << QStringLiteral( "LineString (1500 2200, 2500 2200)" );
wktLines << QStringLiteral( "LineString (1500 1500, 1500 3200)" );
QgsFeatureList flist;
for ( const QString &wkt : wktLines )
{
QgsFeature feat;
feat.setGeometry( QgsGeometry::fromWkt( wkt ) );
flist << feat;
}
layerLine->dataProvider()->addFeatures( flist );
QgsProject::instance()->addMapLayer( layerLine ); QgsProject::instance()->addMapLayer( layerLine );
parameters.insert( QStringLiteral( "INPUT_LINES" ), layerLine->name() );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QFile outputFile( outputPath );
QVERIFY( outputFile.open( QIODevice::ReadOnly ) );
QTextStream textStream( &outputFile );
QString header = textStream.readLine();
QCOMPARE( header, QStringLiteral( "fid,x,y,offset,VertexScalarDataset,VertexVectorDataset,FaceScalarDataset" ) );
QStringList expectedLines;
expectedLines << QStringLiteral( "1,1500.00,2200.00,0.00,2.50,3.33,2.00" )
<< QStringLiteral( "1,1600.00,2200.00,100.00,2.60,3.41,2.00" )
<< QStringLiteral( "1,1700.00,2200.00,200.00,2.70,3.48,2.00" )
<< QStringLiteral( "1,1800.00,2200.00,300.00,2.80,3.56,2.00" )
<< QStringLiteral( "1,1900.00,2200.00,400.00,2.90,3.64,2.00" )
<< QStringLiteral( "1,2000.00,2200.00,500.00,3.00,3.72,2.00" )
<< QStringLiteral( "1,2100.00,2200.00,600.00,3.10,3.86,3.00" )
<< QStringLiteral( "1,2200.00,2200.00,700.00,3.20,4.00,3.00" )
<< QStringLiteral( "1,2300.00,2200.00,800.00,3.30,4.14,3.00" )
<< QStringLiteral( "1,2400.00,2200.00,900.00,3.40,4.28,3.00" )
<< QStringLiteral( "1,2500.00,2200.00,1000.00,3.50,4.42,3.00" )
<< QStringLiteral( "2,1500.00,1500.00,0.00, , , " )
<< QStringLiteral( "2,1500.00,1600.00,100.00, , , " )
<< QStringLiteral( "2,1500.00,1700.00,200.00, , , " )
<< QStringLiteral( "2,1500.00,1800.00,300.00, , , " )
<< QStringLiteral( "2,1500.00,1900.00,400.00, , , " )
<< QStringLiteral( "2,1500.00,2000.00,500.00,2.50,3.20,2.00" )
<< QStringLiteral( "2,1500.00,2100.00,600.00,2.50,3.26,2.00" )
<< QStringLiteral( "2,1500.00,2200.00,700.00,2.50,3.33,2.00" )
<< QStringLiteral( "2,1500.00,2300.00,800.00,2.50,3.40,2.00" )
<< QStringLiteral( "2,1500.00,2400.00,900.00,2.50,3.47,2.00" )
<< QStringLiteral( "2,1500.00,2500.00,1000.00,2.50,3.54,2.00" )
<< QStringLiteral( "2,1500.00,2600.00,1100.00,2.50,3.33,2.00" )
<< QStringLiteral( "2,1500.00,2700.00,1200.00,2.50,3.14,2.00" )
<< QStringLiteral( "2,1500.00,2800.00,1300.00,2.50,2.97,2.00" )
<< QStringLiteral( "2,1500.00,2900.00,1400.00,2.50,2.82,2.00" )
<< QStringLiteral( "2,1500.00,3000.00,1500.00,2.50,2.69,2.00" )
<< QStringLiteral( "2,1500.00,3100.00,1600.00, , , " )
<< QStringLiteral( "2,1500.00,3200.00,1700.00, , , " );
QString line = textStream.readLine();
int i = 0;
QVERIFY( !line.isEmpty() );
while ( !line.isEmpty() )
{
QCOMPARE( line, expectedLines.at( i ) );
++i;
line = textStream.readLine();
}
QVERIFY( i == expectedLines.count() );
}
void TestQgsProcessingAlgs::exportMeshTimeSeries()
{
std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshexporttimeseries" ) ) );
QVERIFY( alg != nullptr );
QVariantMap parameters;
parameters.insert( QStringLiteral( "INPUT" ), "mesh layer" );
QVariantList datasetGroup;
datasetGroup << 1 << 2 << 3;
parameters.insert( QStringLiteral( "DATASET_GROUPS" ), datasetGroup );
QVariantMap datasetStartTime;
datasetStartTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndexStart;
datasetIndexStart << 1 << 0;
datasetStartTime[QStringLiteral( "value" )] = datasetIndexStart;
parameters.insert( QStringLiteral( "STARTING_TIME" ), datasetStartTime );
QVariantMap datasetEndTime;
datasetEndTime[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
QVariantList datasetIndexEnd;
datasetIndexEnd << 1 << 1;
datasetEndTime[QStringLiteral( "value" )] = datasetIndexEnd;
parameters.insert( QStringLiteral( "FINISHING_TIME" ), datasetEndTime );
QString outputPath = QDir::tempPath() + "/test_mesh_ts.csv";
parameters.insert( QStringLiteral( "OUTPUT" ), outputPath );
QgsVectorLayer *layerPoints = new QgsVectorLayer( QStringLiteral( "Point" ),
QStringLiteral( "points_for_ts" ),
QStringLiteral( "memory" ) );
std::unique_ptr< QgsProcessingContext > context = qgis::make_unique< QgsProcessingContext >();
context->setProject( QgsProject::instance() );
QgsProcessingFeedback feedback;
QVariantMap results;
bool ok = false;
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( !ok );
QStringList wktPoints;
wktPoints << QStringLiteral( "Point (1500 2200)" );
wktPoints << QStringLiteral( "Point (1500 1500)" );
wktPoints << QStringLiteral( "Point (2500 2100)" );
QgsFeatureList flist;
for ( const QString &wkt : wktPoints )
{
QgsFeature feat;
feat.setGeometry( QgsGeometry::fromWkt( wkt ) );
flist << feat;
}
layerPoints->dataProvider()->addFeatures( flist );
QgsProject::instance()->addMapLayer( layerPoints ); QgsProject::instance()->addMapLayer( layerPoints );
parameters.insert( QStringLiteral( "INPUT_POINTS" ), layerPoints->name() );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QFile outputFile( outputPath );
QVERIFY( outputFile.open( QIODevice::ReadOnly ) );
QTextStream textStream( &outputFile );
QString header = textStream.readLine();
QCOMPARE( header, QStringLiteral( "fid,x,y,time,VertexScalarDataset,VertexVectorDataset,FaceScalarDataset" ) );
QStringList expectedLines;
expectedLines << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:00:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 01:00:00,2.50,3.33,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:00:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 01:00:00,3.50,4.36,3.00" );
QString line = textStream.readLine();
int i = 0;
QVERIFY( !line.isEmpty() );
while ( !line.isEmpty() )
{
QCOMPARE( line, expectedLines.at( i ) );
++i;
line = textStream.readLine();
}
QVERIFY( i == expectedLines.count() );
outputFile.close();
parameters.insert( QStringLiteral( "TIME_STEP" ), 0.1 );
results = alg->run( parameters, *context, &feedback, &ok );
QVERIFY( ok );
QVERIFY( outputFile.open( QIODevice::ReadOnly ) );
header = textStream.readLine();
QCOMPARE( header, QStringLiteral( "fid,x,y,time,VertexScalarDataset,VertexVectorDataset,FaceScalarDataset" ) );
expectedLines.clear();
expectedLines << QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:00:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:06:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:12:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:18:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:24:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:30:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:36:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:42:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:48:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 00:54:00,1.50,1.92,1.00" )
<< QStringLiteral( "1,1500.00,2200.00,1950-01-01 01:00:00,2.50,3.33,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:00:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:06:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:12:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:18:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:24:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:30:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:36:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:42:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:48:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 00:54:00,2.50,2.97,2.00" )
<< QStringLiteral( "3,2500.00,2100.00,1950-01-01 01:00:00,3.50,4.36,3.00" );
line = textStream.readLine();
i = 0;
QVERIFY( !line.isEmpty() );
while ( !line.isEmpty() )
{
QCOMPARE( line, expectedLines.at( i ) );
++i;
line = textStream.readLine();
}
QVERIFY( i == expectedLines.count() );
outputFile.close();
}
bool TestQgsProcessingAlgs::imageCheck( const QString &testName, const QString &renderedImage )
{
QgsRenderChecker checker;
checker.setControlPathPrefix( QStringLiteral( "processing_algorithm" ) );
checker.setControlName( "expected_" + testName );
checker.setRenderedImage( renderedImage );
checker.setSizeTolerance( 3, 3 );
bool equal = checker.compareImages( testName, 500 );
return equal;
}
QGSTEST_MAIN( TestQgsProcessingAlgs )
#include "testqgsprocessingalgs.moc"