Skip to content
Permalink
Browse files

Unit tests for reclassification utils

  • Loading branch information
nyalldawson committed Jun 8, 2018
1 parent c5966b4 commit 87290734b2e42edd438700aca2acd525c5baf300
@@ -39,8 +39,8 @@ void QgsReclassifyUtils::reclassify( const QVector<QgsReclassifyUtils::RasterCla
iter.setMaximumTileHeight( maxHeight );
iter.startRasterRead( band, sourceWidthPixels, sourceHeightPixels, extent );

int nbBlocksWidth = std::ceil( 1.0 * sourceWidthPixels / maxWidth );
int nbBlocksHeight = std::ceil( 1.0 * sourceHeightPixels / maxHeight );
int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * sourceWidthPixels / maxWidth ) );
int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * sourceHeightPixels / maxHeight ) );
int nbBlocks = nbBlocksWidth * nbBlocksHeight;

int iterLeft = 0;
@@ -73,6 +73,7 @@ SET(TESTS
testqgsprocessingalgs.cpp
testqgszonalstatistics.cpp
testqgsrastercalculator.cpp
testqgsreclassifyutils.cpp
testqgsalignraster.cpp
testqgsnetworkanalysis.cpp
testqgsninecellfilters.cpp
@@ -25,7 +25,6 @@
#include "qgsnativealgorithms.h"
#include "qgsalgorithmimportphotos.h"
#include "qgsalgorithmtransform.h"
#include "qgsreclassifyutils.h"

class TestQgsProcessingAlgs: public QObject
{
@@ -42,7 +41,6 @@ class TestQgsProcessingAlgs: public QObject
void parseGeoTags();
void featureFilterAlg();
void transformAlg();
void reclassifyValue();

private:

@@ -437,45 +435,6 @@ void TestQgsProcessingAlgs::transformAlg()
QVERIFY( ok );
}

void TestQgsProcessingAlgs::reclassifyValue()
{
// no classes
bool ok = false;
QVector< QgsReclassifyUtils::RasterClass > classes;
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5.9, ok ), 5.9 );
QVERIFY( !ok );

// one class
classes << QgsReclassifyUtils::RasterClass( 5, 11, QgsRasterRange::IncludeMin, -99.5 );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5.9, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 15.9, ok ), 15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, -15.9, ok ), -15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 11, ok ), 11.0 );
QVERIFY( !ok );

// second class
classes << QgsReclassifyUtils::RasterClass( 11, 15, QgsRasterRange::IncludeMin, -59.5 );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5.9, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 11.9, ok ), -59.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 15.9, ok ), 15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, -15.9, ok ), -15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 11, ok ), -59.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 15, ok ), 15.0 );
QVERIFY( !ok );
}


QGSTEST_MAIN( TestQgsProcessingAlgs )
#include "testqgsprocessingalgs.moc"
@@ -0,0 +1,238 @@
/***************************************************************************
testqgsreclassifyutils.cpp
---------------------
begin : June 2018
copyright : (C) 2018 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 "qgstest.h"
#include "qgsreclassifyutils.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasterfilewriter.h"

class TestQgsReclassifyUtils: public QObject
{
Q_OBJECT

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 reclassifyValue();
void testReclassify_data();
void testReclassify();

private:

QVector<double> reclassifyBlock( const QVector<double> &input, int nRows, int nCols,
const QVector< QgsReclassifyUtils::RasterClass > &classes,
double destNoDataValue, bool useNoDataForMissing );

};

void TestQgsReclassifyUtils::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();
}

void TestQgsReclassifyUtils::cleanupTestCase()
{
QgsApplication::exitQgis();
}

void TestQgsReclassifyUtils::reclassifyValue()
{
// no classes
bool ok = false;
QVector< QgsReclassifyUtils::RasterClass > classes;
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5.9, ok ), 5.9 );
QVERIFY( !ok );

// one class
classes << QgsReclassifyUtils::RasterClass( 5, 11, QgsRasterRange::IncludeMin, -99.5 );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5.9, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 15.9, ok ), 15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, -15.9, ok ), -15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 11, ok ), 11.0 );
QVERIFY( !ok );

// second class
classes << QgsReclassifyUtils::RasterClass( 11, 15, QgsRasterRange::IncludeMin, -59.5 );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5.9, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 11.9, ok ), -59.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 15.9, ok ), 15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, -15.9, ok ), -15.9 );
QVERIFY( !ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 5, ok ), -99.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 11, ok ), -59.5 );
QVERIFY( ok );
QCOMPARE( QgsReclassifyUtils::reclassifyValue( classes, 15, ok ), 15.0 );
QVERIFY( !ok );
}

Q_DECLARE_METATYPE( QgsReclassifyUtils::RasterClass );

void TestQgsReclassifyUtils::testReclassify_data()
{
QTest::addColumn<QVector< double >>( "input" );
QTest::addColumn<int>( "rows" );
QTest::addColumn<int>( "cols" );
QTest::addColumn<QVector< QgsReclassifyUtils::RasterClass >>( "classes" );
QTest::addColumn<double>( "destNoDataValue" );
QTest::addColumn<bool>( "useNoDataForMissing" );
QTest::addColumn<QVector< double >>( "expected" );

QTest::newRow( "no change" ) << QVector< double > { 1, 2, 3, 4, 5, 6 }
<< 3 << 2
<< QVector< QgsReclassifyUtils::RasterClass >()
<< -9999.0 << false
<< QVector< double > { 1, 2, 3, 4, 5, 6 };

QTest::newRow( "one class" ) << QVector< double > { 1, 2, 3, 4, 5, 6 }
<< 3 << 2
<< ( QVector< QgsReclassifyUtils::RasterClass >()
<< QgsReclassifyUtils::RasterClass( 3, 5, QgsRasterRange::IncludeMax, 8 ) )
<< -9999.0 << false
<< QVector< double > { 1, 2, 3, 8, 8, 6 };

QTest::newRow( "two class" ) << QVector< double > { 1, 2, 3, 4, 5, 6 }
<< 3 << 2
<< ( QVector< QgsReclassifyUtils::RasterClass >()
<< QgsReclassifyUtils::RasterClass( 3, 5, QgsRasterRange::IncludeMax, 8 )
<< QgsReclassifyUtils::RasterClass( 1, 3, QgsRasterRange::IncludeMin, -7 ) )
<< -9999.0 << false
<< QVector< double > { -7, -7, 3, 8, 8, 6 };

QTest::newRow( "with source no data" ) << QVector< double > { 1, 2, -9999, 4, 5, 6 }
<< 3 << 2
<< ( QVector< QgsReclassifyUtils::RasterClass >()
<< QgsReclassifyUtils::RasterClass( 3, 5, QgsRasterRange::IncludeMinAndMax, 8 ) )
<< -9999.0 << false
<< QVector< double > { 1, 2, -9999, 8, 8, 6 };

QTest::newRow( "with dest no data" ) << QVector< double > { 1, 2, -9999, 4, 5, 6 }
<< 3 << 2
<< ( QVector< QgsReclassifyUtils::RasterClass >()
<< QgsReclassifyUtils::RasterClass( 3, 5, QgsRasterRange::IncludeMinAndMax, 8 ) )
<< -99.0 << false
<< QVector< double > { 1, 2, -99, 8, 8, 6 };

QTest::newRow( "use no data for missing" ) << QVector< double > { 1, 2, -9999, 4, 5, 6 }
<< 3 << 2
<< ( QVector< QgsReclassifyUtils::RasterClass >()
<< QgsReclassifyUtils::RasterClass( 3, 5, QgsRasterRange::IncludeMinAndMax, 8 ) )
<< -9999.0 << true
<< QVector< double > { -9999, -9999, -9999, 8, 8, -9999 };
}

void TestQgsReclassifyUtils::testReclassify()
{
QFETCH( QVector< double >, input );
QFETCH( int, rows );
QFETCH( int, cols );
QFETCH( QVector< QgsReclassifyUtils::RasterClass >, classes );
QFETCH( double, destNoDataValue );
QFETCH( bool, useNoDataForMissing );
QFETCH( QVector< double >, expected );

QVector< double > res = reclassifyBlock( input, 2, 3, classes, destNoDataValue, useNoDataForMissing );
for ( int row = 0; row < rows; row++ )
{
for ( int col = 0; col < cols; col++ )
{
QCOMPARE( res[row * cols + col], expected[row * cols + col] );
}
}
}

QVector< double > TestQgsReclassifyUtils::reclassifyBlock( const QVector< double > &input, int nRows, int nCols,
const QVector< QgsReclassifyUtils::RasterClass > &classes,
double destNoDataValue, bool useNoDataForMissing )
{
QgsRectangle extent = QgsRectangle( 0, 0, nRows, nCols );
QgsCoordinateReferenceSystem crs( 3857 );
double tform[] =
{
extent.xMinimum(), extent.width() / nCols, 0.0,
extent.yMaximum(), 0.0, -extent.height() / nRows
};

// 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, extent, crs ) );
dp->setNoDataValue( 1, -9999 );
std::unique_ptr< QgsRasterBlock > block( dp->block( 1, extent, nCols, nRows ) );
dp->setEditable( true );
int i = 0;
for ( int row = 0; row < nRows; row++ )
{
for ( int col = 0; col < nCols; col++ )
{
block->setValue( row, col, input[i++] );
}
}
Q_ASSERT( dp->writeBlock( block.get(), 1 ) );
dp->setEditable( false );

// make destination raster
QTemporaryFile tmpFile2;
tmpFile2.open();
tmpFile2.close();

// create a GeoTIFF - this will create data provider in editable mode
filename = tmpFile2.fileName();
std::unique_ptr< QgsRasterDataProvider > dp2( QgsRasterDataProvider::create( QStringLiteral( "gdal" ), filename, QStringLiteral( "GTiff" ), 1, Qgis::Float32, 10, 10, tform, crs ) );

// reclassify
dp2->setEditable( true );
QgsReclassifyUtils::reclassify( classes, dp.get(), 1, extent, nCols, nRows, dp2.get(), destNoDataValue, useNoDataForMissing );
dp2->setEditable( false );

// read back in values
block.reset( dp2->block( 1, extent, nCols, nRows ) );
QVector< double > res( nCols * nRows );
i = 0;
for ( int row = 0; row < nRows; row++ )
{
for ( int col = 0; col < nCols; col++ )
{
res[i++] = block->value( row, col );
}
}

return res;
}


QGSTEST_MAIN( TestQgsReclassifyUtils )
#include "testqgsreclassifyutils.moc"

0 comments on commit 8729073

Please sign in to comment.
You can’t perform that action at this time.